Merge pull request #50 from antvis/encode

feat(layers): add girdheatmap  add raster imagelayer
This commit is contained in:
@thinkinggis 2019-10-30 12:00:16 +08:00 committed by GitHub
commit f37587c6f5
14 changed files with 469 additions and 107 deletions

View File

@ -1,7 +1,7 @@
import { IEncodeFeature } from '@l7/core'; import { IEncodeFeature } from '@l7/core';
import { vec3 } from 'gl-matrix'; import { vec3 } from 'gl-matrix';
import getNormals from '../utils/polylineNormal'; import getNormals from '../utils/polylineNormal';
import extrudePolygon, { IExtrudeGeomety } from './shape/extrude'; import extrudePolygon, { fillPolygon, IExtrudeGeomety } from './shape/extrude';
import { import {
geometryShape, geometryShape,
IPosition, 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 { function getGeometry(shape: ShapeType3D): IExtrudeGeomety {
if (GeometryCache && GeometryCache[shape]) { if (GeometryCache && GeometryCache[shape]) {
return GeometryCache[shape]; return GeometryCache[shape];
@ -145,3 +206,14 @@ function checkIsClosed(points: number[][][]) {
const p2 = points[0][points[0].length - 1]; const p2 = points[0][points[0].length - 1];
return p1[0] === p2[0] && p1[1] === p2[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;
}

View File

@ -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<IHeatMapLayerStyleOptions> {
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];
},
},
});
}
}

View File

@ -1,7 +1,7 @@
precision highp float; precision highp float;
varying vec4 v_color; varying vec4 v_color;
uniform float u_opacity: 0.1; uniform float u_Opacity: 0.1;
void main() { void main() {
gl_FragColor = v_color; gl_FragColor = v_color;
gl_FragColor.a *= u_opacity; gl_FragColor.a *= u_Opacity;
} }

View File

@ -1,8 +1,8 @@
precision highp float; precision highp float;
attribute vec3 a_Position; attribute vec3 a_Position;
attribute vec3 a_miter; attribute vec3 a_Pos;
attribute float a_size; attribute float a_Size;
attribute vec4 a_color; attribute vec4 a_Color;
uniform vec2 u_radius; uniform vec2 u_radius;
uniform float u_coverage: 1.; uniform float u_coverage: 1.;
uniform float u_angle: 0; uniform float u_angle: 0;
@ -10,9 +10,9 @@ uniform mat4 u_ModelMatrix;
varying vec4 v_color; varying vec4 v_color;
#pragma include "projection" #pragma include "projection"
void main() { 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)); 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)); vec2 offset =(vec2(a_Position.xy * u_radius * u_coverage * rotationMatrix));
vec4 project_pos = project_position(vec4(a_Position.xy + offset, 0, 1.0)); 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)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0));
} }

View File

@ -1,6 +1,6 @@
import { container, ILayerPlugin, TYPES } from '@l7/core'; import { container, ILayerPlugin, TYPES } from '@l7/core';
import BaseLayer from './core/BaseLayer'; import BaseLayer from './core/BaseLayer';
// import HeatMapLayer from './heatmap'; import HeatMapGridLayer from './heatmap/grid';
import LineLayer from './line/index'; import LineLayer from './line/index';
import Point3dLayer from './point/extrude'; import Point3dLayer from './point/extrude';
import PointImageLayer from './point/image'; import PointImageLayer from './point/image';
@ -8,7 +8,7 @@ import PointLayer from './point/index';
// import Point from './point/point'; // import Point from './point/point';
import PolygonLayer from './polygon'; import PolygonLayer from './polygon';
import Polygon3DLayer from './polygon/polygon3D'; import Polygon3DLayer from './polygon/polygon3D';
// import ImageLayer from './raster'; import ImageLayer from './raster/image';
import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin'; import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin';
import DataMappingPlugin from './plugins/DataMappingPlugin'; import DataMappingPlugin from './plugins/DataMappingPlugin';
@ -70,6 +70,8 @@ export {
PointImageLayer, PointImageLayer,
LineLayer, LineLayer,
Polygon3DLayer, Polygon3DLayer,
ImageLayer,
HeatMapGridLayer,
// Line, // Line,
// ImageLayer, // ImageLayer,
// HeatMapLayer, // HeatMapLayer,

View File

@ -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<IPointLayerStyleOptions> {
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]];
},
},
});
}
}

View File

@ -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,
// }),
// );
// });
// }
// }

View File

@ -5,4 +5,4 @@ varying vec2 v_texCoord;
void main() { void main() {
vec4 color = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y)); vec4 color = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y));
gl_FragColor = color; gl_FragColor = color;
} }

View File

@ -1,12 +1,12 @@
precision highp float; precision highp float;
uniform mat4 u_ModelMatrix; uniform mat4 u_ModelMatrix;
attribute vec3 a_Position; attribute vec3 a_Position;
attribute vec2 a_uv; attribute vec2 a_Uv;
varying vec2 v_texCoord; varying vec2 v_texCoord;
#pragma include "projection" #pragma include "projection"
void main() { void main() {
v_texCoord = a_uv; v_texCoord = a_Uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0)); vec4 project_pos = project_position(vec4(a_Position, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
} }

View File

@ -189,7 +189,7 @@ export default class AMapService implements IMapService {
resolve(); 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 = document.createElement('script');
this.$jsapi.charset = 'utf-8'; this.$jsapi.charset = 'utf-8';
this.$jsapi.src = url; this.$jsapi.src = url;

View File

@ -1,10 +1,12 @@
import { storiesOf } from '@storybook/react'; import { storiesOf } from '@storybook/react';
import * as React from 'react'; import * as React from 'react';
import GridHeatMap from './components/heatMapgrid';
import LineLayer from './components/Line'; import LineLayer from './components/Line';
import PointDemo from './components/Point'; import PointDemo from './components/Point';
import Point3D from './components/Point3D'; import Point3D from './components/Point3D';
import PointImage from './components/pointImage'; import PointImage from './components/pointImage';
import Polygon3D from './components/polygon3D'; import Polygon3D from './components/polygon3D';
import ImageLayerDemo from './components/rasterImage';
// @ts-ignore // @ts-ignore
storiesOf('图层', module) storiesOf('图层', module)
@ -12,4 +14,6 @@ storiesOf('图层', module)
.add('3D点', () => <Point3D />) .add('3D点', () => <Point3D />)
.add('图片标注', () => <PointImage />) .add('图片标注', () => <PointImage />)
.add('面3d图层', () => <Polygon3D />) .add('面3d图层', () => <Polygon3D />)
.add('线图层', () => <LineLayer />); .add('线图层', () => <LineLayer />)
.add('网格热力图', () => <GridHeatMap />)
.add('图片', () => <ImageLayerDemo />);

View File

@ -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 (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -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 (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -43,8 +43,9 @@ export default class GridHeatMap extends React.Component {
}) })
.shape('circle') .shape('circle')
.style({ .style({
coverage: 1.2, coverage: 0.5,
angle: 0, angle: 0,
opacity: 1,
}) })
.color('count', [ .color('count', [
'#002466', '#002466',