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:
YiQianYao 2022-04-25 19:36:58 +08:00 committed by GitHub
parent 90f92f069b
commit aa2e9540de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1167 additions and 101 deletions

View File

@ -19,3 +19,5 @@ window.react = require('react');
window.popmotion = require('popmotion');
window.reactDom = require('react-dom');
window.antd = require('antd');
window.d3Dsv = require('d3-dsv');
window.materialUI = require('@material-ui')

View File

@ -88,6 +88,8 @@
"husky": "^3.0.9",
"jest": "^24.9.0",
"jest-electron": "^0.1.11",
"@material-ui/core": "^4.10.2",
"@material-ui/icons": "^4.9.1",
"jest-styled-components": "^6.2.1",
"lerna": "^3.16.4",
"lint-staged": "^9.2.4",

View File

@ -78,6 +78,7 @@ export interface ILayerModel {
// earth mode
setEarthTime?(time: number): void;
createModelData?(options?: any): any;
}
export interface IModelUniform {
[key: string]: IUniform;
@ -110,6 +111,11 @@ export interface ILegendClassificaItem {
// 图层图例
export type LegendItems = ILegendSegmentItem[] | ILegendClassificaItem[];
export interface IAttrubuteAndElements {
attributes: any;
elements: any;
}
export interface ILayer {
id: string; // 一个场景中同一类型 Layer 可能存在多个
type: string; // 代表 Layer 的类型
@ -150,6 +156,7 @@ export interface ILayer {
// 初始化 layer 的时候指定 layer type 类型()兼容空数据的情况
layerType?: string | undefined;
isLayerGroup: boolean;
triangulation?: Triangulation | undefined;
/**
* threejs
* @param lnglat
@ -161,6 +168,7 @@ export interface ILayer {
threeRenderService?: any;
getShaderPickStat: () => boolean;
updateModelData(data: IAttrubuteAndElements): void;
addMaskLayer(maskLayer: ILayer): void;
removeMaskLayer(maskLayer: ILayer): void;

View File

@ -244,6 +244,10 @@ export interface IModelDrawOptions {
*/
export interface IModel {
updateAttributes(attributes: { [key: string]: IAttribute }): void;
updateAttributesAndElements(
attributes: { [key: string]: IAttribute },
elements: IElements,
): void;
addUniforms(uniforms: { [key: string]: IUniform }): void;
draw(options: IModelDrawOptions, pick?: boolean): void;
destroy(): void;

View File

@ -1,6 +1,7 @@
import {
AttributeType,
gl,
IAttrubuteAndElements,
IEncodeFeature,
IModelUniform,
ITexture2D,
@ -15,9 +16,9 @@ import planeVert from '../shaders/plane_vert.glsl';
export default class PlaneModel extends BaseModel {
protected texture: ITexture2D;
protected terrainImage: HTMLImageElement;
protected terrainImageLoaded: boolean = false;
protected mapTexture: string | undefined;
protected positions: number[];
protected indices: number[];
public initPlane(
width = 1,
@ -88,7 +89,6 @@ export default class PlaneModel extends BaseModel {
center = [120, 30],
terrainTexture,
} = this.layer.getLayerConfig() as IGeometryLayerStyleOptions;
const { indices, positions } = this.initPlane(
width,
height,
@ -96,12 +96,9 @@ export default class PlaneModel extends BaseModel {
heightSegments,
...center,
);
this.positions = positions;
this.indices = indices;
if (terrainTexture) {
// 存在地形贴图的时候会根据地形贴图对顶点进行偏移
this.loadTerrainTexture();
this.loadTerrainTexture(positions, indices);
}
return {
@ -110,13 +107,6 @@ export default class PlaneModel extends BaseModel {
size: 5,
};
};
public planeGeometryUpdateTriangulation = () => {
return {
vertices: this.positions,
indices: this.indices,
size: 5,
};
};
public getUninforms(): IModelUniform {
const {
@ -135,14 +125,12 @@ export default class PlaneModel extends BaseModel {
u_mapFlag: mapTexture ? 1 : 0,
u_terrainClipHeight: terrainTexture ? terrainClipHeight : -1,
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 {
// @ts-ignore
this.terrainImage = null;
this.texture?.destroy();
}
@ -161,7 +149,6 @@ export default class PlaneModel extends BaseModel {
});
this.updateTexture(mapTexture);
return [
this.layer.buildLayerModel({
moduleName: 'geometry_plane',
@ -169,6 +156,7 @@ export default class PlaneModel extends BaseModel {
fragmentShader: planeFrag,
triangulation: this.planeGeometryTriangulation,
primitive: gl.TRIANGLES,
// primitive: gl.LINES,
depth: { enable: true },
blend: this.getBlend(),
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() {
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 {
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() {
return {
properties: {

View File

@ -5,6 +5,7 @@ import {
gl,
IActiveOption,
IAnimateOption,
IAttrubuteAndElements,
ICameraService,
ICoordinateSystemService,
IDataState,
@ -44,6 +45,7 @@ import {
ScaleTypes,
StyleAttributeField,
StyleAttributeOption,
Triangulation,
TYPES,
} from '@antv/l7-core';
import Source from '@antv/l7-source';
@ -53,6 +55,7 @@ import { Container } from 'inversify';
import { isEqual, isFunction, isObject, isUndefined } from 'lodash';
import { BlendTypes } from '../utils/blend';
import { handleStyleDataMapping } from '../utils/dataMappingStyle';
import { calculateData } from '../utils/layerData';
import {
createMultiPassRenderer,
normalizePasses,
@ -81,6 +84,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
public clusterZoom: number = 0; // 聚合等级标记
public layerType?: string | undefined;
public isLayerGroup: boolean = false;
public triangulation?: Triangulation | undefined;
public dataState: IDataState = {
dataSourceNeedUpdate: false,
@ -399,8 +403,55 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
target: this,
type: 'add',
});
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样式
*/

View File

@ -165,6 +165,7 @@ export default class LineModel extends BaseModel {
depth = false,
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
const { frag, vert, type } = this.getShaders();
this.layer.triangulation = LineTriangulation;
return [
this.layer.buildLayerModel({
moduleName: 'line_' + type,

View File

@ -201,6 +201,9 @@ export default class FillModel extends BaseModel {
// TODO: 判断当前的点图层的模型是普通地图模式还是地球模式
const isGlobel = this.mapService.version === 'GLOBEL';
this.layer.triangulation = isGlobel
? GlobelPointFillTriangulation
: PointFillTriangulation;
return [
this.layer.buildLayerModel({
moduleName: 'pointfill_' + type,

View File

@ -103,6 +103,7 @@ export default class SimplePointModel extends BaseModel {
mask = false,
maskInside = true,
} = this.layer.getLayerConfig() as IPointLayerStyleOptions;
this.layer.triangulation = PointTriangulation;
return [
this.layer.buildLayerModel({
moduleName: 'simplepoint',

View File

@ -77,6 +77,9 @@ export default class FillModel extends BaseModel {
mask = false,
maskInside = true,
} = this.layer.getLayerConfig() as IPolygonLayerStyleOptions;
this.layer.triangulation = opacityLinear.enable
? polygonTriangulationWithCenter
: polygonTriangulation;
return [
this.layer.buildLayerModel({
moduleName: 'polygon',

View File

@ -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;
}

View File

@ -1,6 +1,7 @@
import {
gl,
IAttribute,
IElements,
IModel,
IModelDrawOptions,
IModelInitializationOptions,
@ -107,6 +108,27 @@ export default class ReglModel implements IModel {
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 }) {
const reglAttributes: { [key: string]: regl.Attribute } = {};
Object.keys(attributes).forEach((name: string) => {

View File

@ -23,6 +23,12 @@ export default function geoJSON(
): IParserData {
const resultData: IParseDataItem[] = [];
const featureKeys: IFeatureKey = {};
if (!data.features) {
data.features = [];
return {
dataArray: [],
};
}
data.features = data.features.filter((item: Feature) => {
const geometry: Geometry | null = item.geometry as Geometry;
return (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,21 @@
import { storiesOf } from '@storybook/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 BigLine from './components/BigLine';
import DataUpdate from './components/DataUpdate';
// @ts-ignore
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('BigLine', () => <BigLine />)
.add('DataUpdate', () => <DataUpdate />);

View File

@ -31,7 +31,7 @@ export default class Demo extends React.Component {
center: [120.1025, 30.2594],
widthSegments: 200,
heightSegments: 200,
terrainClipHeight: 1,
// terrainClipHeight: 1,
mapTexture:
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ',
terrainTexture: