mirror of https://gitee.com/antv-l7/antv-l7
feat: 图层新增 creatModelData 与 updateModelData 方法 (#1064)
* feat: 支持提前计算 attribute/elements 并更新 models * style: lint style * feat: 支持 lineLayer 的 models 更新 attributes/elements * style: lint style * chore: update version 2.8.30 -> 2.8.31 * feat: 完善 geometry 加载地形的 lod * style: lint style * feat: rename func updateMudelsData to updateModelData * feat: pointLayer simple 支持 initModelData/updateModelData * feat: 增加顶点更新 demo * style: lint style * feat: reset func name => initModelData => createModelData
This commit is contained in:
parent
90f92f069b
commit
aa2e9540de
|
@ -19,3 +19,5 @@ window.react = require('react');
|
||||||
window.popmotion = require('popmotion');
|
window.popmotion = require('popmotion');
|
||||||
window.reactDom = require('react-dom');
|
window.reactDom = require('react-dom');
|
||||||
window.antd = require('antd');
|
window.antd = require('antd');
|
||||||
|
window.d3Dsv = require('d3-dsv');
|
||||||
|
window.materialUI = require('@material-ui')
|
||||||
|
|
|
@ -88,6 +88,8 @@
|
||||||
"husky": "^3.0.9",
|
"husky": "^3.0.9",
|
||||||
"jest": "^24.9.0",
|
"jest": "^24.9.0",
|
||||||
"jest-electron": "^0.1.11",
|
"jest-electron": "^0.1.11",
|
||||||
|
"@material-ui/core": "^4.10.2",
|
||||||
|
"@material-ui/icons": "^4.9.1",
|
||||||
"jest-styled-components": "^6.2.1",
|
"jest-styled-components": "^6.2.1",
|
||||||
"lerna": "^3.16.4",
|
"lerna": "^3.16.4",
|
||||||
"lint-staged": "^9.2.4",
|
"lint-staged": "^9.2.4",
|
||||||
|
|
|
@ -78,6 +78,7 @@ export interface ILayerModel {
|
||||||
|
|
||||||
// earth mode
|
// earth mode
|
||||||
setEarthTime?(time: number): void;
|
setEarthTime?(time: number): void;
|
||||||
|
createModelData?(options?: any): any;
|
||||||
}
|
}
|
||||||
export interface IModelUniform {
|
export interface IModelUniform {
|
||||||
[key: string]: IUniform;
|
[key: string]: IUniform;
|
||||||
|
@ -110,6 +111,11 @@ export interface ILegendClassificaItem {
|
||||||
// 图层图例
|
// 图层图例
|
||||||
export type LegendItems = ILegendSegmentItem[] | ILegendClassificaItem[];
|
export type LegendItems = ILegendSegmentItem[] | ILegendClassificaItem[];
|
||||||
|
|
||||||
|
export interface IAttrubuteAndElements {
|
||||||
|
attributes: any;
|
||||||
|
elements: any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ILayer {
|
export interface ILayer {
|
||||||
id: string; // 一个场景中同一类型 Layer 可能存在多个
|
id: string; // 一个场景中同一类型 Layer 可能存在多个
|
||||||
type: string; // 代表 Layer 的类型
|
type: string; // 代表 Layer 的类型
|
||||||
|
@ -150,6 +156,7 @@ export interface ILayer {
|
||||||
// 初始化 layer 的时候指定 layer type 类型()兼容空数据的情况
|
// 初始化 layer 的时候指定 layer type 类型()兼容空数据的情况
|
||||||
layerType?: string | undefined;
|
layerType?: string | undefined;
|
||||||
isLayerGroup: boolean;
|
isLayerGroup: boolean;
|
||||||
|
triangulation?: Triangulation | undefined;
|
||||||
/**
|
/**
|
||||||
* threejs 适配兼容相关的方法
|
* threejs 适配兼容相关的方法
|
||||||
* @param lnglat
|
* @param lnglat
|
||||||
|
@ -161,6 +168,7 @@ export interface ILayer {
|
||||||
threeRenderService?: any;
|
threeRenderService?: any;
|
||||||
|
|
||||||
getShaderPickStat: () => boolean;
|
getShaderPickStat: () => boolean;
|
||||||
|
updateModelData(data: IAttrubuteAndElements): void;
|
||||||
|
|
||||||
addMaskLayer(maskLayer: ILayer): void;
|
addMaskLayer(maskLayer: ILayer): void;
|
||||||
removeMaskLayer(maskLayer: ILayer): void;
|
removeMaskLayer(maskLayer: ILayer): void;
|
||||||
|
|
|
@ -244,6 +244,10 @@ export interface IModelDrawOptions {
|
||||||
*/
|
*/
|
||||||
export interface IModel {
|
export interface IModel {
|
||||||
updateAttributes(attributes: { [key: string]: IAttribute }): void;
|
updateAttributes(attributes: { [key: string]: IAttribute }): void;
|
||||||
|
updateAttributesAndElements(
|
||||||
|
attributes: { [key: string]: IAttribute },
|
||||||
|
elements: IElements,
|
||||||
|
): void;
|
||||||
addUniforms(uniforms: { [key: string]: IUniform }): void;
|
addUniforms(uniforms: { [key: string]: IUniform }): void;
|
||||||
draw(options: IModelDrawOptions, pick?: boolean): void;
|
draw(options: IModelDrawOptions, pick?: boolean): void;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
AttributeType,
|
AttributeType,
|
||||||
gl,
|
gl,
|
||||||
|
IAttrubuteAndElements,
|
||||||
IEncodeFeature,
|
IEncodeFeature,
|
||||||
IModelUniform,
|
IModelUniform,
|
||||||
ITexture2D,
|
ITexture2D,
|
||||||
|
@ -15,9 +16,9 @@ import planeVert from '../shaders/plane_vert.glsl';
|
||||||
|
|
||||||
export default class PlaneModel extends BaseModel {
|
export default class PlaneModel extends BaseModel {
|
||||||
protected texture: ITexture2D;
|
protected texture: ITexture2D;
|
||||||
|
protected terrainImage: HTMLImageElement;
|
||||||
|
protected terrainImageLoaded: boolean = false;
|
||||||
protected mapTexture: string | undefined;
|
protected mapTexture: string | undefined;
|
||||||
protected positions: number[];
|
|
||||||
protected indices: number[];
|
|
||||||
|
|
||||||
public initPlane(
|
public initPlane(
|
||||||
width = 1,
|
width = 1,
|
||||||
|
@ -88,7 +89,6 @@ export default class PlaneModel extends BaseModel {
|
||||||
center = [120, 30],
|
center = [120, 30],
|
||||||
terrainTexture,
|
terrainTexture,
|
||||||
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||||
|
|
||||||
const { indices, positions } = this.initPlane(
|
const { indices, positions } = this.initPlane(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -96,12 +96,9 @@ export default class PlaneModel extends BaseModel {
|
||||||
heightSegments,
|
heightSegments,
|
||||||
...center,
|
...center,
|
||||||
);
|
);
|
||||||
this.positions = positions;
|
|
||||||
this.indices = indices;
|
|
||||||
|
|
||||||
if (terrainTexture) {
|
if (terrainTexture) {
|
||||||
// 存在地形贴图的时候会根据地形贴图对顶点进行偏移
|
// 存在地形贴图的时候会根据地形贴图对顶点进行偏移
|
||||||
this.loadTerrainTexture();
|
this.loadTerrainTexture(positions, indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -110,13 +107,6 @@ export default class PlaneModel extends BaseModel {
|
||||||
size: 5,
|
size: 5,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
public planeGeometryUpdateTriangulation = () => {
|
|
||||||
return {
|
|
||||||
vertices: this.positions,
|
|
||||||
indices: this.indices,
|
|
||||||
size: 5,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
public getUninforms(): IModelUniform {
|
public getUninforms(): IModelUniform {
|
||||||
const {
|
const {
|
||||||
|
@ -135,14 +125,12 @@ export default class PlaneModel extends BaseModel {
|
||||||
u_mapFlag: mapTexture ? 1 : 0,
|
u_mapFlag: mapTexture ? 1 : 0,
|
||||||
u_terrainClipHeight: terrainTexture ? terrainClipHeight : -1,
|
u_terrainClipHeight: terrainTexture ? terrainClipHeight : -1,
|
||||||
u_texture: this.texture,
|
u_texture: this.texture,
|
||||||
// u_ModelMatrix: mat4.translate(mat4.create(), mat4.create(), [1, 0, 0])
|
|
||||||
// u_ModelMatrix: mat4.rotateZ(mat4.create(), mat4.create(), 10)
|
|
||||||
// u_ModelMatrix: mat4.rotateZ(mat4.create(), mat4.create(), 10)
|
|
||||||
// u_ModelMatrix: this.rotateZ()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public clearModels(): void {
|
public clearModels(): void {
|
||||||
|
// @ts-ignore
|
||||||
|
this.terrainImage = null;
|
||||||
this.texture?.destroy();
|
this.texture?.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +149,6 @@ export default class PlaneModel extends BaseModel {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateTexture(mapTexture);
|
this.updateTexture(mapTexture);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
this.layer.buildLayerModel({
|
this.layer.buildLayerModel({
|
||||||
moduleName: 'geometry_plane',
|
moduleName: 'geometry_plane',
|
||||||
|
@ -169,6 +156,7 @@ export default class PlaneModel extends BaseModel {
|
||||||
fragmentShader: planeFrag,
|
fragmentShader: planeFrag,
|
||||||
triangulation: this.planeGeometryTriangulation,
|
triangulation: this.planeGeometryTriangulation,
|
||||||
primitive: gl.TRIANGLES,
|
primitive: gl.TRIANGLES,
|
||||||
|
// primitive: gl.LINES,
|
||||||
depth: { enable: true },
|
depth: { enable: true },
|
||||||
blend: this.getBlend(),
|
blend: this.getBlend(),
|
||||||
stencil: getMask(mask, maskInside),
|
stencil: getMask(mask, maskInside),
|
||||||
|
@ -180,90 +168,41 @@ export default class PlaneModel extends BaseModel {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getImageData(img: HTMLImageElement) {
|
|
||||||
const canvas: HTMLCanvasElement = document.createElement('canvas');
|
|
||||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
||||||
const { width, height } = img;
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
|
|
||||||
ctx.drawImage(img, 0, 0, width, height);
|
|
||||||
const imageData = ctx.getImageData(0, 0, width, height);
|
|
||||||
|
|
||||||
return imageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* load terrain texture & offset attribute z
|
|
||||||
*/
|
|
||||||
public loadTerrainTexture(): void {
|
|
||||||
const {
|
|
||||||
mask = false,
|
|
||||||
maskInside = true,
|
|
||||||
widthSegments = 1,
|
|
||||||
heightSegments = 1,
|
|
||||||
terrainTexture,
|
|
||||||
rgb2height = (r: number, g: number, b: number) => r + g + b,
|
|
||||||
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
|
||||||
const terrainImage = new Image();
|
|
||||||
terrainImage.crossOrigin = 'anonymous';
|
|
||||||
terrainImage.onload = () => {
|
|
||||||
const imgWidth = terrainImage.width;
|
|
||||||
const imgHeight = terrainImage.height;
|
|
||||||
|
|
||||||
const imageData = this.getImageData(terrainImage).data;
|
|
||||||
|
|
||||||
const gridX = Math.floor(widthSegments);
|
|
||||||
const gridY = Math.floor(heightSegments);
|
|
||||||
|
|
||||||
const gridX1 = gridX + 1;
|
|
||||||
const gridY1 = gridY + 1;
|
|
||||||
|
|
||||||
const widthStep = imgWidth / gridX;
|
|
||||||
const heihgtStep = imgHeight / gridY;
|
|
||||||
|
|
||||||
for (let iy = 0; iy < gridY1; iy++) {
|
|
||||||
const imgIndexY = Math.floor(iy * heihgtStep);
|
|
||||||
const imgLen = imgIndexY * imgWidth;
|
|
||||||
|
|
||||||
for (let ix = 0; ix < gridX1; ix++) {
|
|
||||||
const imgIndexX = Math.floor(ix * widthStep);
|
|
||||||
const imgDataIndex = (imgLen + imgIndexX) * 4;
|
|
||||||
|
|
||||||
const r = imageData[imgDataIndex];
|
|
||||||
const g = imageData[imgDataIndex + 1];
|
|
||||||
const b = imageData[imgDataIndex + 2];
|
|
||||||
|
|
||||||
const z = (iy * gridX1 + ix) * 5 + 2;
|
|
||||||
this.positions[z] = rgb2height(r, g, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.layer.models = [
|
|
||||||
this.layer.buildLayerModel({
|
|
||||||
moduleName: 'geometry_plane',
|
|
||||||
vertexShader: planeVert,
|
|
||||||
fragmentShader: planeFrag,
|
|
||||||
triangulation: this.planeGeometryUpdateTriangulation,
|
|
||||||
primitive: gl.TRIANGLES,
|
|
||||||
depth: { enable: true },
|
|
||||||
blend: this.getBlend(),
|
|
||||||
stencil: getMask(mask, maskInside),
|
|
||||||
cull: {
|
|
||||||
enable: true,
|
|
||||||
face: gl.BACK,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
this.layerService.renderLayers();
|
|
||||||
};
|
|
||||||
terrainImage.src = terrainTexture as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public buildModels() {
|
public buildModels() {
|
||||||
return this.initModels();
|
return this.initModels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public createModelData(options?: any) {
|
||||||
|
if (options) {
|
||||||
|
const {
|
||||||
|
widthSegments: oldwidthSegments,
|
||||||
|
heightSegments: oldheightSegments,
|
||||||
|
width: oldwidth,
|
||||||
|
height: oldheight,
|
||||||
|
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||||
|
const {
|
||||||
|
widthSegments,
|
||||||
|
heightSegments,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
} = options as IGeometryLayerStyleOptions;
|
||||||
|
this.layer.style({
|
||||||
|
widthSegments:
|
||||||
|
widthSegments !== undefined ? widthSegments : oldwidthSegments,
|
||||||
|
heightSegments:
|
||||||
|
heightSegments !== undefined ? heightSegments : oldheightSegments,
|
||||||
|
width: width !== undefined ? width : oldwidth,
|
||||||
|
height: height !== undefined ? height : oldheight,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const oldFeatures = this.layer.getEncodedData();
|
||||||
|
const res = this.styleAttributeService.createAttributesAndIndices(
|
||||||
|
oldFeatures,
|
||||||
|
this.planeGeometryTriangulation,
|
||||||
|
);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public updateTexture(mapTexture: string | undefined): void {
|
public updateTexture(mapTexture: string | undefined): void {
|
||||||
const { createTexture2D } = this.rendererService;
|
const { createTexture2D } = this.rendererService;
|
||||||
|
|
||||||
|
@ -290,6 +229,127 @@ export default class PlaneModel extends BaseModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getImageData(img: HTMLImageElement) {
|
||||||
|
const canvas: HTMLCanvasElement = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
|
const { width, height } = img;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
const imageData = ctx.getImageData(0, 0, width, height);
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected translateVertex(
|
||||||
|
positions: number[],
|
||||||
|
indices: number[],
|
||||||
|
image: HTMLImageElement,
|
||||||
|
widthSegments: number,
|
||||||
|
heightSegments: number,
|
||||||
|
rgb2height: (r: number, g: number, b: number) => number,
|
||||||
|
) {
|
||||||
|
const imgWidth = image.width;
|
||||||
|
const imgHeight = image.height;
|
||||||
|
const imageData = this.getImageData(image).data;
|
||||||
|
|
||||||
|
const gridX = Math.floor(widthSegments);
|
||||||
|
const gridY = Math.floor(heightSegments);
|
||||||
|
|
||||||
|
const gridX1 = gridX + 1;
|
||||||
|
const gridY1 = gridY + 1;
|
||||||
|
|
||||||
|
const widthStep = imgWidth / gridX;
|
||||||
|
const heihgtStep = imgHeight / gridY;
|
||||||
|
|
||||||
|
for (let iy = 0; iy < gridY1; iy++) {
|
||||||
|
const imgIndexY = Math.floor(iy * heihgtStep);
|
||||||
|
const imgLen = imgIndexY * imgWidth;
|
||||||
|
|
||||||
|
for (let ix = 0; ix < gridX1; ix++) {
|
||||||
|
const imgIndexX = Math.floor(ix * widthStep);
|
||||||
|
const imgDataIndex = (imgLen + imgIndexX) * 4;
|
||||||
|
|
||||||
|
const r = imageData[imgDataIndex];
|
||||||
|
const g = imageData[imgDataIndex + 1];
|
||||||
|
const b = imageData[imgDataIndex + 2];
|
||||||
|
|
||||||
|
const z = (iy * gridX1 + ix) * 5 + 2;
|
||||||
|
positions[z] = rgb2height(r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldFeatures = this.layer.getEncodedData();
|
||||||
|
const modelData = this.styleAttributeService.createAttributesAndIndices(
|
||||||
|
oldFeatures,
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
vertices: positions,
|
||||||
|
indices,
|
||||||
|
size: 5,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
this.layer.updateModelData(modelData as IAttrubuteAndElements);
|
||||||
|
this.layerService.renderLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load terrain texture & offset attribute z
|
||||||
|
*/
|
||||||
|
protected loadTerrainTexture(positions: number[], indices: number[]) {
|
||||||
|
const {
|
||||||
|
widthSegments = 1,
|
||||||
|
heightSegments = 1,
|
||||||
|
terrainTexture,
|
||||||
|
rgb2height = (r: number, g: number, b: number) => r + g + b,
|
||||||
|
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
|
||||||
|
if (this.terrainImage) {
|
||||||
|
// 若当前已经存在 image,直接进行偏移计算(LOD)
|
||||||
|
if (this.terrainImageLoaded) {
|
||||||
|
this.translateVertex(
|
||||||
|
positions,
|
||||||
|
indices,
|
||||||
|
this.terrainImage,
|
||||||
|
widthSegments,
|
||||||
|
heightSegments,
|
||||||
|
rgb2height,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.terrainImage.onload = () => {
|
||||||
|
this.translateVertex(
|
||||||
|
positions,
|
||||||
|
indices,
|
||||||
|
this.terrainImage,
|
||||||
|
widthSegments,
|
||||||
|
heightSegments,
|
||||||
|
rgb2height,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 加载地形贴图、根据地形贴图对 planeGeometry 进行偏移
|
||||||
|
const terrainImage = new Image();
|
||||||
|
this.terrainImage = terrainImage;
|
||||||
|
terrainImage.crossOrigin = 'anonymous';
|
||||||
|
terrainImage.onload = () => {
|
||||||
|
this.terrainImageLoaded = true;
|
||||||
|
// 图片加载完,触发事件,可以进行地形图的顶点计算存储
|
||||||
|
setTimeout(() => this.layer.emit('terrainImageLoaded', null));
|
||||||
|
this.translateVertex(
|
||||||
|
positions,
|
||||||
|
indices,
|
||||||
|
terrainImage,
|
||||||
|
widthSegments,
|
||||||
|
heightSegments,
|
||||||
|
rgb2height,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
terrainImage.src = terrainTexture as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected getConfigSchema() {
|
protected getConfigSchema() {
|
||||||
return {
|
return {
|
||||||
properties: {
|
properties: {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
gl,
|
gl,
|
||||||
IActiveOption,
|
IActiveOption,
|
||||||
IAnimateOption,
|
IAnimateOption,
|
||||||
|
IAttrubuteAndElements,
|
||||||
ICameraService,
|
ICameraService,
|
||||||
ICoordinateSystemService,
|
ICoordinateSystemService,
|
||||||
IDataState,
|
IDataState,
|
||||||
|
@ -44,6 +45,7 @@ import {
|
||||||
ScaleTypes,
|
ScaleTypes,
|
||||||
StyleAttributeField,
|
StyleAttributeField,
|
||||||
StyleAttributeOption,
|
StyleAttributeOption,
|
||||||
|
Triangulation,
|
||||||
TYPES,
|
TYPES,
|
||||||
} from '@antv/l7-core';
|
} from '@antv/l7-core';
|
||||||
import Source from '@antv/l7-source';
|
import Source from '@antv/l7-source';
|
||||||
|
@ -53,6 +55,7 @@ import { Container } from 'inversify';
|
||||||
import { isEqual, isFunction, isObject, isUndefined } from 'lodash';
|
import { isEqual, isFunction, isObject, isUndefined } from 'lodash';
|
||||||
import { BlendTypes } from '../utils/blend';
|
import { BlendTypes } from '../utils/blend';
|
||||||
import { handleStyleDataMapping } from '../utils/dataMappingStyle';
|
import { handleStyleDataMapping } from '../utils/dataMappingStyle';
|
||||||
|
import { calculateData } from '../utils/layerData';
|
||||||
import {
|
import {
|
||||||
createMultiPassRenderer,
|
createMultiPassRenderer,
|
||||||
normalizePasses,
|
normalizePasses,
|
||||||
|
@ -81,6 +84,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
public clusterZoom: number = 0; // 聚合等级标记
|
public clusterZoom: number = 0; // 聚合等级标记
|
||||||
public layerType?: string | undefined;
|
public layerType?: string | undefined;
|
||||||
public isLayerGroup: boolean = false;
|
public isLayerGroup: boolean = false;
|
||||||
|
public triangulation?: Triangulation | undefined;
|
||||||
|
|
||||||
public dataState: IDataState = {
|
public dataState: IDataState = {
|
||||||
dataSourceNeedUpdate: false,
|
dataSourceNeedUpdate: false,
|
||||||
|
@ -399,8 +403,55 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
target: this,
|
target: this,
|
||||||
type: 'add',
|
type: 'add',
|
||||||
});
|
});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateModelData(data: IAttrubuteAndElements) {
|
||||||
|
if (data.attributes && data.elements) {
|
||||||
|
this.models.map((m) => {
|
||||||
|
m.updateAttributesAndElements(data.attributes, data.elements);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn('data error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public createModelData(data: any, option?: ISourceCFG) {
|
||||||
|
if (this.layerModel.createModelData) {
|
||||||
|
// 在某些特殊图层中单独构建 attribute & elements
|
||||||
|
return this.layerModel.createModelData(option);
|
||||||
|
}
|
||||||
|
const calEncodeData = this.calculateEncodeData(data, option);
|
||||||
|
const triangulation = this.triangulation;
|
||||||
|
if (calEncodeData && triangulation) {
|
||||||
|
return this.styleAttributeService.createAttributesAndIndices(
|
||||||
|
calEncodeData,
|
||||||
|
triangulation,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
attributes: undefined,
|
||||||
|
elements: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public calculateEncodeData(data: any, option?: ISourceCFG) {
|
||||||
|
if (this.inited) {
|
||||||
|
return calculateData(
|
||||||
|
this,
|
||||||
|
this.fontService,
|
||||||
|
this.mapService,
|
||||||
|
this.styleAttributeService,
|
||||||
|
data,
|
||||||
|
option,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.warn('layer not inited!');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Model初始化前需要更新Model样式
|
* Model初始化前需要更新Model样式
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -165,6 +165,7 @@ export default class LineModel extends BaseModel {
|
||||||
depth = false,
|
depth = false,
|
||||||
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
|
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
|
||||||
const { frag, vert, type } = this.getShaders();
|
const { frag, vert, type } = this.getShaders();
|
||||||
|
this.layer.triangulation = LineTriangulation;
|
||||||
return [
|
return [
|
||||||
this.layer.buildLayerModel({
|
this.layer.buildLayerModel({
|
||||||
moduleName: 'line_' + type,
|
moduleName: 'line_' + type,
|
||||||
|
|
|
@ -201,6 +201,9 @@ export default class FillModel extends BaseModel {
|
||||||
|
|
||||||
// TODO: 判断当前的点图层的模型是普通地图模式还是地球模式
|
// TODO: 判断当前的点图层的模型是普通地图模式还是地球模式
|
||||||
const isGlobel = this.mapService.version === 'GLOBEL';
|
const isGlobel = this.mapService.version === 'GLOBEL';
|
||||||
|
this.layer.triangulation = isGlobel
|
||||||
|
? GlobelPointFillTriangulation
|
||||||
|
: PointFillTriangulation;
|
||||||
return [
|
return [
|
||||||
this.layer.buildLayerModel({
|
this.layer.buildLayerModel({
|
||||||
moduleName: 'pointfill_' + type,
|
moduleName: 'pointfill_' + type,
|
||||||
|
|
|
@ -103,6 +103,7 @@ export default class SimplePointModel extends BaseModel {
|
||||||
mask = false,
|
mask = false,
|
||||||
maskInside = true,
|
maskInside = true,
|
||||||
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
|
||||||
|
this.layer.triangulation = PointTriangulation;
|
||||||
return [
|
return [
|
||||||
this.layer.buildLayerModel({
|
this.layer.buildLayerModel({
|
||||||
moduleName: 'simplepoint',
|
moduleName: 'simplepoint',
|
||||||
|
|
|
@ -77,6 +77,9 @@ export default class FillModel extends BaseModel {
|
||||||
mask = false,
|
mask = false,
|
||||||
maskInside = true,
|
maskInside = true,
|
||||||
} = this.layer.getLayerConfig() as IPolygonLayerStyleOptions;
|
} = this.layer.getLayerConfig() as IPolygonLayerStyleOptions;
|
||||||
|
this.layer.triangulation = opacityLinear.enable
|
||||||
|
? polygonTriangulationWithCenter
|
||||||
|
: polygonTriangulation;
|
||||||
return [
|
return [
|
||||||
this.layer.buildLayerModel({
|
this.layer.buildLayerModel({
|
||||||
moduleName: 'polygon',
|
moduleName: 'polygon',
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
import {
|
||||||
|
IEncodeFeature,
|
||||||
|
IFontService,
|
||||||
|
ILayer,
|
||||||
|
IMapService,
|
||||||
|
IParseDataItem,
|
||||||
|
ISourceCFG,
|
||||||
|
IStyleAttribute,
|
||||||
|
IStyleAttributeService,
|
||||||
|
Position,
|
||||||
|
} from '@antv/l7-core';
|
||||||
|
import { Version } from '@antv/l7-maps';
|
||||||
|
import Source from '@antv/l7-source';
|
||||||
|
import { isColor, normalize, rgb2arr } from '@antv/l7-utils';
|
||||||
|
import { ILineLayerStyleOptions } from '../core/interface';
|
||||||
|
|
||||||
|
function getArrowPoints(p1: Position, p2: Position) {
|
||||||
|
const dir = [p2[0] - p1[0], p2[1] - p1[1]];
|
||||||
|
const normalizeDir = normalize(dir);
|
||||||
|
const arrowPoint = [
|
||||||
|
p1[0] + normalizeDir[0] * 0.0001,
|
||||||
|
p1[1] + normalizeDir[1] * 0.0001,
|
||||||
|
];
|
||||||
|
return arrowPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustData2Amap2Coordinates(
|
||||||
|
mappedData: IEncodeFeature[],
|
||||||
|
mapService: IMapService,
|
||||||
|
) {
|
||||||
|
// 根据地图的类型判断是否需要对点位数据进行处理, 若是高德2.0则需要对坐标进行相对偏移
|
||||||
|
if (mappedData.length > 0 && mapService.version === Version['GAODE2.x']) {
|
||||||
|
if (typeof mappedData[0].coordinates[0] === 'number') {
|
||||||
|
// 单个的点数据
|
||||||
|
// @ts-ignore
|
||||||
|
mappedData
|
||||||
|
// TODO: 避免经纬度被重复计算导致坐标位置偏移
|
||||||
|
.filter((d) => !d.originCoordinates)
|
||||||
|
.map((d) => {
|
||||||
|
d.version = Version['GAODE2.x'];
|
||||||
|
// @ts-ignore
|
||||||
|
d.originCoordinates = cloneDeep(d.coordinates); // 为了兼容高德1.x 需要保存一份原始的经纬度坐标数据(许多上层逻辑依赖经纬度数据)
|
||||||
|
// @ts-ignore
|
||||||
|
d.coordinates = this.mapService.lngLatToCoord(d.coordinates);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 连续的线、面数据
|
||||||
|
// @ts-ignore
|
||||||
|
mappedData
|
||||||
|
// TODO: 避免经纬度被重复计算导致坐标位置偏移
|
||||||
|
.filter((d) => !d.originCoordinates)
|
||||||
|
.map((d) => {
|
||||||
|
d.version = Version['GAODE2.x'];
|
||||||
|
// @ts-ignore
|
||||||
|
d.originCoordinates = cloneDeep(d.coordinates); // 为了兼容高德1.x 需要保存一份原始的经纬度坐标数据(许多上层逻辑依赖经纬度数据)
|
||||||
|
// @ts-ignore
|
||||||
|
d.coordinates = this.mapService.lngLatToCoords(d.coordinates);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function adjustData2SimpleCoordinates(
|
||||||
|
mappedData: IEncodeFeature[],
|
||||||
|
mapService: IMapService,
|
||||||
|
) {
|
||||||
|
if (mappedData.length > 0 && mapService.version === Version.SIMPLE) {
|
||||||
|
mappedData.map((d) => {
|
||||||
|
if (!d.simpleCoordinate) {
|
||||||
|
d.coordinates = unProjectCoordinates(d.coordinates, mapService);
|
||||||
|
d.simpleCoordinate = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unProjectCoordinates(coordinates: any, mapService: IMapService) {
|
||||||
|
if (typeof coordinates[0] === 'number') {
|
||||||
|
return mapService.simpleMapCoord.unproject(coordinates as [number, number]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coordinates[0] && coordinates[0][0] instanceof Array) {
|
||||||
|
// @ts-ignore
|
||||||
|
const coords = [];
|
||||||
|
coordinates.map((coord: any) => {
|
||||||
|
// @ts-ignore
|
||||||
|
const c1 = [];
|
||||||
|
coord.map((co: any) => {
|
||||||
|
c1.push(mapService.simpleMapCoord.unproject(co as [number, number]));
|
||||||
|
});
|
||||||
|
// @ts-ignore
|
||||||
|
coords.push(c1);
|
||||||
|
});
|
||||||
|
// @ts-ignore
|
||||||
|
return coords;
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
const coords = [];
|
||||||
|
// @ts-ignore
|
||||||
|
coordinates.map((coord) => {
|
||||||
|
coords.push(
|
||||||
|
mapService.simpleMapCoord.unproject(coord as [number, number]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// @ts-ignore
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyAttributeMapping(
|
||||||
|
attribute: IStyleAttribute,
|
||||||
|
record: { [key: string]: unknown },
|
||||||
|
minimumColor?: string,
|
||||||
|
) {
|
||||||
|
if (!attribute.scale) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const scalers = attribute?.scale?.scalers || [];
|
||||||
|
const params: unknown[] = [];
|
||||||
|
|
||||||
|
scalers.forEach(({ field }) => {
|
||||||
|
if (record.hasOwnProperty(field) || attribute.scale?.type === 'variable') {
|
||||||
|
// TODO:多字段,常量
|
||||||
|
params.push(record[field]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const mappingResult = attribute.mapping ? attribute.mapping(params) : [];
|
||||||
|
if (attribute.name === 'color' && !isColor(mappingResult[0])) {
|
||||||
|
return [minimumColor];
|
||||||
|
}
|
||||||
|
return mappingResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapping(
|
||||||
|
attributes: IStyleAttribute[],
|
||||||
|
data: IParseDataItem[],
|
||||||
|
fontService: IFontService,
|
||||||
|
mapService: IMapService,
|
||||||
|
minimumColor?: string,
|
||||||
|
layer?: ILayer,
|
||||||
|
): IEncodeFeature[] {
|
||||||
|
const {
|
||||||
|
arrow = {
|
||||||
|
enable: false,
|
||||||
|
},
|
||||||
|
} = layer?.getLayerConfig() as ILineLayerStyleOptions;
|
||||||
|
const mappedData = data.map((record: IParseDataItem) => {
|
||||||
|
const encodeRecord: IEncodeFeature = {
|
||||||
|
id: record._id,
|
||||||
|
coordinates: record.coordinates,
|
||||||
|
};
|
||||||
|
|
||||||
|
attributes
|
||||||
|
.filter((attribute) => attribute.scale !== undefined)
|
||||||
|
.forEach((attribute: IStyleAttribute) => {
|
||||||
|
let values = applyAttributeMapping(attribute, record, minimumColor);
|
||||||
|
|
||||||
|
attribute.needRemapping = false;
|
||||||
|
|
||||||
|
// TODO: 支持每个属性配置 postprocess
|
||||||
|
if (attribute.name === 'color') {
|
||||||
|
values = values.map((c: unknown) => {
|
||||||
|
return rgb2arr(c as string);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
encodeRecord[attribute.name] =
|
||||||
|
Array.isArray(values) && values.length === 1 ? values[0] : values;
|
||||||
|
|
||||||
|
// 增加对 layer/text/iconfont unicode 映射的解析
|
||||||
|
if (attribute.name === 'shape') {
|
||||||
|
encodeRecord.shape = fontService.getIconFontKey(
|
||||||
|
encodeRecord[attribute.name] as string,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (encodeRecord.shape === 'line' && arrow.enable) {
|
||||||
|
// 只有在线图层且支持配置箭头的时候进行插入顶点的处理
|
||||||
|
const coords = encodeRecord.coordinates as Position[];
|
||||||
|
const arrowPoint = getArrowPoints(coords[0], coords[1]);
|
||||||
|
encodeRecord.coordinates.splice(1, 0, arrowPoint, arrowPoint);
|
||||||
|
}
|
||||||
|
return encodeRecord;
|
||||||
|
}) as IEncodeFeature[];
|
||||||
|
// 调整数据兼容 Amap2.0
|
||||||
|
adjustData2Amap2Coordinates(mappedData, mapService);
|
||||||
|
|
||||||
|
// 调整数据兼容 SimpleCoordinates
|
||||||
|
adjustData2SimpleCoordinates(mappedData, mapService);
|
||||||
|
|
||||||
|
return mappedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateData(
|
||||||
|
layer: ILayer,
|
||||||
|
fontService: IFontService,
|
||||||
|
mapService: IMapService,
|
||||||
|
styleAttributeService: IStyleAttributeService,
|
||||||
|
data: any,
|
||||||
|
options: ISourceCFG | undefined,
|
||||||
|
): IEncodeFeature[] {
|
||||||
|
const source = new Source(data, options);
|
||||||
|
const bottomColor = layer.getBottomColor();
|
||||||
|
const attributes = styleAttributeService.getLayerStyleAttributes() || [];
|
||||||
|
const { dataArray } = source.data;
|
||||||
|
const filterData = dataArray;
|
||||||
|
|
||||||
|
const mappedEncodeData = mapping(
|
||||||
|
attributes,
|
||||||
|
filterData,
|
||||||
|
fontService,
|
||||||
|
mapService,
|
||||||
|
bottomColor,
|
||||||
|
layer,
|
||||||
|
);
|
||||||
|
source.destroy();
|
||||||
|
return mappedEncodeData;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
gl,
|
gl,
|
||||||
IAttribute,
|
IAttribute,
|
||||||
|
IElements,
|
||||||
IModel,
|
IModel,
|
||||||
IModelDrawOptions,
|
IModelDrawOptions,
|
||||||
IModelInitializationOptions,
|
IModelInitializationOptions,
|
||||||
|
@ -107,6 +108,27 @@ export default class ReglModel implements IModel {
|
||||||
this.drawParams = drawParams;
|
this.drawParams = drawParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateAttributesAndElements(
|
||||||
|
attributes: { [key: string]: IAttribute },
|
||||||
|
elements: IElements,
|
||||||
|
) {
|
||||||
|
const reglAttributes: { [key: string]: regl.Attribute } = {};
|
||||||
|
Object.keys(attributes).forEach((name: string) => {
|
||||||
|
reglAttributes[name] = (attributes[name] as ReglAttribute).get();
|
||||||
|
});
|
||||||
|
this.drawParams.attributes = reglAttributes;
|
||||||
|
this.drawParams.elements = (elements as ReglElements).get();
|
||||||
|
|
||||||
|
this.drawCommand = this.reGl(this.drawParams);
|
||||||
|
const pickDrawParams = cloneDeep(this.drawParams);
|
||||||
|
pickDrawParams.blend = {
|
||||||
|
...pickDrawParams.blend,
|
||||||
|
enable: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.drawPickCommand = this.reGl(pickDrawParams);
|
||||||
|
}
|
||||||
|
|
||||||
public updateAttributes(attributes: { [key: string]: IAttribute }) {
|
public updateAttributes(attributes: { [key: string]: IAttribute }) {
|
||||||
const reglAttributes: { [key: string]: regl.Attribute } = {};
|
const reglAttributes: { [key: string]: regl.Attribute } = {};
|
||||||
Object.keys(attributes).forEach((name: string) => {
|
Object.keys(attributes).forEach((name: string) => {
|
||||||
|
|
|
@ -23,6 +23,12 @@ export default function geoJSON(
|
||||||
): IParserData {
|
): IParserData {
|
||||||
const resultData: IParseDataItem[] = [];
|
const resultData: IParseDataItem[] = [];
|
||||||
const featureKeys: IFeatureKey = {};
|
const featureKeys: IFeatureKey = {};
|
||||||
|
if (!data.features) {
|
||||||
|
data.features = [];
|
||||||
|
return {
|
||||||
|
dataArray: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
data.features = data.features.filter((item: Feature) => {
|
data.features = data.features.filter((item: Feature) => {
|
||||||
const geometry: Geometry | null = item.geometry as Geometry;
|
const geometry: Geometry | null = item.geometry as Geometry;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
import { Scene, json } from '@antv/l7';
|
||||||
|
import { PointLayer } from '@antv/l7-layers';
|
||||||
|
import { GaodeMap } from '@antv/l7-maps';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { csvParse } from 'd3-dsv';
|
||||||
|
import { styled, withStyles } from '@material-ui/core/styles';
|
||||||
|
import Slider from '@material-ui/core/Slider';
|
||||||
|
|
||||||
|
export default class Demo extends React.Component {
|
||||||
|
private scene: Scene;
|
||||||
|
private layer: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.state = {
|
||||||
|
currentYear: 50,
|
||||||
|
modelDatas: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSortedData(dataList: { DateTime: string }[]) {
|
||||||
|
const res = {},
|
||||||
|
years = [];
|
||||||
|
dataList.map((data) => {
|
||||||
|
const { DateTime } = data;
|
||||||
|
const year = DateTime.slice(0, 4);
|
||||||
|
if (res[year]) {
|
||||||
|
res[year].push({
|
||||||
|
Latitude: Number(data.Latitude),
|
||||||
|
Longitude: Number(data.Longitude),
|
||||||
|
Depth: Number(data.Depth),
|
||||||
|
Magnitude: Number(data.Magnitude),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
years.push(year);
|
||||||
|
res[year] = [];
|
||||||
|
res[year].push({
|
||||||
|
Latitude: Number(data.Latitude),
|
||||||
|
Longitude: Number(data.Longitude),
|
||||||
|
Depth: Number(data.Depth),
|
||||||
|
Magnitude: Number(data.Magnitude),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
res,
|
||||||
|
years,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getModelDatas(layer, sortedData, years, parser) {
|
||||||
|
const modelDatas = {};
|
||||||
|
years.map((year) => {
|
||||||
|
modelDatas[year] = layer.createModelData(sortedData[year], parser);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
modelDatas,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateData(size) {
|
||||||
|
let data = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
data.push({
|
||||||
|
lng: Math.random() * 180,
|
||||||
|
lat: Math.random() * 80 - 40,
|
||||||
|
c: Math.random() > 0.5 ? '#f00' : '#ff0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new GaodeMap({
|
||||||
|
center: [-120, 36],
|
||||||
|
pitch: 0,
|
||||||
|
zoom: 6,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
fetch(
|
||||||
|
'https://gw.alipayobjects.com/os/bmw-prod/6b15fe03-9d5b-4779-831d-ec30aa2e4738.csv',
|
||||||
|
)
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((res) => {
|
||||||
|
const originData = csvParse(res);
|
||||||
|
const { res: sortedData, years } = this.getSortedData(originData);
|
||||||
|
const parser = {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'Longitude',
|
||||||
|
y: 'Latitude',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let layer = new PointLayer()
|
||||||
|
.source(sortedData[years[0]], parser)
|
||||||
|
.shape('simple')
|
||||||
|
.size('Magnitude', (v) => Math.pow(v, 2))
|
||||||
|
.color('Magnitude', [
|
||||||
|
'#ffffb2',
|
||||||
|
'#fed976',
|
||||||
|
'#feb24c',
|
||||||
|
'#fd8d3c',
|
||||||
|
'#f03b20',
|
||||||
|
'#bd0026',
|
||||||
|
])
|
||||||
|
.style({
|
||||||
|
opacity: 0.5,
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.addLayer(layer);
|
||||||
|
this.layer = layer;
|
||||||
|
|
||||||
|
this.getModelDatas(layer, sortedData, years, parser);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public timelinechange(time) {
|
||||||
|
if (time !== this.state.currentYear) {
|
||||||
|
this.layer.updateModelData(this.state.modelDatas[time]);
|
||||||
|
this.scene.render();
|
||||||
|
this.setState({
|
||||||
|
currentYear: time,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{this.state.modelDatas !== undefined && (
|
||||||
|
<RangeInput
|
||||||
|
min={1988}
|
||||||
|
max={2018}
|
||||||
|
value={this?.state?.currentYear || 1988}
|
||||||
|
onChange={(e) => this.timelinechange(e)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PositionContainer = styled('div')({
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 1,
|
||||||
|
bottom: '40px',
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
const SliderInput = withStyles({
|
||||||
|
root: {
|
||||||
|
marginLeft: 12,
|
||||||
|
width: '40%',
|
||||||
|
},
|
||||||
|
valueLabel: {
|
||||||
|
'& span': {
|
||||||
|
background: 'none',
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})(Slider);
|
||||||
|
|
||||||
|
function RangeInput({ min, max, value, onChange }) {
|
||||||
|
return (
|
||||||
|
<PositionContainer>
|
||||||
|
<SliderInput
|
||||||
|
min={min}
|
||||||
|
max={max}
|
||||||
|
value={value}
|
||||||
|
onChange={(event, newValue) => onChange(newValue)}
|
||||||
|
valueLabelDisplay="auto"
|
||||||
|
valueLabelFormat={(t) => t}
|
||||||
|
/>
|
||||||
|
</PositionContainer>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// @ts-ignore
|
||||||
|
import { Scene } from '@antv/l7';
|
||||||
|
import { PointLayer } from '@antv/l7-layers';
|
||||||
|
import { GaodeMap } from '@antv/l7-maps';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default class Demo extends React.Component {
|
||||||
|
private scene: Scene;
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateData(size) {
|
||||||
|
let data = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
data.push({
|
||||||
|
lng: Math.random() * 180,
|
||||||
|
lat: Math.random() * 80 - 40,
|
||||||
|
c: Math.random() > 0.5 ? '#f00' : '#ff0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new GaodeMap({
|
||||||
|
center: [120, 30],
|
||||||
|
pitch: 0,
|
||||||
|
zoom: 2,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data1 = this.generateData(1000);
|
||||||
|
const data2 = this.generateData(10000);
|
||||||
|
|
||||||
|
const layer = new PointLayer()
|
||||||
|
.source(data1, {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'lng',
|
||||||
|
y: 'lat',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.size(10)
|
||||||
|
// .color('#f00')
|
||||||
|
.color('c', (v) => v)
|
||||||
|
// .shape('circle')
|
||||||
|
.shape('simple')
|
||||||
|
.active(true);
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
scene.addLayer(layer);
|
||||||
|
|
||||||
|
let data1cache = layer.createModelData(data1, {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'lng',
|
||||||
|
y: 'lat',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(data1cache);
|
||||||
|
|
||||||
|
let data2cache = layer.createModelData(data2, {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'lng',
|
||||||
|
y: 'lat',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let c = 0;
|
||||||
|
setInterval(() => {
|
||||||
|
if (c === 0) {
|
||||||
|
c = 1;
|
||||||
|
layer.updateModelData(data2cache);
|
||||||
|
scene.render();
|
||||||
|
} else {
|
||||||
|
c = 0;
|
||||||
|
layer.updateModelData(data1cache);
|
||||||
|
scene.render();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// @ts-ignore
|
||||||
|
import { Scene } from '@antv/l7';
|
||||||
|
import { PointLayer, LineLayer } from '@antv/l7-layers';
|
||||||
|
import { GaodeMap } from '@antv/l7-maps';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default class Demo extends React.Component {
|
||||||
|
private scene: Scene;
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateData(size) {
|
||||||
|
let data = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
data.push({
|
||||||
|
lng: Math.random() * 180,
|
||||||
|
lat: Math.random() * 80 - 40,
|
||||||
|
c: Math.random() > 0.5 ? '#f00' : '#ff0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new GaodeMap({
|
||||||
|
center: [110, 30],
|
||||||
|
pitch: 0,
|
||||||
|
zoom: 4,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// const data1 = this.generateData(1000);
|
||||||
|
// const data2 = this.generateData(10000);
|
||||||
|
const data1 = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
c: '#f00',
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: [
|
||||||
|
[100.37109375, 32.32427558887655],
|
||||||
|
[101.689453125, 28.844673680771795],
|
||||||
|
[104.853515625, 30.524413269923986],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const data2 = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {
|
||||||
|
c: '#ff0',
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
type: 'LineString',
|
||||||
|
coordinates: [
|
||||||
|
[109.6875, 39.90973623453719],
|
||||||
|
[115.75195312499999, 39.90973623453719],
|
||||||
|
[109.3359375, 37.579412513438385],
|
||||||
|
[115.57617187499999, 36.80928470205937],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const layer = new LineLayer()
|
||||||
|
.source(data1)
|
||||||
|
.size(2)
|
||||||
|
.color('c', (v) => v)
|
||||||
|
.shape('line')
|
||||||
|
.active(true);
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
scene.addLayer(layer);
|
||||||
|
|
||||||
|
// let data1cache = layer.createModelData(data1, {});
|
||||||
|
|
||||||
|
// console.log(data1cache);
|
||||||
|
|
||||||
|
// let data2cache = layer.createModelData(data2, {});
|
||||||
|
// console.log('data2cache', data2cache)
|
||||||
|
|
||||||
|
// let c = 0;
|
||||||
|
// setInterval(() => {
|
||||||
|
// if (c === 0) {
|
||||||
|
// c = 1;
|
||||||
|
// layer.updateModelData(data2cache);
|
||||||
|
// scene.render();
|
||||||
|
// } else {
|
||||||
|
// c = 0;
|
||||||
|
// layer.updateModelData(data1cache);
|
||||||
|
// scene.render();
|
||||||
|
// }
|
||||||
|
// }, 1000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
let data2cache = layer.createModelData(data2);
|
||||||
|
layer.updateModelData(data2cache);
|
||||||
|
scene.render();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// @ts-ignore
|
||||||
|
import { Scene } from '@antv/l7';
|
||||||
|
import {
|
||||||
|
PointLayer,
|
||||||
|
LineLayer,
|
||||||
|
PolygonLayer,
|
||||||
|
GeometryLayer,
|
||||||
|
} from '@antv/l7-layers';
|
||||||
|
import { GaodeMap } from '@antv/l7-maps';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default class Demo extends React.Component {
|
||||||
|
private scene: Scene;
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateData(size) {
|
||||||
|
let data = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
data.push({
|
||||||
|
lng: Math.random() * 180,
|
||||||
|
lat: Math.random() * 80 - 40,
|
||||||
|
c: Math.random() > 0.5 ? '#f00' : '#ff0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new GaodeMap({
|
||||||
|
pitch: 60,
|
||||||
|
center: [120.1025, 30.2594],
|
||||||
|
rotation: 160,
|
||||||
|
zoom: 14,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const layer = new GeometryLayer()
|
||||||
|
.style({
|
||||||
|
width: 0.074,
|
||||||
|
height: 0.061,
|
||||||
|
center: [120.1025, 30.2594],
|
||||||
|
widthSegments: 100,
|
||||||
|
heightSegments: 100,
|
||||||
|
// widthSegments: 10,
|
||||||
|
// heightSegments: 10,
|
||||||
|
mapTexture:
|
||||||
|
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ',
|
||||||
|
terrainTexture:
|
||||||
|
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*eYFaRYlnnOUAAAAAAAAAAAAAARQnAQ',
|
||||||
|
rgb2height: (r: number, g: number, b: number) => {
|
||||||
|
let h =
|
||||||
|
-10000.0 +
|
||||||
|
(r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) * 0.1;
|
||||||
|
h = h / 20 - 127600;
|
||||||
|
h = Math.max(0, h);
|
||||||
|
return h;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.color('#f00');
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
scene.addLayer(layer);
|
||||||
|
|
||||||
|
let cache10 = null,
|
||||||
|
cache100 = null;
|
||||||
|
|
||||||
|
layer.on('terrainImageLoaded', () => {
|
||||||
|
console.log('terrainImageLoaded');
|
||||||
|
|
||||||
|
cache10 = layer.createModelData([], {
|
||||||
|
widthSegments: 10,
|
||||||
|
heightSegments: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
cache100 = layer.createModelData([], {
|
||||||
|
widthSegments: 100,
|
||||||
|
heightSegments: 100,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let currentCache = 'cache100';
|
||||||
|
scene.on('zoom', ({ value }) => {
|
||||||
|
if (!cache10 || !cache100) return;
|
||||||
|
if (value < 14.5) {
|
||||||
|
if (currentCache !== 'cache10') {
|
||||||
|
console.log('set cache10');
|
||||||
|
layer.updateModelData(cache10);
|
||||||
|
currentCache = 'cache10';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentCache !== 'cache100') {
|
||||||
|
console.log('set cache100');
|
||||||
|
layer.updateModelData(cache100);
|
||||||
|
currentCache = 'cache100';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
// @ts-ignore
|
||||||
|
import { Scene } from '@antv/l7';
|
||||||
|
import { PointLayer, LineLayer, PolygonLayer } from '@antv/l7-layers';
|
||||||
|
import { GaodeMap } from '@antv/l7-maps';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default class Demo extends React.Component {
|
||||||
|
private scene: Scene;
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public generateData(size) {
|
||||||
|
let data = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
data.push({
|
||||||
|
lng: Math.random() * 180,
|
||||||
|
lat: Math.random() * 80 - 40,
|
||||||
|
c: Math.random() > 0.5 ? '#f00' : '#ff0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new GaodeMap({
|
||||||
|
center: [110, 30],
|
||||||
|
pitch: 0,
|
||||||
|
zoom: 4,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data1 = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[98.4375, 35.746512259918504],
|
||||||
|
[98.173828125, 30.14512718337613],
|
||||||
|
[104.94140625, 30.600093873550072],
|
||||||
|
[98.4375, 35.746512259918504],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const data2 = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: [
|
||||||
|
{
|
||||||
|
type: 'Feature',
|
||||||
|
properties: {},
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
[110.390625, 38.06539235133249],
|
||||||
|
[106.435546875, 33.50475906922609],
|
||||||
|
[112.763671875, 32.47269502206151],
|
||||||
|
[109.072265625, 27.21555620902969],
|
||||||
|
[117.24609374999999, 27.605670826465445],
|
||||||
|
[118.037109375, 30.372875188118016],
|
||||||
|
[115.927734375, 34.30714385628804],
|
||||||
|
[111.97265625, 36.87962060502676],
|
||||||
|
[110.390625, 38.06539235133249],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const layer = new PolygonLayer()
|
||||||
|
.source(data1)
|
||||||
|
.size(2)
|
||||||
|
.color('#f00')
|
||||||
|
.shape('fill')
|
||||||
|
.active(true);
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
scene.addLayer(layer);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
let data2cache = layer.createModelData(data2);
|
||||||
|
layer.updateModelData(data2cache);
|
||||||
|
scene.render();
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,21 @@
|
||||||
import { storiesOf } from '@storybook/react';
|
import { storiesOf } from '@storybook/react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import UpdateAttrAndEle from './components/updateAttrAndEle';
|
||||||
|
import UpdateAttrAndEle_line from './components/updateAttrAndEle_line';
|
||||||
|
import UpdateAttrAndEle_polygon from './components/updateAttrAndEle_polygon';
|
||||||
|
import UpdateAttrAndEle_planeGeometry from './components/updateAttrAndEle_planeGeometry';
|
||||||
|
import UpdateAttrTimeLine from './components/updataPointsTimeLine';
|
||||||
import PointTest from './components/Map';
|
import PointTest from './components/Map';
|
||||||
import BigLine from './components/BigLine';
|
import BigLine from './components/BigLine';
|
||||||
import DataUpdate from './components/DataUpdate';
|
import DataUpdate from './components/DataUpdate';
|
||||||
// @ts-ignore
|
|
||||||
storiesOf('地图性能检测', module)
|
storiesOf('地图性能检测', module)
|
||||||
|
.add('更新数据 update point attr&ele', () => <UpdateAttrAndEle />)
|
||||||
|
.add('更新数据 update line attr&ele', () => <UpdateAttrAndEle_line />)
|
||||||
|
.add('更新数据 update polygon attr&ele', () => <UpdateAttrAndEle_polygon />)
|
||||||
|
.add('更新数据 update plane geometry attr&ele', () => <UpdateAttrAndEle_planeGeometry />)
|
||||||
|
.add('更新数据 update updateAttrTimeLine', () => <UpdateAttrTimeLine />)
|
||||||
.add('点', () => <PointTest />)
|
.add('点', () => <PointTest />)
|
||||||
.add('BigLine', () => <BigLine />)
|
.add('BigLine', () => <BigLine />)
|
||||||
.add('DataUpdate', () => <DataUpdate />);
|
.add('DataUpdate', () => <DataUpdate />);
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class Demo extends React.Component {
|
||||||
center: [120.1025, 30.2594],
|
center: [120.1025, 30.2594],
|
||||||
widthSegments: 200,
|
widthSegments: 200,
|
||||||
heightSegments: 200,
|
heightSegments: 200,
|
||||||
terrainClipHeight: 1,
|
// terrainClipHeight: 1,
|
||||||
mapTexture:
|
mapTexture:
|
||||||
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ',
|
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ',
|
||||||
terrainTexture:
|
terrainTexture:
|
||||||
|
|
Loading…
Reference in New Issue