diff --git a/packages/core/src/services/layer/ILayerService.ts b/packages/core/src/services/layer/ILayerService.ts index 3c6cec27c1..7f60a8873e 100644 --- a/packages/core/src/services/layer/ILayerService.ts +++ b/packages/core/src/services/layer/ILayerService.ts @@ -1,4 +1,4 @@ -import { ISourceCFG } from '@l7/source'; +import { ISourceCFG } from '@l7/core'; import { AsyncParallelHook, SyncHook } from 'tapable'; import { IModel } from '../renderer/IModel'; import { IMultiPassRenderer } from '../renderer/IMultiPassRenderer'; diff --git a/packages/core/src/services/renderer/ITexture2D.ts b/packages/core/src/services/renderer/ITexture2D.ts index 9fd28ca068..5a77c70d21 100644 --- a/packages/core/src/services/renderer/ITexture2D.ts +++ b/packages/core/src/services/renderer/ITexture2D.ts @@ -33,6 +33,7 @@ export interface ITexture2DInitializationOptions { */ data?: | undefined + | HTMLImageElement | number[] | number[][] | Uint8Array diff --git a/packages/core/src/services/source/ISourceService.ts b/packages/core/src/services/source/ISourceService.ts index 950fb469b6..4c86911b4f 100644 --- a/packages/core/src/services/source/ISourceService.ts +++ b/packages/core/src/services/source/ISourceService.ts @@ -12,7 +12,7 @@ type CallBack = (...args: any[]) => any; export interface ITransform { type: string; [key: string]: any; - callback: CallBack; + callback?: CallBack; } export interface ISourceCFG { diff --git a/packages/layers/package.json b/packages/layers/package.json index f986aa58e9..431b0345cc 100644 --- a/packages/layers/package.json +++ b/packages/layers/package.json @@ -29,7 +29,9 @@ "earcut": "^2.2.1", "eventemitter3": "^3.1.0", "gl-matrix": "^3.1.0", + "gl-vec2": "^1.3.0", "lodash": "^4.17.15", + "polyline-miter-util": "^1.0.1", "tapable": "^2.0.0-beta.8" }, "devDependencies": { diff --git a/packages/layers/src/core/BaseBuffer.ts b/packages/layers/src/core/BaseBuffer.ts index bf91cacd84..c4a977af49 100644 --- a/packages/layers/src/core/BaseBuffer.ts +++ b/packages/layers/src/core/BaseBuffer.ts @@ -1,9 +1,10 @@ -import { ILayerStyleOptions } from '@l7/core'; +import { IICONMap, ILayerStyleOptions } from '@l7/core'; import { lngLatToMeters } from '@l7/utils'; import { vec3 } from 'gl-matrix'; +import { IExtrudeGeomety } from '../point/shape/extrude'; interface IBufferCfg { data: unknown[]; - imagePos?: unknown; + iconMap?: IICONMap; style?: ILayerStyleOptions; } export type Position = number[]; @@ -26,6 +27,7 @@ export interface IEncodeFeature { coordinates: unknown; bufferInfo: unknown; } + export default class Buffer { public attributes: { [key: string]: Float32Array; @@ -33,14 +35,14 @@ export default class Buffer { public verticesCount: number = 0; public indexArray: Uint32Array = new Uint32Array(0); public indexCount: number = 0; - + public instanceGeometry: IExtrudeGeomety; protected data: unknown[]; - protected imagePos: unknown; + protected iconMap: IICONMap; protected style: any; - constructor({ data, imagePos, style }: IBufferCfg) { + constructor({ data, iconMap, style }: IBufferCfg) { this.data = data; - this.imagePos = imagePos; + this.iconMap = iconMap as IICONMap; this.style = style; this.init(); } @@ -126,7 +128,7 @@ export default class Buffer { const { color, id, pattern, size } = feature; const bufferInfo = feature.bufferInfo as IBufferInfo; const { verticesOffset } = bufferInfo; - const imagePos = this.imagePos; + const imagePos = this.iconMap; const start1 = verticesOffset; for (let i = 0; i < num; i++) { if (color) { diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 0150220569..8b756fdf79 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -10,12 +10,13 @@ import { IMultiPassRenderer, IRendererService, ISource, + ISourceCFG, lazyInject, StyleAttributeField, StyleAttributeOption, TYPES, } from '@l7/core'; -import Source, { ISourceCFG } from '@l7/source'; +import Source from '@l7/source'; import { isFunction } from 'lodash'; import { SyncHook } from 'tapable'; import DataEncodePlugin from '../plugins/DataEncodePlugin'; diff --git a/packages/layers/src/core/StyleAttribute.ts b/packages/layers/src/core/StyleAttribute.ts index 5f080b0deb..459e0bc358 100644 --- a/packages/layers/src/core/StyleAttribute.ts +++ b/packages/layers/src/core/StyleAttribute.ts @@ -46,7 +46,9 @@ export default class StyleAttribute implements ILayerStyleAttribute { if (scales.some((scale) => scale.type === StyleScaleType.VARIABLE)) { this.type = StyleScaleType.VARIABLE; scales.forEach((scale) => { - scale.scale.range(this.values); + if (this.values.length > 0) { + scale.scale.range(this.values); + } }); } else { // 设置attribute 常量值 diff --git a/packages/layers/src/heatmap/buffers/GridBuffer.ts b/packages/layers/src/heatmap/buffers/GridBuffer.ts new file mode 100644 index 0000000000..b75f64fc16 --- /dev/null +++ b/packages/layers/src/heatmap/buffers/GridBuffer.ts @@ -0,0 +1,46 @@ +import BufferBase, { IEncodeFeature, Position } from '../../core/BaseBuffer'; +import extrudePolygon, { + fillPolygon, + IExtrudeGeomety, +} from '../../point/shape/extrude'; +import { + geometryShape, + ShapeType2D, + ShapeType3D, +} from '../../point/shape/Path'; +export default class GridHeatMapBuffer extends BufferBase { + private verticesOffset: number = 0; + protected buildFeatures() { + this.verticesOffset = 0; + const layerData = this.data as IEncodeFeature[]; + layerData.forEach((feature: IEncodeFeature) => { + this.calculateFill(feature); + }); + } + protected calculateFeatures() { + const layerData = this.data as IEncodeFeature[]; + const shape = layerData[0].shape as ShapeType3D | ShapeType2D; + this.verticesCount = layerData.length; + this.indexCount = 0; + this.instanceGeometry = this.getGeometry(shape as + | ShapeType2D + | ShapeType3D); + } + protected calculateFill(feature: IEncodeFeature) { + feature.bufferInfo = { verticesOffset: this.verticesOffset }; + const coordinates = feature.coordinates as Position; + this.encodeArray(feature, 1); + this.attributes.positions.set([...coordinates, 1], this.verticesOffset * 3); + this.verticesOffset++; + } + private getGeometry(shape: ShapeType2D | ShapeType3D): IExtrudeGeomety { + const path = geometryShape[shape] + ? geometryShape[shape]() + : geometryShape.circle(); + // const geometry = ShapeType2D[str as ShapeType2D] + // ? fillPolygon([path]) + // : extrudePolygon([path]); + const geometry = fillPolygon([path]); + return geometry; + } +} diff --git a/packages/layers/src/heatmap/index.ts b/packages/layers/src/heatmap/index.ts new file mode 100644 index 0000000000..aa30d0e77c --- /dev/null +++ b/packages/layers/src/heatmap/index.ts @@ -0,0 +1,114 @@ +import { + gl, + IRendererService, + IShaderModuleService, + lazyInject, + TYPES, +} from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import GridHeatMapBuffer from './buffers/GridBuffer'; +import hexagon_frag from './shaders/hexagon_frag.glsl'; +import hexagon_vert from './shaders/hexagon_vert.glsl'; + +export default class HeatMapLayer extends BaseLayer { + public name: string = 'HeatMapLayer'; + + @lazyInject(TYPES.IShaderModuleService) + private readonly shaderModule: IShaderModuleService; + + @lazyInject(TYPES.IRendererService) + private readonly renderer: IRendererService; + + protected renderModels() { + this.models.forEach((model) => + model.draw({ + uniforms: { + u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + }, + }), + ); + return this; + } + + protected buildModels(): void { + this.shaderModule.registerModule('grid', { + vs: hexagon_vert, + fs: hexagon_frag, + }); + this.models = []; + const { vs, fs, uniforms } = this.shaderModule.getModule('grid'); + const buffer = new GridHeatMapBuffer({ + data: this.getEncodedData(), + }); + console.log(this.getSource()); + console.log(buffer); + const { + createAttribute, + createBuffer, + createElements, + createModel, + } = this.renderer; + + this.models.push( + createModel({ + attributes: { + a_miter: createAttribute({ + buffer: createBuffer({ + data: buffer.instanceGeometry.positions, + type: gl.FLOAT, + }), + size: 3, + divisor: 0, + }), + // a_normal: createAttribute({ + // buffer: createBuffer({ + // data: buffer.attributes.normals, + // type: gl.FLOAT, + // }), + // size: 3, + // }), + a_color: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.colors, + type: gl.FLOAT, + }), + size: 4, + divisor: 1, + }), + // a_size: createAttribute({ + // buffer: createBuffer({ + // data: buffer.attributes.sizes, + // type: gl.FLOAT, + // }), + // size: 1, + // divisor: 1, + // }), + a_Position: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.positions, + type: gl.FLOAT, + }), + size: 3, + divisor: 1, + }), + }, + uniforms: { + ...uniforms, + u_opacity: (this.styleOption.opacity as number) || 1.0, + u_radius: [ + this.getSource().data.xOffset, + this.getSource().data.yOffset, + ], + }, + fs, + vs, + count: buffer.instanceGeometry.index.length, + instances: buffer.verticesCount, + elements: createElements({ + data: buffer.instanceGeometry.index, + type: gl.UNSIGNED_INT, + }), + }), + ); + } +} diff --git a/packages/layers/src/heatmap/shaders/hexagon_frag.glsl b/packages/layers/src/heatmap/shaders/hexagon_frag.glsl new file mode 100644 index 0000000000..c9944e0a01 --- /dev/null +++ b/packages/layers/src/heatmap/shaders/hexagon_frag.glsl @@ -0,0 +1,7 @@ +precision highp float; +varying vec4 v_color; +uniform float u_opacity: 0.1; +void main() { + gl_FragColor = v_color; + gl_FragColor.a *= u_opacity; +} \ No newline at end of file diff --git a/packages/layers/src/heatmap/shaders/hexagon_vert.glsl b/packages/layers/src/heatmap/shaders/hexagon_vert.glsl new file mode 100644 index 0000000000..36bd09e2c0 --- /dev/null +++ b/packages/layers/src/heatmap/shaders/hexagon_vert.glsl @@ -0,0 +1,18 @@ +precision highp float; +attribute vec3 a_Position; +attribute vec3 a_miter; +attribute float a_size; +attribute vec4 a_color; +uniform vec2 u_radius; +uniform float u_coverage: 1.; +uniform float u_angle: 0; +uniform mat4 u_ModelMatrix; +varying vec4 v_color; +#pragma include "projection" +void main() { + v_color = a_color; + mat2 rotationMatrix = mat2(cos(u_angle), sin(u_angle), -sin(u_angle), cos(u_angle)); + vec2 offset =(vec2(a_miter.xy * u_radius * u_coverage * rotationMatrix)); + vec4 project_pos = project_position(vec4(a_Position.xy + offset, 0, 1.0)); + gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0)); +} \ No newline at end of file diff --git a/packages/layers/src/index.ts b/packages/layers/src/index.ts index 93e61285ff..91a9c56138 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -1,5 +1,16 @@ import BaseLayer from './core/BaseLayer'; +import HeatMapLayer from './heatmap'; +import Line from './line'; import PointLayer from './point'; import Point from './point/point'; import PolygonLayer from './polygon'; -export { BaseLayer, PointLayer, PolygonLayer, Point }; +import ImageLayer from './raster'; +export { + BaseLayer, + PointLayer, + PolygonLayer, + Point, + Line, + ImageLayer, + HeatMapLayer, +}; diff --git a/packages/layers/src/line/buffers/line.ts b/packages/layers/src/line/buffers/line.ts index d2f16491ba..522b124220 100644 --- a/packages/layers/src/line/buffers/line.ts +++ b/packages/layers/src/line/buffers/line.ts @@ -1,4 +1,6 @@ +import { lngLatToMeters, Point } from '@l7/utils'; import BufferBase, { IEncodeFeature, Position } from '../../core/BaseBuffer'; +import getNormals from '../../utils/polylineNormal'; interface IBufferInfo { normals: number[]; arrayIndex: number[]; @@ -8,7 +10,7 @@ interface IBufferInfo { verticesOffset: number; indexOffset: number; } -export default class FillBuffer extends BufferBase { +export default class LineBuffer extends BufferBase { private hasPattern: boolean; protected buildFeatures() { const layerData = this.data as IEncodeFeature[]; @@ -32,13 +34,19 @@ export default class FillBuffer extends BufferBase { protected calculateFeatures() { const layerData = this.data as IEncodeFeature[]; // 计算长 - layerData.forEach((feature: IEncodeFeature) => { - let { coordinates } = feature; + layerData.forEach((feature: IEncodeFeature, index: number) => { + let coordinates = feature.coordinates as Position[] | Position[][]; if (Array.isArray(coordinates[0][0])) { - coordinates = coordinates[0]; + coordinates = coordinates[0] as Position[]; } + // @ts-ignore + const projectCoord: number[][] = coordinates.map((item: Position[]) => { + // @ts-ignore + const p: Point = [...item]; + return lngLatToMeters(p); + }); const { normals, attrIndex, attrPos, attrDistance, miters } = getNormals( - coordinates, + coordinates as number[][], false, this.verticesCount, ); diff --git a/packages/layers/src/line/shaders/line_frag.glsl b/packages/layers/src/line/shaders/line_frag.glsl index d2b43d6f18..8379af0d5e 100644 --- a/packages/layers/src/line/shaders/line_frag.glsl +++ b/packages/layers/src/line/shaders/line_frag.glsl @@ -1,9 +1,9 @@ -uniform float u_blur : 0.9; +uniform float u_blur : 0.99; varying vec4 v_color; varying vec3 v_normal; void main() { gl_FragColor = v_color; // anti-alias - // float blur = 1. - smoothstep(u_blur, 1., length(v_normal)); - // gl_FragColor.a *= blur; + float blur = smoothstep(u_blur, 1., length(v_normal.xy)); + gl_FragColor.a *= blur; } \ No newline at end of file diff --git a/packages/layers/src/plugins/DataEncodePlugin.ts b/packages/layers/src/plugins/DataEncodePlugin.ts index b326d63706..dda035e0a3 100644 --- a/packages/layers/src/plugins/DataEncodePlugin.ts +++ b/packages/layers/src/plugins/DataEncodePlugin.ts @@ -60,7 +60,11 @@ export default class DataEncodePlugin implements ILayerPlugin { let scale = this.scaleCache[field as string]; if (!scale) { scale = this.scaleController.createScale(field as string, data); - if (scale.type === StyleScaleType.VARIABLE) { + if ( + scale.type === StyleScaleType.VARIABLE && + attribute.values && + attribute.values.length > 0 + ) { scale.scale.range(attribute.values); } this.scaleCache[field as string] = scale; diff --git a/packages/layers/src/plugins/DataSourcePlugin.ts b/packages/layers/src/plugins/DataSourcePlugin.ts index 950d7814a8..ed4aa268be 100644 --- a/packages/layers/src/plugins/DataSourcePlugin.ts +++ b/packages/layers/src/plugins/DataSourcePlugin.ts @@ -9,7 +9,8 @@ import { StyleScaleType, TYPES, } from '@l7/core'; -import Source, { ISourceCFG } from '@l7/source'; +import { ISourceCFG } from '@l7/core'; +import Source from '@l7/source'; export default class DataSourcePlugin implements ILayerPlugin { public apply(layer: ILayer) { layer.hooks.init.tap('DataSourcePlugin', () => { diff --git a/packages/layers/src/point/buffers/ExtrudeBuffer.ts b/packages/layers/src/point/buffers/ExtrudeBuffer.ts index 6313ee9bf4..a3b168f3a3 100644 --- a/packages/layers/src/point/buffers/ExtrudeBuffer.ts +++ b/packages/layers/src/point/buffers/ExtrudeBuffer.ts @@ -4,7 +4,7 @@ import BaseBuffer, { Position, } from '../../core/BaseBuffer'; import extrudePolygon, { IExtrudeGeomety } from '../shape/extrude'; -import { geometryShape, ShapeType } from '../shape/Path'; +import { geometryShape, ShapeType2D, ShapeType3D } from '../shape/Path'; interface IGeometryCache { [key: string]: IExtrudeGeomety; } @@ -26,7 +26,7 @@ export default class ExtrudeBuffer extends BaseBuffer { this.indexOffset = 0; layerData.forEach((feature: IEncodeFeature) => { const { shape } = feature; - const { positions, index } = this.getGeometry(shape as ShapeType); + const { positions, index } = this.getGeometry(shape as ShapeType3D); this.verticesCount += positions.length / 3; this.indexCount += index.length; }); @@ -39,7 +39,7 @@ export default class ExtrudeBuffer extends BaseBuffer { } private calculateFill(feature: IEncodeFeature) { const { coordinates, shape } = feature; - const instanceGeometry = this.getGeometry(shape as ShapeType); + const instanceGeometry = this.getGeometry(shape as ShapeType3D); const numPoint = instanceGeometry.positions.length / 3; feature.bufferInfo = { verticesOffset: this.verticesOffset, @@ -65,7 +65,7 @@ export default class ExtrudeBuffer extends BaseBuffer { this.indexOffset += indexArray.length; } - private getGeometry(shape: ShapeType): IExtrudeGeomety { + private getGeometry(shape: ShapeType3D): IExtrudeGeomety { if (this.geometryCache && this.geometryCache[shape]) { return this.geometryCache[shape]; } diff --git a/packages/layers/src/point/buffers/ImageBuffer.ts b/packages/layers/src/point/buffers/ImageBuffer.ts index cfe6a15254..c4494dcb7b 100644 --- a/packages/layers/src/point/buffers/ImageBuffer.ts +++ b/packages/layers/src/point/buffers/ImageBuffer.ts @@ -9,11 +9,11 @@ export default class ImageBuffer extends BaseBuffer { const layerData = this.data as IEncodeFeature[]; layerData.forEach((item: IEncodeFeature, index: number) => { const { color = [0, 0, 0, 0], size, id, shape, coordinates } = item; - const { x, y } = this.imagePos[shape]; + const { x, y } = this.iconMap[shape as string]; const coor = coordinates as Position; this.attributes.vertices.set([coor[0], coor[1], coor[2] || 0], index * 3); this.attributes.colors.set(color, index * 4); - this.attributes.pickingIds.set([id], index); + this.attributes.pickingIds.set([id as number], index); this.attributes.sizes.set([size as number], index); // this.attributes.uv.set([x, y], index * 2); }); diff --git a/packages/layers/src/point/point.ts b/packages/layers/src/point/point.ts index 36cfea61bd..7e7ad2a7ba 100644 --- a/packages/layers/src/point/point.ts +++ b/packages/layers/src/point/point.ts @@ -42,7 +42,6 @@ export default class PointLayer extends BaseLayer { data: this.getEncodedData(), }); buffer.computeVertexNormals('miters', false); - console.log(buffer); // TODO: normal const { createAttribute, createBuffer, diff --git a/packages/layers/src/point/shape/Path.ts b/packages/layers/src/point/shape/Path.ts index d9647ff73b..1e12eb5709 100644 --- a/packages/layers/src/point/shape/Path.ts +++ b/packages/layers/src/point/shape/Path.ts @@ -1,11 +1,18 @@ type IPosition = [number, number, number]; export type IPath = IPosition[]; -export enum ShapeType { - CIRCLE = 'cylinder', - SQUARE = 'squareColumn', - TRIANGLE = 'triangleColumn', - HEXAGON = 'hexagonColumn', - PENTAGON = 'pentagonColumn', +export enum ShapeType3D { + CYLINDER = 'cylinder', + SQUARECOLUMN = 'squareColumn', + TRIANGLECOLUMN = 'triangleColumn', + HEXAGONCOLUMN = 'hexagonColumn', + PENTAGONCOLUMN = 'pentagonColumn', +} +export enum ShapeType2D { + CIRCLE = 'circle', + SQUARE = 'square', + TRIANGLE = 'triangle', + HEXAGON = 'hexagon', + PENTAGON = 'pentagon', } /** @@ -24,7 +31,7 @@ export function polygonPath(pointCount: number, start: number = 0): IPath { const y = Math.cos(t + Math.PI / 4); return [x, y, 0]; }); - path.push(path[0]); + // path.push(path[0]); return path; } @@ -45,9 +52,14 @@ export function pentagon(): IPath { } export const geometryShape = { - [ShapeType.CIRCLE]: circle, - [ShapeType.HEXAGON]: hexagon, - [ShapeType.TRIANGLE]: triangle, - [ShapeType.SQUARE]: square, - [ShapeType.PENTAGON]: pentagon, + [ShapeType2D.CIRCLE]: circle, + [ShapeType2D.HEXAGON]: hexagon, + [ShapeType2D.TRIANGLE]: triangle, + [ShapeType2D.SQUARE]: square, + [ShapeType2D.PENTAGON]: pentagon, + [ShapeType3D.CYLINDER]: circle, + [ShapeType3D.HEXAGONCOLUMN]: hexagon, + [ShapeType3D.TRIANGLECOLUMN]: triangle, + [ShapeType3D.SQUARECOLUMN]: square, + [ShapeType3D.PENTAGONCOLUMN]: pentagon, }; diff --git a/packages/layers/src/point/shape/extrude.ts b/packages/layers/src/point/shape/extrude.ts index 8e0d8f7620..717d585609 100644 --- a/packages/layers/src/point/shape/extrude.ts +++ b/packages/layers/src/point/shape/extrude.ts @@ -60,3 +60,15 @@ export default function extrudePolygon(path: IPath[]): IExtrudeGeomety { index: indexArray, }; } +export function fillPolygon(points: IPath[]) { + const flattengeo = earcut.flatten(points); + const triangles = earcut( + flattengeo.vertices, + flattengeo.holes, + flattengeo.dimensions, + ); + return { + positions: flattengeo.vertices, + index: triangles, + }; +} diff --git a/packages/layers/src/raster/buffers/ImageBuffer.ts b/packages/layers/src/raster/buffers/ImageBuffer.ts index 1208b76460..66f3398e2d 100644 --- a/packages/layers/src/raster/buffers/ImageBuffer.ts +++ b/packages/layers/src/raster/buffers/ImageBuffer.ts @@ -4,14 +4,13 @@ interface IImageFeature extends IEncodeFeature { } export default class ImageBuffer extends BaseBuffer { protected calculateFeatures() { - const layerData = this.data as IImageFeature[]; - this.verticesCount = 4; + this.verticesCount = 6; this.indexCount = 6; } protected buildFeatures() { + this.attributes.uv = new Float32Array(this.verticesCount * 2); const layerData = this.data as IImageFeature[]; const coordinates = layerData[0].coordinates as Position[]; - const images = layerData[0].images; const positions: number[] = [ ...coordinates[0], 0, diff --git a/packages/layers/src/raster/index.ts b/packages/layers/src/raster/index.ts new file mode 100644 index 0000000000..1b4366dc4f --- /dev/null +++ b/packages/layers/src/raster/index.ts @@ -0,0 +1,87 @@ +import { + gl, + IRendererService, + IShaderModuleService, + ITexture2D, + lazyInject, + TYPES, +} from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import ImageBuffer from './buffers/ImageBuffer'; +import image_frag from './shaders/image_frag.glsl'; +import image_vert from './shaders/image_vert.glsl'; +export default class ImageLayer extends BaseLayer { + public name: string = 'imageLayer'; + @lazyInject(TYPES.IShaderModuleService) + private readonly shaderModule: IShaderModuleService; + + @lazyInject(TYPES.IRendererService) + private readonly renderer: IRendererService; + + protected renderModels() { + this.models.forEach((model) => + model.draw({ + uniforms: { + u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + }, + }), + ); + return this; + } + protected buildModels() { + const { + createAttribute, + createBuffer, + createElements, + createTexture2D, + createModel, + } = this.renderer; + this.shaderModule.registerModule('image', { + vs: image_vert, + fs: image_frag, + }); + + this.models = []; + const { vs, fs, uniforms } = this.shaderModule.getModule('image'); + const source = this.getSource(); + // const imageData = await source.data.images; + const buffer = new ImageBuffer({ + data: this.getEncodedData(), + }); + source.data.images.then((imageData: HTMLImageElement[]) => { + const texture: ITexture2D = createTexture2D({ + data: imageData[0], + width: imageData[0].width, + height: imageData[0].height, + }); + this.models.push( + createModel({ + attributes: { + a_Position: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.positions, + type: gl.FLOAT, + }), + size: 3, + }), + a_uv: createAttribute({ + buffer: createBuffer({ + data: buffer.attributes.uv, + type: gl.FLOAT, + }), + size: 2, + }), + }, + uniforms: { + ...uniforms, + u_texture: texture, + u_opacity: 1.0, + }, + fs, + vs, + count: buffer.verticesCount, + }), + ); + }); + } +} diff --git a/packages/layers/src/raster/shaders/image_frag.glsl b/packages/layers/src/raster/shaders/image_frag.glsl index a83beb90e7..8d3b5683b4 100644 --- a/packages/layers/src/raster/shaders/image_frag.glsl +++ b/packages/layers/src/raster/shaders/image_frag.glsl @@ -1,8 +1,8 @@ precision mediump float; +uniform float u_opacity: 1.0; uniform sampler2D u_texture; -uniform float u_opacity; varying vec2 v_texCoord; void main() { - vec4 color = texture2D(u_texture,vec2(v_texCoord.x,1.0-v_texCoord.y)); - gl_FragColor = color * u_opacity; + vec4 color = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y)); + gl_FragColor = color; } \ No newline at end of file diff --git a/packages/layers/src/raster/shaders/image_vert.glsl b/packages/layers/src/raster/shaders/image_vert.glsl index 778b2c576c..f0b98f4a57 100644 --- a/packages/layers/src/raster/shaders/image_vert.glsl +++ b/packages/layers/src/raster/shaders/image_vert.glsl @@ -1,9 +1,12 @@ precision highp float; -varying vec2 v_texCoord; uniform mat4 u_ModelMatrix; attribute vec3 a_Position; +attribute vec2 a_uv; +varying vec2 v_texCoord; + +#pragma include "projection" void main() { - v_texCoord = uv; + v_texCoord = a_uv; vec4 project_pos = project_position(vec4(a_Position, 1.0)); - gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0)); + gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0)); } \ No newline at end of file diff --git a/packages/layers/src/utils/normal2.ts b/packages/layers/src/utils/normal2.ts deleted file mode 100644 index 8689a286ba..0000000000 --- a/packages/layers/src/utils/normal2.ts +++ /dev/null @@ -1,195 +0,0 @@ -/** - * 对于 polyline-normal 的改进 - * 超过阈值,miter 转成 bevel 接头, - * 要注意 Three.js 中默认 THREE.FrontFaceDirectionCCW - * @see https://zhuanlan.zhihu.com/p/59541559 - */ - -// @ts-ignore -import { copy, create, dot } from 'gl-vec2'; -// @ts-ignore -import { computeMiter, direction, normal } from 'polyline-miter-util'; - -// @ts-ignore -function extrusions(positions, out, miters, point, normal1, scale) { - addNext(out, miters, normal1, -scale); - addNext(out, miters, normal1, scale); - positions.push(...point, 0); - positions.push(...point, 0); -} - -// @ts-ignore -// tslint:disable-next-line:no-shadowed-variable -function addNext(out, miters, normal, length) { - out.push(normal[0], normal[1], 0); - miters.push(length); -} -// @ts-ignore -function lineSegmentDistance(end, start) { - const dx = start[0] - end[0]; - const dy = start[1] - end[1]; - const dz = start[2] - end[2]; - return Math.sqrt(dx * dx + dy * dy + dz * dz); -} -// @ts-ignore -function isPointEqual(a, b) { - return a[0] === b[0] && a[1] === b[1]; -} -// @ts-ignore -export default function(points, closed, indexOffset) { - const lineA = [0, 0]; - const lineB = [0, 0]; - const tangent = [0, 0]; - const miter = [0, 0]; - // tslint:disable-next-line:variable-name - let _started = false; - // tslint:disable-next-line:variable-name - let _normal = null; - const tmp = create(); - let count = indexOffset || 0; - const miterLimit = 3; -// @ts-ignore - const out = []; - const attrPos = []; - const attrIndex = []; - // @ts-ignore - const miters = []; - const attrDistance = [0, 0]; - if (closed) { - points = points.slice(); - points.push(points[0]); - } - - const total = points.length; - - for (let i = 1; i < total; i++) { - const index = count; - const last = points[i - 1]; - const cur = points[i]; - let next = i < points.length - 1 ? points[i + 1] : null; - // 如果当前点和前一点相同,跳过 - if (isPointEqual(last, cur)) { - continue; - } - if (next) { - let nextIndex = i + 1; - // 找到不相同的下一点 - while (next && isPointEqual(cur, next)) { - next = nextIndex < points.length - 1 ? points[++nextIndex] : null; - } - } - const lineDistance = lineSegmentDistance(cur, last); - const d = lineDistance + attrDistance[attrDistance.length - 1]; - - direction(lineA, cur, last); - - if (!_normal) { - _normal = [0, 0]; - normal(_normal, lineA); - } - - if (!_started) { - _started = true; - // @ts-ignore - extrusions(attrPos, out, miters, last, _normal, 1); - } - - attrIndex.push(index + 0, index + 2, index + 1); - - // no miter, simple segment - if (!next) { - // reset normal - normal(_normal, lineA); - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - attrDistance.push(d, d); - attrIndex.push(index + 1, index + 2, index + 3); - count += 2; - } else { - // get unit dir of next line - direction(lineB, next, cur); - - // stores tangent & miter - let miterLen = computeMiter(tangent, miter, lineA, lineB, 1); - - // get orientation - const flip = dot(tangent, _normal) < 0 ? -1 : 1; - const bevel = Math.abs(miterLen) > miterLimit; - - // 处理前后两条线段重合的情况,这种情况不需要使用任何接头(miter/bevel)。 - // 理论上这种情况下 miterLen = Infinity,本应通过 isFinite(miterLen) 判断, - // 但是 AMap 投影变换后丢失精度,只能通过一个阈值(1000)判断。 - if (Math.abs(miterLen) > 1000) { - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - attrIndex.push(index + 1, index + 2, index + 3); - attrIndex.push(index + 2, index + 4, index + 3); - normal(tmp, lineB); - copy(_normal, tmp); // store normal for next round - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - attrDistance.push(d, d, d, d); - - // the miter is now the normal for our next join - count += 4; - continue; - } - - if (bevel) { - miterLen = miterLimit; - - // next two points in our first segment - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - - attrIndex.push(index + 1, index + 2, index + 3); - - // now add the bevel triangle - attrIndex.push( - ...(flip === 1 - ? [index + 2, index + 4, index + 5] - : [index + 4, index + 5, index + 3]), - ); - - normal(tmp, lineB); - copy(_normal, tmp); // store normal for next round - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - attrDistance.push(d, d, d, d); - - // the miter is now the normal for our next join - count += 4; - } else { - // next two points in our first segment - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - attrIndex.push(index + 1, index + 2, index + 3); - - // now add the miter triangles - // @ts-ignore - addNext(out, miters, miter, miterLen * -flip); - attrPos.push(...cur, 0); - attrIndex.push(index + 2, index + 4, index + 3); - attrIndex.push(index + 4, index + 5, index + 6); - normal(tmp, lineB); - copy(_normal, tmp); // store normal for next round - // @ts-ignore - extrusions(attrPos, out, miters, cur, _normal, 1); - attrDistance.push(d, d, d, d, d); - - // the miter is now the normal for our next join - count += 5; - } - } - } - // @ts-ignore - return { - // @ts-ignore - normals: out, - attrIndex, - attrPos, - attrDistance, - // @ts-ignore - miters, - }; -} diff --git a/packages/layers/src/utils/polylineNormal.ts b/packages/layers/src/utils/polylineNormal.ts index 9ad8e482aa..86c98b0510 100644 --- a/packages/layers/src/utils/polylineNormal.ts +++ b/packages/layers/src/utils/polylineNormal.ts @@ -1,5 +1,5 @@ +import { aProjectFlat, lngLatToMeters, Point } from '@l7/utils'; import { vec2 } from 'gl-matrix'; - export function computeMiter( tangent: vec2, miter: vec2, @@ -17,55 +17,66 @@ export function computeNormal(out: vec2, dir: vec2) { return vec2.set(out, -dir[1], dir[0]); } export function direction(out: vec2, a: vec2, b: vec2) { - vec2.sub(out, a, b); + const a1 = aProjectFlat([a[0], a[1]]); + const b1 = aProjectFlat([b[0], b[1]]); + vec2.sub(out, a1, b1); vec2.normalize(out, out); return out; } function extrusions( positions: number[], - out: vec2, - miters: vec2, + out: number[], + miters: number[], point: vec2, normal: vec2, - scale, + scale: number, ) { addNext(out, miters, normal, -scale); addNext(out, miters, normal, scale); - positions.push(...point); - positions.push(...point); + positions.push(point[0], point[1], 0); + positions.push(point[0], point[1], 0); } -function addNext(out, miters, normal, length) { +function addNext( + out: number[], + miters: number[], + normal: vec2, + length: number, +) { out.push(normal[0], normal[1], 0); miters.push(length); } -function lineSegmentDistance(end, start) { +function lineSegmentDistance(end: vec2, start: vec2) { const dx = start[0] - end[0]; const dy = start[1] - end[1]; - const dz = start[2] - end[2]; - return Math.sqrt(dx * dx + dy * dy + dz * dz); + // const dz = start[2] - end[2]; + return Math.sqrt(dx * dx + dy * dy); } -function isPointEqual(a, b) { +function isPointEqual(a: vec2, b: vec2) { return a[0] === b[0] && a[1] === b[1]; } -export default function(points, closed, indexOffset) { +export default function( + points: number[][], + closed: boolean, + indexOffset: number, +) { const lineA = vec2.fromValues(0, 0); const lineB = vec2.fromValues(0, 0); const tangent = vec2.fromValues(0, 0); - const miter = vec2.fromValues(0, 0); - let _started = false; - let _normal = null; + const miter: vec2 = vec2.create(); + let started = false; + let lineNormal = null; const tmp = vec2.create(); let count = indexOffset || 0; const miterLimit = 3; - const out = []; - const attrPos = []; - const attrIndex = []; - const miters = []; + const out: number[] = []; + const attrPos: number[] = []; + const attrIndex: number[] = []; + const miters: number[] = []; const attrDistance = [0, 0]; if (closed) { points = points.slice(); @@ -76,9 +87,12 @@ export default function(points, closed, indexOffset) { for (let i = 1; i < total; i++) { const index = count; - const last = points[i - 1]; - const cur = points[i]; - let next = i < points.length - 1 ? points[i + 1] : null; + const last = vec2.fromValues(points[i - 1][0], points[i - 1][1]); + const cur = vec2.fromValues(points[i][0], points[i][1]); + let next = + i < points.length - 1 + ? vec2.fromValues(points[i + 1][0], points[i + 1][1]) + : null; // 如果当前点和前一点相同,跳过 if (isPointEqual(last, cur)) { continue; @@ -87,22 +101,23 @@ export default function(points, closed, indexOffset) { let nextIndex = i + 1; // 找到不相同的下一点 while (next && isPointEqual(cur, next)) { - next = nextIndex < points.length - 1 ? points[++nextIndex] : null; + next = + nextIndex < points.length - 1 + ? vec2.fromValues(points[++nextIndex][0], points[nextIndex][1]) + : null; } } - const lineDistance = lineSegmentDistance(cur, last); + const lineDistance = lineSegmentDistance(cur, last); // TODO: 根据平面坐标计算距离 const d = lineDistance + attrDistance[attrDistance.length - 1]; - direction(lineA, cur, last); - - if (!_normal) { - _normal = [0, 0]; - computeNormal(_normal, lineA); + if (!lineNormal) { + lineNormal = vec2.create(); + computeNormal(lineNormal, lineA); } - if (!_started) { - _started = true; - extrusions(attrPos, out, miters, last, _normal, 1); + if (!started) { + started = true; + extrusions(attrPos, out, miters, last, lineNormal, 1); } attrIndex.push(index + 0, index + 2, index + 1); @@ -110,8 +125,8 @@ export default function(points, closed, indexOffset) { // no miter, simple segment if (!next) { // reset normal - computeNormal(_normal, lineA); - extrusions(attrPos, out, miters, cur, _normal, 1); + computeNormal(lineNormal, lineA); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrDistance.push(d, d); attrIndex.push(index + 1, index + 2, index + 3); count += 2; @@ -120,23 +135,30 @@ export default function(points, closed, indexOffset) { direction(lineB, next, cur); // stores tangent & miter - let miterLen = computeMiter(tangent, miter, lineA, lineB, 1); + let miterLen = computeMiter( + tangent, + vec2.fromValues(miter[0], miter[1]), + lineA, + lineB, + 1, + ); // get orientation - const flip = vec2.dot(tangent, _normal) < 0 ? -1 : 1; + const flip = vec2.dot(tangent, lineNormal) < 0 ? -1 : 1; const bevel = Math.abs(miterLen) > miterLimit; // 处理前后两条线段重合的情况,这种情况不需要使用任何接头(miter/bevel)。 // 理论上这种情况下 miterLen = Infinity,本应通过 isFinite(miterLen) 判断, // 但是 AMap 投影变换后丢失精度,只能通过一个阈值(1000)判断。 + if (Math.abs(miterLen) > 1000) { - extrusions(attrPos, out, miters, cur, _normal, 1); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrIndex.push(index + 1, index + 2, index + 3); attrIndex.push(index + 2, index + 4, index + 3); computeNormal(tmp, lineB); - vec2.copy(_normal, tmp); // store normal for next round + vec2.copy(lineNormal, tmp); // store normal for next round - extrusions(attrPos, out, miters, cur, _normal, 1); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrDistance.push(d, d, d, d); // the miter is now the normal for our next join @@ -148,7 +170,7 @@ export default function(points, closed, indexOffset) { miterLen = miterLimit; // next two points in our first segment - extrusions(attrPos, out, miters, cur, _normal, 1); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrIndex.push(index + 1, index + 2, index + 3); @@ -160,27 +182,27 @@ export default function(points, closed, indexOffset) { ); computeNormal(tmp, lineB); - vec2.copy(_normal, tmp); // store normal for next round + vec2.copy(lineNormal, tmp); // store normal for next round - extrusions(attrPos, out, miters, cur, _normal, 1); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrDistance.push(d, d, d, d); // the miter is now the normal for our next join count += 4; } else { // next two points in our first segment - extrusions(attrPos, out, miters, cur, _normal, 1); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrIndex.push(index + 1, index + 2, index + 3); // now add the miter triangles - addNext(out, miters, miter, miterLen * -flip); - attrPos.push(...cur); + addNext(out, miters, lineNormal, miterLen * -flip); + attrPos.push(cur[0], cur[1], 0); attrIndex.push(index + 2, index + 4, index + 3); attrIndex.push(index + 4, index + 5, index + 6); computeNormal(tmp, lineB); - vec2.copy(_normal, tmp); // store normal for next round + vec2.copy(lineNormal, tmp); // store normal for next round - extrusions(attrPos, out, miters, cur, _normal, 1); + extrusions(attrPos, out, miters, cur, lineNormal, 1); attrDistance.push(d, d, d, d, d); // the miter is now the normal for our next join diff --git a/packages/renderer/src/regl/ReglModel.ts b/packages/renderer/src/regl/ReglModel.ts index 11c5b046c0..e688a19e4f 100644 --- a/packages/renderer/src/regl/ReglModel.ts +++ b/packages/renderer/src/regl/ReglModel.ts @@ -44,8 +44,8 @@ export default class ReglModel implements IModel { blend, stencil, cull, + instances, } = options; - const reglUniforms: { [key: string]: IUniform } = {}; if (uniforms) { this.uniforms = uniforms; @@ -60,7 +60,6 @@ export default class ReglModel implements IModel { Object.keys(attributes).forEach((name: string) => { reglAttributes[name] = (attributes[name] as ReglAttribute).get(); }); - const drawParams: regl.DrawConfig = { attributes: reglAttributes, frag: fs, @@ -70,6 +69,9 @@ export default class ReglModel implements IModel { primitiveMap[primitive === undefined ? gl.TRIANGLES : primitive], count, }; + if (instances) { + drawParams.instances = instances; + } if (elements) { drawParams.elements = (elements as ReglElements).get(); diff --git a/packages/renderer/src/regl/index.ts b/packages/renderer/src/regl/index.ts index a03f46baf4..8b1d617c03 100644 --- a/packages/renderer/src/regl/index.ts +++ b/packages/renderer/src/regl/index.ts @@ -56,6 +56,7 @@ export default class ReglRendererService implements IRendererService { 'EXT_SRGB', // baseColor emmisive 'OES_texture_float', // shadow map 'WEBGL_depth_texture', + 'angle_instanced_arrays', 'EXT_texture_filter_anisotropic', // VSM shadow map ], optionalExtensions: ['oes_texture_float_linear'], diff --git a/packages/source/src/interface.ts b/packages/source/src/interface.ts index ff9f989e1b..fa6612f2ef 100644 --- a/packages/source/src/interface.ts +++ b/packages/source/src/interface.ts @@ -1,24 +1,4 @@ export type DataType = string | object[] | object; -export interface IParserCfg { - type: string; - x?: string; - y?: string; - x1?: string; - y1?: string; - coordinates?: string; - [key: string]: any; -} -type CallBack = (...args: any[]) => any; -export interface ITransform { - type: string; - [key: string]: any; - callback: CallBack; -} - -export interface ISourceCFG { - parser?: IParserCfg; - transforms?: ITransform[]; -} export interface IDictionary { [key: string]: TValue; } diff --git a/packages/source/src/parser/csv.ts b/packages/source/src/parser/csv.ts index 3e8ccfce1e..7c42bb57ed 100644 --- a/packages/source/src/parser/csv.ts +++ b/packages/source/src/parser/csv.ts @@ -1,5 +1,5 @@ import { csvParse } from 'd3-dsv'; -import { IJsonData, IParserCfg, IParserData } from '../interface'; +import { IJsonData, IParserCfg, IParserData } from '@l7/core'; import json from './json'; export default function csv(data: string, cfg: IParserCfg): IParserData { const csvData: IJsonData = csvParse(data); diff --git a/packages/source/src/parser/image.ts b/packages/source/src/parser/image.ts index 72fad48edf..bd5e912c72 100644 --- a/packages/source/src/parser/image.ts +++ b/packages/source/src/parser/image.ts @@ -1,5 +1,5 @@ +import { getImage } from '@l7/utils'; import { IParserData } from '../interface'; - interface IImageCfg { extent: [number, number, number, number]; } @@ -8,9 +8,13 @@ export default function image( cfg: IImageCfg, ): IParserData { const { extent } = cfg; - + const images = new Promise((resolve) => { + loadData(data, (res: any) => { + resolve(res); + }); + }); const resultData: IParserData = { - images: loadData(data), + images, _id: 1, dataArray: [ { @@ -21,16 +25,26 @@ export default function image( }; return resultData; } -function loadData(data: string | string[]): Promise { +function loadData(data: string | string[], done: any) { const url = data; + const imageDatas: HTMLImageElement[] = []; if (typeof url === 'string') { - const imageRequest = new Request(url); - return fetch(imageRequest); + getImage({ url }, (err: string, img: HTMLImageElement) => { + imageDatas.push(img); + done(imageDatas); + }); } else { - const fetchs = url.map((item: string) => { - const imageRequest = new Request(item); - return fetch(imageRequest); + const imageCount = url.length; + let imageindex = 0; + url.forEach((item) => { + getImage({ url: item }, (err: any, img: HTMLImageElement) => { + imageindex++; + imageDatas.push(img); + if (imageindex === imageCount) { + done(imageDatas); + } + }); }); - return Promise.all(fetchs); } + return image; } diff --git a/packages/source/src/parser/json.ts b/packages/source/src/parser/json.ts index ca822dc2ad..c42dc57965 100644 --- a/packages/source/src/parser/json.ts +++ b/packages/source/src/parser/json.ts @@ -6,7 +6,7 @@ import { IParseDataItem, IParserCfg, IParserData, -} from '../interface'; +} from '@l7/core'; export default function json(data: IJsonData, cfg: IParserCfg): IParserData { const { x, y, x1, y1, coordinates } = cfg; const resultData: IParseDataItem[] = []; diff --git a/packages/source/src/source.ts b/packages/source/src/source.ts index e11d737949..48ac92e7fb 100644 --- a/packages/source/src/source.ts +++ b/packages/source/src/source.ts @@ -36,6 +36,9 @@ export default class Source extends EventEmitter { this.hooks.init.tap('parser', () => { this.excuteParser(); }); + this.hooks.init.tap('transform', () => { + this.executeTrans(); + }); this.init(); } @@ -60,6 +63,5 @@ export default class Source extends EventEmitter { } private init() { this.hooks.init.call(this); - // this.excuteParser(); // 数据解析 } } diff --git a/packages/source/src/transform/grid.ts b/packages/source/src/transform/grid.ts index 73e834fd3a..b55581f347 100644 --- a/packages/source/src/transform/grid.ts +++ b/packages/source/src/transform/grid.ts @@ -24,9 +24,9 @@ export function aggregatorToGrid(data: IParserData, option: ITransform) { const { gridHash, gridOffset } = _pointsGridHash(dataArray, size); const layerData = _getGridLayerDataFromGridHash(gridHash, gridOffset, option); return { - yOffset: ((gridOffset.xOffset / 360) * (256 << 20)) / 2, - xOffset: ((gridOffset.xOffset / 360) * (256 << 20)) / 2, - radius: ((gridOffset.xOffset / 360) * (256 << 20)) / 2, + yOffset: gridOffset.yOffset / 1.8, + xOffset: gridOffset.xOffset / 1.8, + radius: gridOffset.xOffset, dataArray: layerData, }; } @@ -42,8 +42,8 @@ function _pointsGridHash(dataArray: any[], size: number) { latMax = pLat > latMax ? pLat : latMax; } } - // const centerLat = (latMin + latMax) / 2; - const centerLat = 34.54083; + const centerLat = (latMin + latMax) / 2; + // const centerLat = 34.54083; const gridOffset = _calculateGridLatLonOffset(size, centerLat); if (gridOffset.xOffset <= 0 || gridOffset.yOffset <= 0) { return { gridHash: {}, gridOffset }; diff --git a/packages/utils/src/fetchData.ts b/packages/utils/src/fetchData.ts new file mode 100644 index 0000000000..8430c34b4b --- /dev/null +++ b/packages/utils/src/fetchData.ts @@ -0,0 +1,124 @@ +class AJAXError extends Error { + private status: number; + private url: string; + + constructor(message: string, status: number, url: string) { + super(message); + this.status = status; + this.url = url; + + // work around for https://github.com/Rich-Harris/buble/issues/40 + this.name = this.constructor.name; + this.message = message; + } + + public toString() { + return `${this.name}: ${this.message} (${this.status}): ${this.url}`; + } +} + +function makeRequest(requestParameters: any) { + const xhr = new XMLHttpRequest(); + + xhr.open('GET', requestParameters.url, true); + for (const k in requestParameters.headers) { + if (requestParameters.headers.hasOwnProperty(k)) { + xhr.setRequestHeader(k, requestParameters.headers[k]); + } + } + xhr.withCredentials = requestParameters.credentials === 'include'; + return xhr; +} + +export const getJSON = (requestParameters: any, callback: any) => { + const xhr = makeRequest(requestParameters); + xhr.setRequestHeader('Accept', 'application/json'); + xhr.onerror = () => { + callback(new Error(xhr.statusText)); + }; + xhr.onload = () => { + if (xhr.status >= 200 && xhr.status < 300 && xhr.response) { + let data; + try { + data = JSON.parse(xhr.response); + } catch (err) { + return callback(err); + } + callback(null, data); + } else { + if (xhr.status === 401) { + callback( + new AJAXError(`${xhr.statusText}`, xhr.status, requestParameters.url), + ); + } else { + callback( + new AJAXError(xhr.statusText, xhr.status, requestParameters.url), + ); + } + } + }; + xhr.send(); + return xhr; +}; + +export const getArrayBuffer = (requestParameters: any, callback: any) => { + const xhr = makeRequest(requestParameters); + xhr.responseType = 'arraybuffer'; + xhr.onerror = () => { + callback(new Error(xhr.statusText)); + }; + xhr.onload = () => { + const response = xhr.response; + if (response.byteLength === 0 && xhr.status === 200) { + return callback(new Error('http status 200 returned without content.')); + } + if (xhr.status >= 200 && xhr.status < 300 && xhr.response) { + callback(null, { + data: response, + cacheControl: xhr.getResponseHeader('Cache-Control'), + expires: xhr.getResponseHeader('Expires'), + }); + } else { + callback( + new AJAXError(xhr.statusText, xhr.status, requestParameters.url), + ); + } + }; + xhr.send(); + return xhr; +}; + +function sameOrigin(url: string) { + const a = window.document.createElement('a'); + a.href = url; + return ( + a.protocol === window.document.location.protocol && + a.host === window.document.location.host + ); +} + +const transparentPngUrl = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII='; + +export const getImage = (requestParameters: any, callback: any) => { + // request the image with XHR to work around caching issues + // see https://github.com/mapbox/mapbox-gl-js/issues/1470 + return getArrayBuffer(requestParameters, (err: string, imgData: any) => { + if (err) { + callback(err); + } else if (imgData) { + const img = new window.Image(); + const URL = window.URL || window.webkitURL; + img.onload = () => { + callback(null, img); + URL.revokeObjectURL(img.src); + }; + const blob = new window.Blob([new Uint8Array(imgData.data)], { + type: 'image/png', + }); + img.src = imgData.data.byteLength + ? URL.createObjectURL(blob) + : transparentPngUrl; + } + }); +}; diff --git a/packages/utils/src/geo.ts b/packages/utils/src/geo.ts index 906cd2011c..851cc4b62a 100644 --- a/packages/utils/src/geo.ts +++ b/packages/utils/src/geo.ts @@ -1,6 +1,6 @@ import { BBox } from '@turf/helpers'; const originShift = (2 * Math.PI * 6378137) / 2.0; -type Point = [number, number] | [number, number, number]; +export type Point = [number, number] | [number, number, number]; /** * 计算地理数据范围 * @param {dataArray} data 地理坐标数据 @@ -137,3 +137,20 @@ export function validateLngLat(lnglat: Point, validate: boolean): Point { } return lnglat.length === 3 ? [lng, lat, lnglat[2]] : [lng, lat]; } +export function aProjectFlat(lnglat: number[]) { + const maxs = 85.0511287798; + const lat = Math.max(Math.min(maxs, lnglat[1]), -maxs); + const scale = 256 << 20; + let d = Math.PI / 180; + let x = lnglat[0] * d; + let y = lat * d; + y = Math.log(Math.tan(Math.PI / 4 + y / 2)); + + const a = 0.5 / Math.PI; + const b = 0.5; + const c = -0.5 / Math.PI; + d = 0.5; + x = scale * (a * x + b) - 215440491; + y = scale * (c * y + d) - 106744817; + return [parseInt(x.toString(), 10), parseInt(y.toString(), 10)]; +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 7165880398..e581a9d4e1 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,2 +1,3 @@ export { djb2hash, BKDRHash } from './hash'; +export * from './fetchData'; export * from './geo'; diff --git a/stories/MapAdaptor/Map.stories.tsx b/stories/MapAdaptor/Map.stories.tsx index c1ede60b07..0aca4641a4 100644 --- a/stories/MapAdaptor/Map.stories.tsx +++ b/stories/MapAdaptor/Map.stories.tsx @@ -4,6 +4,9 @@ import AMap from './components/AMap'; import Mapbox from './components/Mapbox'; import Polygon from './components/Polygon'; import Point3D from './components/Point3D'; +import Line from './components/Line'; +import ImageLayer from './components/Image'; +import GridHeatMap from './components/GridHeatmap'; // @ts-ignore import notes from './Map.md'; @@ -15,4 +18,7 @@ storiesOf('地图底图测试', module) notes: { markdown: notes }, }) .add('Polygon', () => ) - .add('Point3D', () => ); + .add('Point3D', () => ) + .add('Line', () => ) + .add('GridHeatMap', () => ) + .add('Image', () => ); diff --git a/stories/MapAdaptor/components/GridHeatmap.tsx b/stories/MapAdaptor/components/GridHeatmap.tsx new file mode 100644 index 0000000000..9326fa5ad9 --- /dev/null +++ b/stories/MapAdaptor/components/GridHeatmap.tsx @@ -0,0 +1,77 @@ +import { HeatMapLayer } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; + +export default class GridHeatMap extends React.Component { + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public async componentDidMount() { + const response = await fetch( + 'https://gw.alipayobjects.com/os/basement_prod/c3f8bda2-081b-449d-aa9f-9413b779205b.json', + ); + const scene = new Scene({ + center: [116.49434030056, 39.868073421167621], + id: 'map', + pitch: 0, + type: 'amap', + style: 'mapbox://styles/mapbox/streets-v9', + zoom: 16, + }); + const layer = new HeatMapLayer({}); + layer + .source(await response.json(), { + parser: { + type: 'json', + x: 'lng', + y: 'lat', + }, + transforms: [ + { + type: 'grid', + size: 50, + field: 'count', + method: 'sum', + }, + ], + }) + .size('sum', (value: number) => { + return value; + }) + .shape('circle') + .style({ + coverage: 1.2, + angle: 0, + }) + .color('count', [ + '#002466', + '#105CB3', + '#2894E0', + '#CFF6FF', + '#FFF5B8', + '#FFAB5C', + '#F27049', + '#730D1C', + ]); + scene.addLayer(layer); + scene.render(); + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/MapAdaptor/components/Image.tsx b/stories/MapAdaptor/components/Image.tsx new file mode 100644 index 0000000000..77791ba5bb --- /dev/null +++ b/stories/MapAdaptor/components/Image.tsx @@ -0,0 +1,50 @@ +import { ImageLayer } from '@l7/layers'; +import { Scene } from '@l7/scene'; +import * as React from 'react'; + +export default class ImageLayerDemo extends React.Component { + private scene: Scene; + + public componentWillUnmount() { + this.scene.destroy(); + } + + public componentDidMount() { + const scene = new Scene({ + center: [121.2680, 30.3628], + id: 'map', + pitch: 0, + type: 'mapbox', + style: 'mapbox://styles/mapbox/streets-v9', + zoom: 10, + }); + const layer = new ImageLayer({}); + layer.source( + 'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg', + { + parser: { + type: 'image', + extent: [121.168, 30.2828, 121.384, 30.4219], + }, + }, + ); + scene.addLayer(layer); + scene.render(); + this.scene = scene; + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/MapAdaptor/components/Line.tsx b/stories/MapAdaptor/components/Line.tsx index 44ca992fed..590a119418 100644 --- a/stories/MapAdaptor/components/Line.tsx +++ b/stories/MapAdaptor/components/Line.tsx @@ -36,15 +36,29 @@ export default class Point3D extends React.Component { pitch: 0, type: 'mapbox', style: 'mapbox://styles/mapbox/dark-v9', - zoom: 2, + zoom: 13, }); const LineLayer = new Line({}); - LineLayer.source(testdata) - .size(5) - .color('red') + LineLayer.source(await response.json()) + .size(1) .shape('line') - .size(10); + .color( + 'ELEV', + [ + '#E8FCFF', + '#CFF6FF', + '#A1E9ff', + '#65CEF7', + '#3CB1F0', + '#2894E0', + '#1772c2', + '#105CB3', + '#0D408C', + '#002466', + ].reverse(), + ) + .render(); scene.addLayer(LineLayer); // function run() { // scene.render(); diff --git a/stories/MapAdaptor/components/Point3D.tsx b/stories/MapAdaptor/components/Point3D.tsx index 49bd61170a..80c66eef1d 100644 --- a/stories/MapAdaptor/components/Point3D.tsx +++ b/stories/MapAdaptor/components/Point3D.tsx @@ -46,10 +46,6 @@ export default class Point3D extends React.Component { // requestAnimationFrame(run); scene.render(); this.scene = scene; - console.log(pointLayer); - - // @ts-ignore - window.layer = pointLayer; } public render() { diff --git a/yarn.lock b/yarn.lock index 58dc8f9f57..dbc4a9652f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7295,6 +7295,11 @@ gl-matrix@^3.0.0, gl-matrix@^3.1.0: resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.1.0.tgz#f5b2de17d8fed95a79e5025b10cded0ab9ccbed0" integrity sha512-526NA+3EA+ztAQi0IZpSWiM0fyQXIp7IbRvfJ4wS/TjjQD0uv0fVybXwwqqSOlq33UckivI0yMDlVtboWm3k7A== +gl-vec2@^1.0.0, gl-vec2@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/gl-vec2/-/gl-vec2-1.3.0.tgz#83d472ed46034de8e09cbc857123fb6c81c51199" + integrity sha512-YiqaAuNsheWmUV0Sa8k94kBB0D6RWjwZztyO+trEYS8KzJ6OQB/4686gdrf59wld4hHFIvaxynO3nRxpk1Ij/A== + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -11387,6 +11392,13 @@ polished@^3.3.1: dependencies: "@babel/runtime" "^7.4.5" +polyline-miter-util@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/polyline-miter-util/-/polyline-miter-util-1.0.1.tgz#b693f2389ea0ded36a6bcf5ecd2ece4b6917d957" + integrity sha1-tpPyOJ6g3tNqa89ezS7OS2kX2Vc= + dependencies: + gl-vec2 "^1.0.0" + popper.js@^1.14.4, popper.js@^1.14.7: version "1.15.0" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2"