From ddd1d0ef38cc44767d2ec5329eb844c31d847938 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 30 Oct 2019 11:52:36 +0800 Subject: [PATCH] feat(layers): add girdheatmap add raster imagelayer --- packages/layers/src/core/triangulation.ts | 74 +++++++++- packages/layers/src/heatmap/grid.ts | 132 ++++++++++++++++++ .../src/heatmap/shaders/hexagon_frag.glsl | 6 +- .../src/heatmap/shaders/hexagon_vert.glsl | 14 +- packages/layers/src/index.ts | 6 +- packages/layers/src/raster/image.ts | 111 +++++++++++++++ packages/layers/src/raster/index.ts | 87 ------------ .../layers/src/raster/shaders/image_frag.glsl | 2 +- .../layers/src/raster/shaders/image_vert.glsl | 6 +- packages/maps/src/amap/index.ts | 2 +- stories/Layers/Layers.stories.tsx | 6 +- stories/Layers/components/heatMapgrid.tsx | 77 ++++++++++ stories/Layers/components/rasterImage.tsx | 50 +++++++ stories/MapAdaptor/components/GridHeatmap.tsx | 3 +- 14 files changed, 469 insertions(+), 107 deletions(-) create mode 100644 packages/layers/src/heatmap/grid.ts create mode 100644 packages/layers/src/raster/image.ts create mode 100644 stories/Layers/components/heatMapgrid.tsx create mode 100644 stories/Layers/components/rasterImage.tsx diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index ca390c3359..e6d219000c 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -1,7 +1,7 @@ import { IEncodeFeature } from '@l7/core'; import { vec3 } from 'gl-matrix'; import getNormals from '../utils/polylineNormal'; -import extrudePolygon, { IExtrudeGeomety } from './shape/extrude'; +import extrudePolygon, { fillPolygon, IExtrudeGeomety } from './shape/extrude'; import { geometryShape, IPosition, @@ -80,6 +80,67 @@ export function PolygonExtrudeTriangulation(feature: IEncodeFeature) { }; } +export function HeatmapGridTriangulation(feature: IEncodeFeature) { + const { shape } = feature; + + const { positions, index } = getHeatmapGeometry(shape as + | ShapeType2D + | ShapeType3D); + return { + vertices: positions, // [ x, y, z ] + indices: index, + normals: Array.from(computeVertexNormals(positions, index)), + size: 3, + }; +} + +/** + * 图片图层顶点构造 + * @param feature 数据 + */ +export function RasterImageTriangulation(feature: IEncodeFeature) { + const coordinates = feature.coordinates as IPosition[]; + // [ x, y, z. uv.x, uv.y] + const positions: number[] = [ + ...coordinates[0], + 0, + 0, + 1, + coordinates[1][0], + coordinates[0][1], + 0, + 1, + 1, + ...coordinates[1], + 0, + 1, + 0, + ...coordinates[0], + 0, + 0, + 1, + ...coordinates[1], + 0, + 1, + 0, + coordinates[0][0], + coordinates[1][1], + 0, + 0, + 0, + ]; + const indexs = [0, 1, 2, 3, 4, 5]; + return { + vertices: positions, + indices: indexs, + size: 5, + }; +} + +/** + * 点图层3d geomerty + * @param shape 3D形状 + */ function getGeometry(shape: ShapeType3D): IExtrudeGeomety { if (GeometryCache && GeometryCache[shape]) { return GeometryCache[shape]; @@ -145,3 +206,14 @@ function checkIsClosed(points: number[][][]) { const p2 = points[0][points[0].length - 1]; return p1[0] === p2[0] && p1[1] === p2[1]; } + +function getHeatmapGeometry(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/grid.ts b/packages/layers/src/heatmap/grid.ts new file mode 100644 index 0000000000..388f4ff8ec --- /dev/null +++ b/packages/layers/src/heatmap/grid.ts @@ -0,0 +1,132 @@ +import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { HeatmapGridTriangulation } from '../core/triangulation'; +import heatmapGridFrag from './shaders/hexagon_frag.glsl'; +import heatmapGridVert from './shaders/hexagon_vert.glsl'; +interface IHeatMapLayerStyleOptions { + opacity: number; + coverage: number; +} +export default class HeatMapGrid extends BaseLayer { + public name: string = 'PointLayer'; + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + + protected renderModels() { + const { opacity, coverage } = this.getStyleOptions(); + this.models.forEach((model) => + model.draw({ + uniforms: { + u_Opacity: opacity || 1.0, + u_coverage: coverage || 1.0, + u_radius: [ + this.getSource().data.xOffset, + this.getSource().data.yOffset, + ], + }, + }), + ); + return this; + } + + protected buildModels() { + this.registerBuiltinAttributes(this); + this.models = [ + this.buildLayerModel({ + moduleName: 'pointExtrude', + vertexShader: heatmapGridVert, + fragmentShader: heatmapGridFrag, + triangulation: HeatmapGridTriangulation, + blend: { + enable: true, + func: { + srcRGB: gl.SRC_ALPHA, + srcAlpha: 1, + dstRGB: gl.ONE_MINUS_SRC_ALPHA, + dstAlpha: 1, + }, + }, + }), + ]; + } + + private registerBuiltinAttributes(layer: ILayer) { + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'size', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Size', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 3, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + const { size } = feature; + return Array.isArray(size) ? [size[0]] : [size as number]; + }, + }, + }); + + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'normal', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Normal', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.STATIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 3, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + normal: number[], + ) => { + return normal; + }, + }, + }); + layer.styleAttributeService.registerStyleAttribute({ + name: 'pos', // 顶点经纬度位置 + type: AttributeType.Attribute, + descriptor: { + name: 'a_Pos', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 3, + update: (feature: IEncodeFeature, featureIdx: number) => { + const coordinates = feature.coordinates as number[]; + return [coordinates[0], coordinates[1], 0]; + }, + }, + }); + } +} diff --git a/packages/layers/src/heatmap/shaders/hexagon_frag.glsl b/packages/layers/src/heatmap/shaders/hexagon_frag.glsl index c9944e0a01..9bc316c11d 100644 --- a/packages/layers/src/heatmap/shaders/hexagon_frag.glsl +++ b/packages/layers/src/heatmap/shaders/hexagon_frag.glsl @@ -1,7 +1,7 @@ precision highp float; varying vec4 v_color; -uniform float u_opacity: 0.1; +uniform float u_Opacity: 0.1; void main() { gl_FragColor = v_color; - gl_FragColor.a *= u_opacity; -} \ No newline at end of file + gl_FragColor.a *= u_Opacity; +} diff --git a/packages/layers/src/heatmap/shaders/hexagon_vert.glsl b/packages/layers/src/heatmap/shaders/hexagon_vert.glsl index 36bd09e2c0..2f68a0d4e4 100644 --- a/packages/layers/src/heatmap/shaders/hexagon_vert.glsl +++ b/packages/layers/src/heatmap/shaders/hexagon_vert.glsl @@ -1,8 +1,8 @@ precision highp float; attribute vec3 a_Position; -attribute vec3 a_miter; -attribute float a_size; -attribute vec4 a_color; +attribute vec3 a_Pos; +attribute float a_Size; +attribute vec4 a_Color; uniform vec2 u_radius; uniform float u_coverage: 1.; uniform float u_angle: 0; @@ -10,9 +10,9 @@ uniform mat4 u_ModelMatrix; varying vec4 v_color; #pragma include "projection" void main() { - v_color = a_color; + 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)); + vec2 offset =(vec2(a_Position.xy * u_radius * u_coverage * rotationMatrix)); + vec4 project_pos = project_position(vec4(a_Pos.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 a373b611f2..f1b752992c 100644 --- a/packages/layers/src/index.ts +++ b/packages/layers/src/index.ts @@ -1,6 +1,6 @@ import { container, ILayerPlugin, TYPES } from '@l7/core'; import BaseLayer from './core/BaseLayer'; -// import HeatMapLayer from './heatmap'; +import HeatMapGridLayer from './heatmap/grid'; import LineLayer from './line/index'; import Point3dLayer from './point/extrude'; import PointImageLayer from './point/image'; @@ -8,7 +8,7 @@ import PointLayer from './point/index'; // import Point from './point/point'; import PolygonLayer from './polygon'; import Polygon3DLayer from './polygon/polygon3D'; -// import ImageLayer from './raster'; +import ImageLayer from './raster/image'; import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin'; import DataMappingPlugin from './plugins/DataMappingPlugin'; @@ -70,6 +70,8 @@ export { PointImageLayer, LineLayer, Polygon3DLayer, + ImageLayer, + HeatMapGridLayer, // Line, // ImageLayer, // HeatMapLayer, diff --git a/packages/layers/src/raster/image.ts b/packages/layers/src/raster/image.ts new file mode 100644 index 0000000000..74a6e84465 --- /dev/null +++ b/packages/layers/src/raster/image.ts @@ -0,0 +1,111 @@ +import { + AttributeType, + gl, + IEncodeFeature, + ILayer, + ILayerPlugin, + ILogService, + IStyleAttributeService, + ITexture2D, + lazyInject, + TYPES, +} from '@l7/core'; +import BaseLayer from '../core/BaseLayer'; +import { RasterImageTriangulation } from '../core/triangulation'; +import rasterImageFrag from './shaders/image_frag.glsl'; +import rasterImageVert from './shaders/image_vert.glsl'; +interface IPointLayerStyleOptions { + opacity: number; +} + +export default class ImageLayer extends BaseLayer { + public name: string = 'PointLayer'; + protected texture: ITexture2D; + + protected getConfigSchema() { + return { + properties: { + opacity: { + type: 'number', + minimum: 0, + maximum: 1, + }, + }, + }; + } + + protected renderModels() { + const { opacity } = this.getStyleOptions(); + if (this.texture) { + this.models.forEach((model) => + model.draw({ + uniforms: { + u_Opacity: opacity || 1, + u_texture: this.texture, + }, + }), + ); + } + + return this; + } + + protected buildModels() { + this.registerBuiltinAttributes(this); + const source = this.getSource(); + const { createTexture2D } = this.rendererService; + source.data.images.then((imageData: HTMLImageElement[]) => { + this.texture = createTexture2D({ + data: imageData[0], + width: imageData[0].width, + height: imageData[0].height, + }); + this.renderModels(); + }); + this.models = [ + this.buildLayerModel({ + moduleName: 'RasterImage', + vertexShader: rasterImageVert, + fragmentShader: rasterImageFrag, + triangulation: RasterImageTriangulation, + primitive: gl.TRIANGLES, + depth: { enable: false }, + blend: { + enable: true, + func: { + srcRGB: gl.SRC_ALPHA, + srcAlpha: 1, + dstRGB: gl.ONE_MINUS_SRC_ALPHA, + dstAlpha: 1, + }, + }, + }), + ]; + } + + private registerBuiltinAttributes(layer: ILayer) { + // point layer size; + layer.styleAttributeService.registerStyleAttribute({ + name: 'uv', + type: AttributeType.Attribute, + descriptor: { + name: 'a_Uv', + buffer: { + // give the WebGL driver a hint that this buffer may change + usage: gl.DYNAMIC_DRAW, + data: [], + type: gl.FLOAT, + }, + size: 2, + update: ( + feature: IEncodeFeature, + featureIdx: number, + vertex: number[], + attributeIdx: number, + ) => { + return [vertex[3], vertex[4]]; + }, + }, + }); + } +} diff --git a/packages/layers/src/raster/index.ts b/packages/layers/src/raster/index.ts index 93ebc02f8f..e69de29bb2 100644 --- a/packages/layers/src/raster/index.ts +++ b/packages/layers/src/raster/index.ts @@ -1,87 +0,0 @@ -// 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 8d3b5683b4..91901e16b2 100644 --- a/packages/layers/src/raster/shaders/image_frag.glsl +++ b/packages/layers/src/raster/shaders/image_frag.glsl @@ -5,4 +5,4 @@ varying vec2 v_texCoord; void main() { 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 f0b98f4a57..344574f90d 100644 --- a/packages/layers/src/raster/shaders/image_vert.glsl +++ b/packages/layers/src/raster/shaders/image_vert.glsl @@ -1,12 +1,12 @@ precision highp float; uniform mat4 u_ModelMatrix; attribute vec3 a_Position; -attribute vec2 a_uv; +attribute vec2 a_Uv; varying vec2 v_texCoord; #pragma include "projection" void main() { - v_texCoord = a_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.xy,0., 1.0)); -} \ No newline at end of file +} diff --git a/packages/maps/src/amap/index.ts b/packages/maps/src/amap/index.ts index dbd8aa1c30..c1bbd6cb4f 100644 --- a/packages/maps/src/amap/index.ts +++ b/packages/maps/src/amap/index.ts @@ -189,7 +189,7 @@ export default class AMapService implements IMapService { resolve(); }; - const url: string = `https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${AMAP_API_KEY}&plugin=Map3D&callback=onLoad`; + const url: string = `https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${AMAP_API_KEY}&plugin=Map3D&callback=onload`; this.$jsapi = document.createElement('script'); this.$jsapi.charset = 'utf-8'; this.$jsapi.src = url; diff --git a/stories/Layers/Layers.stories.tsx b/stories/Layers/Layers.stories.tsx index 77e98759b2..a019f80220 100644 --- a/stories/Layers/Layers.stories.tsx +++ b/stories/Layers/Layers.stories.tsx @@ -1,10 +1,12 @@ import { storiesOf } from '@storybook/react'; import * as React from 'react'; +import GridHeatMap from './components/heatMapgrid'; import LineLayer from './components/Line'; import PointDemo from './components/Point'; import Point3D from './components/Point3D'; import PointImage from './components/pointImage'; import Polygon3D from './components/polygon3D'; +import ImageLayerDemo from './components/rasterImage'; // @ts-ignore storiesOf('图层', module) @@ -12,4 +14,6 @@ storiesOf('图层', module) .add('3D点', () => ) .add('图片标注', () => ) .add('面3d图层', () => ) - .add('线图层', () => ); + .add('线图层', () => ) + .add('网格热力图', () => ) + .add('图片', () => ); diff --git a/stories/Layers/components/heatMapgrid.tsx b/stories/Layers/components/heatMapgrid.tsx new file mode 100644 index 0000000000..3029ab9110 --- /dev/null +++ b/stories/Layers/components/heatMapgrid.tsx @@ -0,0 +1,77 @@ +import { HeatMapGridLayer } 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 HeatMapGridLayer({}); + 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: 0.8, + angle: 0, + }) + .color('count', [ + '#002466', + '#105CB3', + '#2894E0', + '#CFF6FF', + '#FFF5B8', + '#FFAB5C', + '#F27049', + '#730D1C', + ]); + scene.addLayer(layer); + scene.render(); + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/Layers/components/rasterImage.tsx b/stories/Layers/components/rasterImage.tsx new file mode 100644 index 0000000000..295f0a34e1 --- /dev/null +++ b/stories/Layers/components/rasterImage.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); + console.log(layer); + scene.render(); + } + + public render() { + return ( +
+ ); + } +} diff --git a/stories/MapAdaptor/components/GridHeatmap.tsx b/stories/MapAdaptor/components/GridHeatmap.tsx index 9326fa5ad9..69ca5a9d42 100644 --- a/stories/MapAdaptor/components/GridHeatmap.tsx +++ b/stories/MapAdaptor/components/GridHeatmap.tsx @@ -43,8 +43,9 @@ export default class GridHeatMap extends React.Component { }) .shape('circle') .style({ - coverage: 1.2, + coverage: 0.5, angle: 0, + opacity: 1, }) .color('count', [ '#002466',