Shihui dev (#784)

* feat: add getModelMatrix into viewport

* feat: 新增地球模式 (初步构建)

* feat: 完善地球交互

* style: lint style

* feat: 调整地球图层缩放的方向

* style: lint style

* feat: 增加地球模式的 pointLayer/fill 图层

* style: lint style

* feat: 增加地球、太阳的简单运动系统,优化部分代码结构
This commit is contained in:
YiQianYao 2021-09-27 15:52:20 +08:00 committed by GitHub
parent a0bf0ebaab
commit 0bcc822cdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2019 additions and 47 deletions

View File

@ -10,7 +10,7 @@
"@antv/gatsby-theme-antv": "^1.1.1", "@antv/gatsby-theme-antv": "^1.1.1",
"@antv/l7-district": "^2.3.9", "@antv/l7-district": "^2.3.9",
"@antv/l7-draw": "^2.3.40", "@antv/l7-draw": "^2.3.40",
"@antv/l7-react": "^2.3.2", "@antv/l7-react": "^2.3.3",
"@babel/cli": "^7.6.4", "@babel/cli": "^7.6.4",
"@babel/core": "^7.6.4", "@babel/core": "^7.6.4",
"@babel/plugin-proposal-decorators": "^7.6.0", "@babel/plugin-proposal-decorators": "^7.6.0",
@ -196,5 +196,6 @@
}, },
"tnpm": { "tnpm": {
"mode": "yarn" "mode": "yarn"
} },
"dependencies": {}
} }

View File

@ -63,6 +63,10 @@ export default class CameraService implements ICameraService {
return this.jitteredProjectionMatrix || this.viewport.getProjectionMatrix(); return this.jitteredProjectionMatrix || this.viewport.getProjectionMatrix();
} }
public getModelMatrix(): number[] {
return this.viewport.getModelMatrix();
}
public getViewMatrix(): number[] { public getViewMatrix(): number[] {
return this.viewport.getViewMatrix(); return this.viewport.getViewMatrix();
} }

View File

@ -14,6 +14,7 @@ export const CameraUniform = {
export interface IViewport { export interface IViewport {
syncWithMapCamera(mapCamera: Partial<IMapCamera>): void; syncWithMapCamera(mapCamera: Partial<IMapCamera>): void;
getProjectionMatrix(): number[]; getProjectionMatrix(): number[];
getModelMatrix(): number[];
getViewMatrix(): number[]; getViewMatrix(): number[];
getViewMatrixUncentered(): number[]; getViewMatrixUncentered(): number[];
getViewProjectionMatrixUncentered(): number[]; getViewProjectionMatrixUncentered(): number[];

View File

@ -71,6 +71,9 @@ export interface ILayerModel {
initModels(): IModel[]; initModels(): IModel[];
needUpdate(): boolean; needUpdate(): boolean;
clearModels(): void; clearModels(): void;
// earth mode
setEarthTime?(time: number): void;
} }
export interface IModelUniform { export interface IModelUniform {
[key: string]: IUniform; [key: string]: IUniform;
@ -261,6 +264,13 @@ export interface ILayer {
// 增加加载模型的动画混合器 // 增加加载模型的动画混合器
addAnimateMixer?(mixer: AnimationMixer): void; addAnimateMixer?(mixer: AnimationMixer): void;
/**
*
*/
// 设置当前地球时间 控制太阳角度
setEarthTime(time: number): void;
} }
/** /**
@ -336,7 +346,16 @@ export interface ILayerConfig {
* *
*/ */
enableLighting: boolean; enableLighting: boolean;
/**
*
*/
animateOption: Partial<IAnimateOption>; animateOption: Partial<IAnimateOption>;
/**
*
*/
globelOtions: any;
/** /**
* layer point text iconfont * layer point text iconfont
*/ */

View File

@ -94,6 +94,16 @@ export interface IMapService<RawMap = {}> {
// lngLatToCoords?(lnglatArray: any): any; // lngLatToCoords?(lnglatArray: any): any;
getCustomCoordCenter?(): [number, number]; getCustomCoordCenter?(): [number, number];
exportMap(type: 'jpg' | 'png'): string; exportMap(type: 'jpg' | 'png'): string;
// 地球模式下的地图方法/属性
rotateY?(
option:
| {
force?: boolean;
reg?: number;
}
| undefined,
): void;
} }
export const MapServiceEvent = ['mapload']; export const MapServiceEvent = ['mapload'];

View File

@ -989,6 +989,14 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
} }
} }
/**
*
* @param time
*/
public setEarthTime(time: number) {
console.warn('empty fn');
}
protected getConfigSchema() { protected getConfigSchema() {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@ -1,7 +1,8 @@
import { IEncodeFeature } from '@antv/l7-core'; import { IEncodeFeature } from '@antv/l7-core';
import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils'; import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils';
import earcut from 'earcut'; import earcut from 'earcut';
import { vec3 } from 'gl-matrix'; // @ts-ignore
import { mat4, vec3 } from 'gl-matrix';
import ExtrudePolyline from '../utils/extrude_polyline'; import ExtrudePolyline from '../utils/extrude_polyline';
import { calculateCentroid } from '../utils/geo'; import { calculateCentroid } from '../utils/geo';
import extrudePolygon, { import extrudePolygon, {
@ -20,6 +21,10 @@ interface IGeometryCache {
[key: string]: IExtrudeGeomety; [key: string]: IExtrudeGeomety;
} }
const GeometryCache: IGeometryCache = {}; const GeometryCache: IGeometryCache = {};
// 地球网格半径
const EARTH_RADIUS = 100;
const EARTH_SEGMENTS = 36;
/** /**
* 2D * 2D
* @param feature feature * @param feature feature
@ -33,6 +38,39 @@ export function PointFillTriangulation(feature: IEncodeFeature) {
size: coordinates.length, size: coordinates.length,
}; };
} }
/**
* 2D ()
* @param feature feature
*/
export function GlobelPointFillTriangulation(feature: IEncodeFeature) {
const coordinates = calculateCentroid(feature.coordinates);
const xyz = lglt2xyz(coordinates as [number, number]);
return {
vertices: [...xyz, ...xyz, ...xyz, ...xyz],
indices: [0, 1, 2, 2, 3, 0],
size: xyz.length,
};
}
function torad(deg: number) {
return (deg / 180) * Math.acos(-1);
}
/**
* xyz
* @param longitude
* @param latitude
* @param radius
*/
function lglt2xyz(lnglat: [number, number]) {
// TODO: + Math.PI/2 是为了对齐坐标
const lng = torad(lnglat[0]) + Math.PI / 2;
const lat = torad(lnglat[1]);
const z = EARTH_RADIUS * Math.cos(lat) * Math.cos(lng);
const x = EARTH_RADIUS * Math.cos(lat) * Math.sin(lng);
const y = EARTH_RADIUS * Math.sin(lat);
return [x, y, z];
}
/** /**
* 3D * 3D
@ -123,7 +161,6 @@ export function polygonTriangulation(feature: IEncodeFeature) {
const { coordinates } = feature; const { coordinates } = feature;
const flattengeo = earcut.flatten(coordinates as number[][][]); const flattengeo = earcut.flatten(coordinates as number[][][]);
const { vertices, dimensions, holes } = flattengeo; const { vertices, dimensions, holes } = flattengeo;
return { return {
indices: earcut(vertices, holes, dimensions), indices: earcut(vertices, holes, dimensions),
vertices, vertices,
@ -137,7 +174,6 @@ export function PolygonExtrudeTriangulation(feature: IEncodeFeature) {
coordinates, coordinates,
true, true,
); );
return { return {
vertices: positions, // [ x, y, z, uv.x,uv.y ] vertices: positions, // [ x, y, z, uv.x,uv.y ]
indices: index, indices: index,
@ -383,3 +419,134 @@ function addDir(dirX: number, dirY: number) {
const y = (dirY + 1) / 2; const y = (dirY + 1) / 2;
return [x, y]; return [x, y];
} }
/**
*
* @returns
*/
export function earthTriangulation() {
const mesh = primitiveSphere(EARTH_RADIUS, { segments: EARTH_SEGMENTS });
const { positionsArr, indicesArr, normalArr } = mesh;
return {
vertices: positionsArr,
indices: indicesArr,
size: 5,
normals: normalArr,
};
}
/**
*
* @param radius
* @param opt
* @returns
*/
function primitiveSphere(
radius: number,
opt: {
segments: number;
},
) {
const matRotY = mat4.create();
const matRotZ = mat4.create();
const up = vec3.fromValues(0, 1, 0);
const tmpVec3 = vec3.fromValues(0, 0, 0);
opt = opt || {};
radius = typeof radius !== 'undefined' ? radius : 1;
const segments = typeof opt.segments !== 'undefined' ? opt.segments : 32;
const totalZRotationSteps = 2 + segments;
const totalYRotationSteps = 2 * totalZRotationSteps;
const indices = [];
const indicesArr = [];
const positions = [];
const positionsArr = [];
const normals = [];
const normalArr = [];
const uvs = [];
for (
let zRotationStep = 0;
zRotationStep <= totalZRotationSteps;
zRotationStep++
) {
const normalizedZ = zRotationStep / totalZRotationSteps;
const angleZ = normalizedZ * Math.PI;
for (
let yRotationStep = 0;
yRotationStep <= totalYRotationSteps;
yRotationStep++
) {
const normalizedY = yRotationStep / totalYRotationSteps;
const angleY = normalizedY * Math.PI * 2;
mat4.identity(matRotZ);
mat4.rotateZ(matRotZ, matRotZ, -angleZ);
mat4.identity(matRotY);
mat4.rotateY(matRotY, matRotY, angleY);
vec3.transformMat4(tmpVec3, up, matRotZ);
vec3.transformMat4(tmpVec3, tmpVec3, matRotY);
vec3.scale(tmpVec3, tmpVec3, -radius);
positions.push(tmpVec3.slice());
positionsArr.push(...tmpVec3.slice());
vec3.normalize(tmpVec3, tmpVec3);
normals.push(tmpVec3.slice());
normalArr.push(...tmpVec3.slice());
uvs.push([normalizedY, 1 - normalizedZ]);
// position 和 uv 一起存储
positionsArr.push(normalizedY, 1 - normalizedZ);
}
if (zRotationStep > 0) {
const verticesCount = positions.length;
let firstIndex = verticesCount - 2 * (totalYRotationSteps + 1);
for (
;
firstIndex + totalYRotationSteps + 2 < verticesCount;
firstIndex++
) {
indices.push([
firstIndex,
firstIndex + 1,
firstIndex + totalYRotationSteps + 1,
]);
indicesArr.push(
firstIndex,
firstIndex + 1,
firstIndex + totalYRotationSteps + 1,
);
indices.push([
firstIndex + totalYRotationSteps + 1,
firstIndex + 1,
firstIndex + totalYRotationSteps + 2,
]);
indicesArr.push(
firstIndex + totalYRotationSteps + 1,
firstIndex + 1,
firstIndex + totalYRotationSteps + 2,
);
}
}
}
return {
cells: indices,
positions,
normals,
uvs,
positionsArr,
indicesArr,
normalArr,
};
}

View File

@ -0,0 +1,33 @@
import BaseLayer from '../core/BaseLayer';
import BaseEarthModel from './models/base';
export type EarthType = 'base';
interface IEarthLayerStyleOptions {
setEarthTime(time: number): void;
}
const EarthModels: { [key in EarthType]: any } = {
base: BaseEarthModel,
};
export default class EarthLayer extends BaseLayer<IEarthLayerStyleOptions> {
public type: string = 'EarthLayer';
public buildModels() {
const shape = 'base';
this.layerModel = new EarthModels[shape](this);
this.models = this.layerModel.initModels();
}
/**
*
* @param time
*/
public setEarthTime(time: number) {
if (this.layerModel && this.layerModel.setEarthTime) {
this.layerModel.setEarthTime(time);
} else {
console.error('请在 scene loaded 之后执行该方法!');
}
}
}

View File

@ -0,0 +1,179 @@
import {
AttributeType,
BlendType,
gl,
IEncodeFeature,
ILayerConfig,
IModel,
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import BaseModel, { styleOffset, styleSingle } from '../../core/BaseModel';
import { earthTriangulation } from '../../core/triangulation';
import baseFrag from '../shaders/base_frag.glsl';
import baseVert from '../shaders/base_vert.glsl';
export default class BaseEarthModel extends BaseModel {
protected texture: ITexture2D;
// T: 当前的地球时间 - 控制太阳的方位
private earthTime: number = 3.4;
private sunX = 1000;
private sunY = 1000;
private sunZ = 1000;
private sunRadius = Math.sqrt(
this.sunX * this.sunX + this.sunY * this.sunY + this.sunZ * this.sunZ,
);
public getUninforms(): IModelUniform {
const { animateOption, globelOtions } = this.layer.getLayerConfig();
if (animateOption?.enable) {
// @ts-ignore
// T: rotateY 方法只有在地球模式下存在
this.mapService.rotateY({
reg: 0.002,
});
this.earthTime += 0.02;
this.sunY = 10;
this.sunX = Math.cos(this.earthTime) * (this.sunRadius - this.sunY);
this.sunZ = Math.sin(this.earthTime) * (this.sunRadius - this.sunY);
}
return {
u_ambientRatio: globelOtions?.ambientRatio || 0.6, // 环境光
u_diffuseRatio: globelOtions?.diffuseRatio || 0.4, // 漫反射
u_specularRatio: globelOtions?.specularRatio || 0.1, // 高光反射
// u_sunLight: [120, 120, 120],
u_sunLight: [this.sunX, this.sunY, this.sunZ],
u_texture: this.texture,
};
}
public setEarthTime(time: number) {
this.earthTime = time;
this.sunY = 10;
this.sunX = Math.cos(this.earthTime) * (this.sunRadius - this.sunY);
this.sunZ = Math.sin(this.earthTime) * (this.sunRadius - this.sunY);
this.layerService.renderLayers();
}
public initModels(): IModel[] {
const { globelOtions } = this.layer.getLayerConfig();
if (globelOtions?.earthTime !== undefined) {
this.setEarthTime(globelOtions.earthTime);
}
const source = this.layer.getSource();
const { createTexture2D } = this.rendererService;
this.texture = createTexture2D({
height: 0,
width: 0,
});
source.data.images.then((imageData: HTMLImageElement[]) => {
this.texture = createTexture2D({
data: imageData[0],
width: imageData[0].width,
height: imageData[0].height,
});
this.layerService.renderLayers();
});
return this.buildModels();
}
public clearModels() {
return '';
}
public buildModels(): IModel[] {
return [
this.layer.buildLayerModel({
moduleName: 'baseEarth',
vertexShader: baseVert,
fragmentShader: baseFrag,
triangulation: earthTriangulation,
depth: { enable: true },
blend: this.getBlend(),
}),
];
}
protected registerBuiltinAttributes() {
// point layer size;
this.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: 1,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
const { size = 1 } = feature;
return Array.isArray(size) ? [size[0]] : [size as number];
},
},
});
this.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;
},
},
});
this.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

@ -0,0 +1,13 @@
uniform sampler2D u_texture;
varying vec2 v_texCoord;
varying float v_lightWeight;
void main() {
vec4 color = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y));
color.xyz = color.xyz * v_lightWeight;
gl_FragColor = color;
}

View File

@ -0,0 +1,52 @@
// attribute vec4 a_Color;
attribute vec3 a_Position;
attribute vec3 a_Normal;
attribute vec2 a_Uv;
varying vec2 v_texCoord;
// attribute vec2 a_Extrude;
// attribute float a_Size;
// attribute float a_Shape;
uniform vec3 u_CameraPosition;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ModelMatrix;
uniform float u_ambientRatio : 0.5;
uniform float u_diffuseRatio : 0.3;
uniform float u_specularRatio : 0.2;
uniform vec3 u_sunLight: [1.0, -10.5, 12.0];
float calc_lighting(vec4 pos) {
vec3 worldPos = vec3(pos * u_ModelMatrix);
vec3 worldNormal = a_Normal;
// cal light weight
vec3 viewDir = normalize(u_CameraPosition - worldPos);
vec3 lightDir = normalize(u_sunLight);
vec3 halfDir = normalize(viewDir+lightDir);
// lambert
float lambert = dot(worldNormal, lightDir);
// specular
float specular = pow(max(0.0, dot(worldNormal, halfDir)), 32.0);
//sum to light weight
float lightWeight = u_ambientRatio + u_diffuseRatio * lambert + u_specularRatio * specular;
return lightWeight;
}
varying float v_lightWeight;
void main() {
v_texCoord = a_Uv;
float lightWeight = calc_lighting(vec4(a_Position, 1.0));
v_lightWeight = lightWeight;
gl_Position = u_ViewProjectionMatrix * u_ModelMatrix * vec4(a_Position, 1.0);
}

View File

@ -9,6 +9,8 @@ import PointLayer from './point';
import PolygonLayer from './polygon'; import PolygonLayer from './polygon';
import RasterLayer from './raster'; import RasterLayer from './raster';
import EarthLayer from './earth';
// import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin'; // import ConfigSchemaValidationPlugin from './plugins/ConfigSchemaValidationPlugin';
import DataMappingPlugin from './plugins/DataMappingPlugin'; import DataMappingPlugin from './plugins/DataMappingPlugin';
import DataSourcePlugin from './plugins/DataSourcePlugin'; import DataSourcePlugin from './plugins/DataSourcePlugin';
@ -137,4 +139,5 @@ export {
ImageLayer, ImageLayer,
RasterLayer, RasterLayer,
HeatmapLayer, HeatmapLayer,
EarthLayer,
}; };

View File

@ -75,7 +75,8 @@ export default class ShaderUniformPlugin implements ILayerPlugin {
// 其他参数例如视口大小、DPR 等 // 其他参数例如视口大小、DPR 等
u_ViewportSize: [width, height], u_ViewportSize: [width, height],
u_DevicePixelRatio: window.devicePixelRatio, u_DevicePixelRatio: window.devicePixelRatio,
u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], // u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
u_ModelMatrix: this.cameraService.getModelMatrix(),
u_PickingBuffer: layer.getLayerConfig().pickingBuffer || 0, u_PickingBuffer: layer.getLayerConfig().pickingBuffer || 0,
}); });
}); });

View File

@ -15,17 +15,24 @@ import BaseModel, {
styleOffset, styleOffset,
styleSingle, styleSingle,
} from '../../core/BaseModel'; } from '../../core/BaseModel';
import { PointFillTriangulation } from '../../core/triangulation'; import {
GlobelPointFillTriangulation,
PointFillTriangulation,
} from '../../core/triangulation';
import pointFillFrag from '../shaders/fill_frag.glsl'; import pointFillFrag from '../shaders/fill_frag.glsl';
import pointFillVert from '../shaders/fill_vert.glsl'; import pointFillVert from '../shaders/fill_vert.glsl';
import { isNumber, isString } from 'lodash'; import { isNumber, isString } from 'lodash';
import { mat4, vec3 } from 'gl-matrix';
interface IPointLayerStyleOptions { interface IPointLayerStyleOptions {
opacity: styleSingle; opacity: styleSingle;
strokeWidth: styleSingle; strokeWidth: styleSingle;
stroke: styleColor; stroke: styleColor;
strokeOpacity: styleSingle; strokeOpacity: styleSingle;
offsets: styleOffset; offsets: styleOffset;
isGlobel?: boolean;
} }
// 判断当前使用的 style 中的变量属性是否需要进行数据映射 // 判断当前使用的 style 中的变量属性是否需要进行数据映射
export default class FillModel extends BaseModel { export default class FillModel extends BaseModel {
@ -36,6 +43,8 @@ export default class FillModel extends BaseModel {
strokeWidth = 0, strokeWidth = 0,
stroke = 'rgba(0,0,0,0)', stroke = 'rgba(0,0,0,0)',
offsets = [0, 0], offsets = [0, 0],
// TODO: 判断当前图层是否为地球模式
isGlobel = false,
} = this.layer.getLayerConfig() as IPointLayerStyleOptions; } = this.layer.getLayerConfig() as IPointLayerStyleOptions;
if ( if (
@ -85,6 +94,7 @@ export default class FillModel extends BaseModel {
}); });
} }
return { return {
u_globel: this.mapService.version === 'GLOBEL' ? 1 : 0,
u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1] u_dataTexture: this.dataTexture, // 数据纹理 - 有数据映射的时候纹理中带数据,若没有任何数据映射时纹理是 [1]
u_cellTypeLayout: this.getCellTypeLayout(), u_cellTypeLayout: this.getCellTypeLayout(),
@ -122,13 +132,18 @@ export default class FillModel extends BaseModel {
} }
public buildModels(): IModel[] { public buildModels(): IModel[] {
// TODO: 判断当前的点图层的模型是普通地图模式还是地球模式
const isGlobel = this.mapService.version === 'GLOBEL';
return [ return [
this.layer.buildLayerModel({ this.layer.buildLayerModel({
moduleName: 'pointfill', moduleName: 'pointfill',
vertexShader: pointFillVert, vertexShader: pointFillVert,
fragmentShader: pointFillFrag, fragmentShader: pointFillFrag,
triangulation: PointFillTriangulation, triangulation: isGlobel
depth: { enable: false }, ? GlobelPointFillTriangulation
: PointFillTriangulation,
// depth: { enable: false },
depth: { enable: isGlobel },
blend: this.getBlend(), blend: this.getBlend(),
}), }),
]; ];
@ -142,6 +157,9 @@ export default class FillModel extends BaseModel {
return [option.enable ? 0 : 1.0, option.speed || 1, option.rings || 3, 0]; return [option.enable ? 0 : 1.0, option.speed || 1, option.rings || 3, 0];
} }
protected registerBuiltinAttributes() { protected registerBuiltinAttributes() {
// TODO: 判断当前的点图层的模型是普通地图模式还是地球模式
const isGlobel = this.mapService.version === 'GLOBEL';
this.styleAttributeService.registerStyleAttribute({ this.styleAttributeService.registerStyleAttribute({
name: 'extrude', name: 'extrude',
type: AttributeType.Attribute, type: AttributeType.Attribute,
@ -153,16 +171,57 @@ export default class FillModel extends BaseModel {
data: [], data: [],
type: gl.FLOAT, type: gl.FLOAT,
}, },
size: 2, size: 3,
update: ( update: (
feature: IEncodeFeature, feature: IEncodeFeature,
featureIdx: number, featureIdx: number,
vertex: number[], vertex: number[],
attributeIdx: number, attributeIdx: number,
) => { ) => {
const extrude = [1, 1, -1, 1, -1, -1, 1, -1]; let extrude;
const extrudeIndex = (attributeIdx % 4) * 2; // 地球模式
return [extrude[extrudeIndex], extrude[extrudeIndex + 1]]; if (isGlobel) {
const [x, y, z] = vertex;
const n1 = vec3.fromValues(0, 0, 1);
const n2 = vec3.fromValues(x, 0, z);
const xzReg =
x >= 0 ? vec3.angle(n1, n2) : Math.PI * 2 - vec3.angle(n1, n2);
const yReg = Math.PI * 2 - Math.asin(y / 100);
const m = mat4.create();
mat4.rotateY(m, m, xzReg);
mat4.rotateX(m, m, yReg);
const v1 = vec3.fromValues(1, 1, 0);
vec3.transformMat4(v1, v1, m);
vec3.normalize(v1, v1);
const v2 = vec3.fromValues(-1, 1, 0);
vec3.transformMat4(v2, v2, m);
vec3.normalize(v2, v2);
const v3 = vec3.fromValues(-1, -1, 0);
vec3.transformMat4(v3, v3, m);
vec3.normalize(v3, v3);
const v4 = vec3.fromValues(1, -1, 0);
vec3.transformMat4(v4, v4, m);
vec3.normalize(v4, v4);
extrude = [...v1, ...v2, ...v3, ...v4];
} else {
// 平面模式
extrude = [1, 1, 0, -1, 1, 0, -1, -1, 0, 1, -1, 0];
}
const extrudeIndex = (attributeIdx % 4) * 3;
return [
extrude[extrudeIndex],
extrude[extrudeIndex + 1],
extrude[extrudeIndex + 2],
];
}, },
}, },
}); });

View File

@ -1,11 +1,12 @@
attribute vec4 a_Color; attribute vec4 a_Color;
attribute vec3 a_Position; attribute vec3 a_Position;
attribute vec2 a_Extrude; attribute vec3 a_Extrude;
attribute float a_Size; attribute float a_Size;
attribute float a_Shape; attribute float a_Shape;
varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样式值传递给片元 varying mat4 styleMappingMat; // 用于将在顶点着色器中计算好的样式值传递给片元
uniform float u_globel;
uniform mat4 u_ModelMatrix; uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp; uniform mat4 u_Mvp;
@ -29,7 +30,7 @@ uniform vec2 u_offsets;
void main() { void main() {
vec2 extrude = a_Extrude; vec3 extrude = a_Extrude;
float shape_type = a_Shape; float shape_type = a_Shape;
float newSize = setPickingSize(a_Size); float newSize = setPickingSize(a_Size);
@ -123,10 +124,11 @@ void main() {
float antialiasblur = 1.0 / u_DevicePixelRatio / (newSize + u_stroke_width); float antialiasblur = 1.0 / u_DevicePixelRatio / (newSize + u_stroke_width);
// construct point coords // construct point coords
v_data = vec4(extrude, antialiasblur,shape_type); // TODP: /abs(extrude.x) 是为了兼容地球模式
v_data = vec4(extrude.x/abs(extrude.x), extrude.y/abs(extrude.y), antialiasblur,shape_type);
// vec2 offset = project_pixel(extrude * (newSize + u_stroke_width) + u_offsets); // vec2 offset = project_pixel(extrude * (newSize + u_stroke_width) + u_offsets);
vec2 offset = project_pixel(extrude * (newSize + u_stroke_width) + textrueOffsets); vec2 offset = project_pixel(extrude.xy * (newSize + u_stroke_width) + textrueOffsets);
vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0)); vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0)); // gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0));
@ -136,6 +138,10 @@ void main() {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0)); gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0));
} }
if(u_globel > 0.0) {
gl_Position = u_ViewProjectionMatrix * vec4(a_Position + extrude * newSize * 0.1, 1.0);
}
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0)); // gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0));
setPickingColor(a_PickingColor); setPickingColor(a_PickingColor);

View File

@ -0,0 +1,375 @@
import { DOM } from '@antv/l7-utils';
import { merge } from 'lodash';
import Camera from './camera';
import './css/l7.css';
import LngLat, { LngLatLike } from './geo/lng_lat';
import LngLatBounds, { LngLatBoundsLike } from './geo/lng_lat_bounds';
// @ts-ignore
import Point, { PointLike } from './geo/point';
import BoxZoomHandler from './handler/box_zoom';
import HandlerManager from './handler/handler_manager';
import KeyboardHandler from './handler/keyboard';
import ScrollZoomHandler from './handler/scroll_zoom';
import DoubleClickZoomHandler from './handler/shim/dblclick_zoom';
import DragPanHandler from './handler/shim/drag_pan';
import DragRotateHandler from './handler/shim/drag_rotate';
import TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate';
import { TouchPitchHandler } from './handler/touch';
import Hash from './hash';
import { IMapOptions } from './interface';
import { renderframe } from './util';
import { PerformanceUtils } from './utils/performance';
import TaskQueue, { TaskID } from './utils/task_queue';
type CallBack = (_: number) => void;
const defaultMinZoom = -2;
const defaultMaxZoom = 22;
// the default values, but also the valid range
const defaultMinPitch = 0;
const defaultMaxPitch = 60;
const DefaultOptions: IMapOptions = {
hash: false,
zoom: -1,
center: [112, 32],
pitch: 0,
bearing: 0,
interactive: true,
minZoom: defaultMinZoom,
maxZoom: defaultMaxZoom,
minPitch: defaultMinPitch,
maxPitch: defaultMaxPitch,
scrollZoom: true,
boxZoom: true,
dragRotate: true,
dragPan: true,
keyboard: true,
doubleClickZoom: true,
touchZoomRotate: true,
touchPitch: true,
bearingSnap: 7,
clickTolerance: 3,
pitchWithRotate: true,
trackResize: true,
renderWorldCopies: true,
};
export class EarthMap extends Camera {
public doubleClickZoom: DoubleClickZoomHandler;
public dragRotate: DragRotateHandler;
public dragPan: DragPanHandler;
public touchZoomRotate: TouchZoomRotateHandler;
public scrollZoom: ScrollZoomHandler;
public keyboard: KeyboardHandler;
public touchPitch: TouchPitchHandler;
public boxZoom: BoxZoomHandler;
public handlers: HandlerManager;
private container: HTMLElement;
private canvas: HTMLCanvasElement;
private canvasContainer: HTMLElement;
private renderTaskQueue: TaskQueue = new TaskQueue();
private frame: { cancel: () => void } | null;
private trackResize: boolean = true;
private hash: Hash | undefined;
constructor(options: Partial<IMapOptions>) {
super(merge({}, DefaultOptions, options));
this.initContainer();
this.resize();
this.handlers = new HandlerManager(this, this.options);
if (typeof window !== 'undefined') {
window.addEventListener('online', this.onWindowOnline, false);
window.addEventListener('resize', this.onWindowResize, false);
window.addEventListener('orientationchange', this.onWindowResize, false);
}
const hashName =
(typeof options.hash === 'string' && options.hash) || undefined;
if (options.hash) {
this.hash = new Hash(hashName).addTo(this) as Hash;
}
// don't set position from options if set through hash
if (!this.hash || !this.hash.onHashChange()) {
this.jumpTo({
center: options.center,
zoom: options.zoom,
bearing: options.bearing,
pitch: options.pitch,
});
if (options.bounds) {
this.resize();
this.fitBounds(
options.bounds,
merge({}, options.fitBoundsOptions, { duration: 0 }),
);
}
}
}
public resize(eventData?: any) {
const dimensions = this.containerDimensions();
const width = dimensions[0];
const height = dimensions[1];
// this.resizeCanvas(width, height);
this.transform.resize(width, height);
const fireMoving = !this.moving;
if (fireMoving) {
this.stop();
this.emit('movestart', new Event('movestart', eventData));
this.emit('move', new Event('move', eventData));
}
this.emit('resize', new Event('resize', eventData));
if (fireMoving) {
this.emit('moveend', new Event('moveend', eventData));
}
return this;
}
public getContainer() {
return this.container;
}
public getCanvas() {
return this.canvas;
}
public getCanvasContainer() {
return this.canvasContainer;
}
public project(lngLat: LngLatLike) {
return this.transform.locationPoint(LngLat.convert(lngLat));
}
public unproject(point: PointLike) {
return this.transform.pointLocation(Point.convert(point));
}
public getBounds(): LngLatBounds {
return this.transform.getBounds();
}
public getMaxBounds(): LngLatBounds | null {
return this.transform.getMaxBounds();
}
public setMaxBounds(bounds: LngLatBoundsLike) {
this.transform.setMaxBounds(LngLatBounds.convert(bounds));
}
public setStyle(style: any) {
return;
}
public setMinZoom(minZoom?: number) {
minZoom =
minZoom === null || minZoom === undefined ? defaultMinZoom : minZoom;
if (minZoom >= defaultMinZoom && minZoom <= this.transform.maxZoom) {
this.transform.minZoom = minZoom;
if (this.getZoom() < minZoom) {
this.setZoom(minZoom);
}
return this;
} else {
throw new Error(
`minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive`,
);
}
}
public getMinZoom() {
return this.transform.minZoom;
}
public setMaxZoom(maxZoom?: number) {
maxZoom =
maxZoom === null || maxZoom === undefined ? defaultMaxZoom : maxZoom;
if (maxZoom >= this.transform.minZoom) {
this.transform.maxZoom = maxZoom;
if (this.getZoom() > maxZoom) {
this.setZoom(maxZoom);
}
return this;
} else {
throw new Error('maxZoom must be greater than the current minZoom');
}
}
public getMaxZoom() {
return this.transform.maxZoom;
}
public setMinPitch(minPitch?: number) {
minPitch =
minPitch === null || minPitch === undefined ? defaultMinPitch : minPitch;
if (minPitch < defaultMinPitch) {
throw new Error(
`minPitch must be greater than or equal to ${defaultMinPitch}`,
);
}
if (minPitch >= defaultMinPitch && minPitch <= this.transform.maxPitch) {
this.transform.minPitch = minPitch;
if (this.getPitch() < minPitch) {
this.setPitch(minPitch);
}
return this;
} else {
throw new Error(
`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`,
);
}
}
public getMinPitch() {
return this.transform.minPitch;
}
public setMaxPitch(maxPitch?: number) {
maxPitch =
maxPitch === null || maxPitch === undefined ? defaultMaxPitch : maxPitch;
if (maxPitch > defaultMaxPitch) {
throw new Error(
`maxPitch must be less than or equal to ${defaultMaxPitch}`,
);
}
if (maxPitch >= this.transform.minPitch) {
this.transform.maxPitch = maxPitch;
if (this.getPitch() > maxPitch) {
this.setPitch(maxPitch);
}
return this;
} else {
throw new Error('maxPitch must be greater than the current minPitch');
}
}
public getMaxPitch() {
return this.transform.maxPitch;
}
public getRenderWorldCopies() {
return this.transform.renderWorldCopies;
}
public setRenderWorldCopies(renderWorldCopies?: boolean) {
this.transform.renderWorldCopies = !!renderWorldCopies;
}
public remove() {
if (this.frame) {
this.frame.cancel();
this.frame = null;
}
this.renderTaskQueue.clear();
}
public requestRenderFrame(cb: CallBack): TaskID {
this.update();
return this.renderTaskQueue.add(cb);
}
public cancelRenderFrame(id: TaskID) {
return this.renderTaskQueue.remove(id);
}
public triggerRepaint() {
if (!this.frame) {
this.frame = renderframe((paintStartTimeStamp: number) => {
PerformanceUtils.frame(paintStartTimeStamp);
this.frame = null;
this.update(paintStartTimeStamp);
});
}
}
public update(time?: number) {
if (!this.frame) {
this.frame = renderframe((paintStartTimeStamp: number) => {
PerformanceUtils.frame(paintStartTimeStamp);
this.frame = null;
this.renderTaskQueue.run(time);
});
}
}
private initContainer() {
if (typeof this.options.container === 'string') {
this.container = window.document.getElementById(
this.options.container,
) as HTMLElement;
if (!this.container) {
throw new Error(`Container '${this.options.container}' not found.`);
}
} else if (this.options.container instanceof HTMLElement) {
this.container = this.options.container;
} else {
throw new Error(
"Invalid type: 'container' must be a String or HTMLElement.",
);
}
const container = this.container;
container.classList.add('l7-map');
const canvasContainer = (this.canvasContainer = DOM.create(
'div',
'l7-canvas-container',
container,
));
if (this.options.interactive) {
canvasContainer.classList.add('l7-interactive');
}
// this.canvas = DOM.create(
// 'canvas',
// 'l7-canvas',
// canvasContainer,
// ) as HTMLCanvasElement;
// this.canvas.setAttribute('tabindex', '-');
// this.canvas.setAttribute('aria-label', 'Map');
}
private containerDimensions(): [number, number] {
let width = 0;
let height = 0;
if (this.container) {
width = this.container.clientWidth || 400;
height = this.container.clientHeight || 300;
}
return [width, height];
}
private resizeCanvas(width: number, height: number) {
const pixelRatio = DOM.DPR || 1;
this.canvas.width = pixelRatio * width;
this.canvas.height = pixelRatio * height;
// Maintain the same canvas size, potentially downscaling it for HiDPI displays
this.canvas.style.width = `${width}px`;
this.canvas.style.height = `${height}px`;
}
private onWindowOnline = () => {
this.update();
};
private onWindowResize = (event: Event) => {
if (this.trackResize) {
this.resize({ originalEvent: event }).update();
}
};
}

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../earthmap';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
export interface IHandlerResult { export interface IHandlerResult {
@ -8,7 +9,7 @@ export interface IHandlerResult {
pitchDelta?: number; pitchDelta?: number;
around?: Point | null; around?: Point | null;
pinchAround?: Point | null; pinchAround?: Point | null;
cameraAnimation?: (map: Map) => any; cameraAnimation?: (map: Map | EarthMap) => any;
originalEvent?: any; originalEvent?: any;
// Makes the manager trigger a frame; allowing the handler to return multiple results over time (see scrollzoom). // Makes the manager trigger a frame; allowing the handler to return multiple results over time (see scrollzoom).
needsRenderFrame?: boolean; needsRenderFrame?: boolean;

View File

@ -1,18 +1,20 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../earthmap';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
import { MapMouseEvent, MapTouchEvent, MapWheelEvent } from './events'; import { MapMouseEvent, MapTouchEvent, MapWheelEvent } from './events';
export default class BlockableMapEventHandler { export default class BlockableMapEventHandler {
private map: Map; private map: Map | EarthMap;
private delayContextMenu: boolean; private delayContextMenu: boolean;
private contextMenuEvent: MouseEvent; private contextMenuEvent: MouseEvent;
constructor(map: Map) { constructor(map: Map | EarthMap) {
this.map = map; this.map = map;
} }
public reset() { public reset() {
this.delayContextMenu = false; this.delayContextMenu = false;
// @ts-ignore
delete this.contextMenuEvent; delete this.contextMenuEvent;
} }
@ -32,6 +34,7 @@ export default class BlockableMapEventHandler {
'contextmenu', 'contextmenu',
new MapMouseEvent('contextmenu', this.map, this.contextMenuEvent), new MapMouseEvent('contextmenu', this.map, this.contextMenuEvent),
); );
// @ts-ignore
delete this.contextMenuEvent; delete this.contextMenuEvent;
} }
} }

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../earthmap';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
import DOM from '../utils/dom'; import DOM from '../utils/dom';
@ -9,7 +10,7 @@ import { Event } from './events/event';
* The bounding box is defined by clicking and holding `shift` while dragging the cursor. * The bounding box is defined by clicking and holding `shift` while dragging the cursor.
*/ */
class BoxZoomHandler { class BoxZoomHandler {
private map: Map; private map: Map | EarthMap;
private el: HTMLElement; private el: HTMLElement;
private container: HTMLElement; private container: HTMLElement;
private enabled: boolean; private enabled: boolean;
@ -23,7 +24,7 @@ class BoxZoomHandler {
* @private * @private
*/ */
constructor( constructor(
map: Map, map: Map | EarthMap,
options: { options: {
clickTolerance: number; clickTolerance: number;
}, },
@ -179,8 +180,9 @@ class BoxZoomHandler {
} }
DOM.enableDrag(); DOM.enableDrag();
// @ts-ignore
delete this.startPos; delete this.startPos;
// @ts-ignore
delete this.lastPos; delete this.lastPos;
} }

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../earthmap';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
@ -17,7 +18,7 @@ export default class ClickZoomHandler {
public dblclick(e: MouseEvent, point: Point) { public dblclick(e: MouseEvent, point: Point) {
e.preventDefault(); e.preventDefault();
return { return {
cameraAnimation: (map: Map) => { cameraAnimation: (map: Map | EarthMap) => {
map.easeTo( map.easeTo(
{ {
duration: 300, duration: 300,

View File

@ -1,6 +1,7 @@
// @ts-ignore // @ts-ignore
// tslint:disable-next-line:no-submodule-imports // tslint:disable-next-line:no-submodule-imports
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import { EarthMap } from '../../earthmap';
import LngLat from '../../geo/lng_lat'; import LngLat from '../../geo/lng_lat';
import Point from '../../geo/point'; import Point from '../../geo/point';
import { Map } from '../../map'; import { Map } from '../../map';
@ -27,7 +28,7 @@ export default class MapMouseEvent extends Event {
/** /**
* The `Map` object that fired the event. * The `Map` object that fired the event.
*/ */
public target: Map; public target: Map | EarthMap;
/** /**
* The DOM event which caused the map event. * The DOM event which caused the map event.
@ -51,7 +52,7 @@ export default class MapMouseEvent extends Event {
*/ */
constructor( constructor(
type: string, type: string,
map: Map, map: Map | EarthMap,
originalEvent: MouseEvent, originalEvent: MouseEvent,
data: any = {}, data: any = {},
) { ) {

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../../earthmap';
import LngLat from '../../geo/lng_lat'; import LngLat from '../../geo/lng_lat';
import Point from '../../geo/point'; import Point from '../../geo/point';
import { Map } from '../../map'; import { Map } from '../../map';
@ -13,7 +14,7 @@ export default class MapTouchEvent extends Event {
/** /**
* The `Map` object that fired the event. * The `Map` object that fired the event.
*/ */
public target: Map; public target: Map | EarthMap;
/** /**
* The DOM event which caused the map event. * The DOM event which caused the map event.
@ -53,7 +54,7 @@ export default class MapTouchEvent extends Event {
/** /**
* @private * @private
*/ */
constructor(type: string, map: Map, originalEvent: TouchEvent) { constructor(type: string, map: Map | EarthMap, originalEvent: TouchEvent) {
const touches = const touches =
type === 'touchend' type === 'touchend'
? originalEvent.changedTouches ? originalEvent.changedTouches

View File

@ -1,9 +1,10 @@
import { EarthMap } from '../../earthmap';
import { Map } from '../../map'; import { Map } from '../../map';
import { Event } from './event'; import { Event } from './event';
export interface IMapBoxZoomEvent { export interface IMapBoxZoomEvent {
type: 'boxzoomstart' | 'boxzoomend' | 'boxzoomcancel'; type: 'boxzoomstart' | 'boxzoomend' | 'boxzoomcancel';
target: Map; target: Map | EarthMap;
originalEvent: MouseEvent; originalEvent: MouseEvent;
} }
export default class MapWheelEvent extends Event { export default class MapWheelEvent extends Event {
@ -22,12 +23,12 @@ export default class MapWheelEvent extends Event {
/** /**
* The `Map` object that fired the event. * The `Map` object that fired the event.
*/ */
public target: Map; public target: Map | EarthMap;
/** /**
* @private * @private
*/ */
constructor(type: string, map: Map, originalEvent: WheelEvent) { constructor(type: string, map: Map | EarthMap, originalEvent: WheelEvent) {
super(type, { originalEvent }); super(type, { originalEvent });
this.defaultPrevented = false; this.defaultPrevented = false;
} }

View File

@ -3,6 +3,7 @@ import Point from '../geo/point';
// tslint:disable-next-line:no-submodule-imports // tslint:disable-next-line:no-submodule-imports
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import { EarthMap } from '../earthmap';
import { Map } from '../map'; import { Map } from '../map';
import { bezier, clamp, now } from '../util'; import { bezier, clamp, now } from '../util';
import { IDragPanOptions } from './shim/drag_pan'; import { IDragPanOptions } from './shim/drag_pan';
@ -54,13 +55,13 @@ export interface IInertiaOptions {
export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent; export type InputEvent = MouseEvent | TouchEvent | KeyboardEvent | WheelEvent;
export default class HandlerInertia { export default class HandlerInertia {
private map: Map; private map: Map | EarthMap;
private inertiaBuffer: Array<{ private inertiaBuffer: Array<{
time: number; time: number;
settings: { [key: string]: any }; settings: { [key: string]: any };
}>; }>;
constructor(map: Map) { constructor(map: Map | EarthMap) {
this.map = map; this.map = map;
this.clear(); this.clear();
} }

View File

@ -1,6 +1,7 @@
// @ts-ignore // @ts-ignore
// tslint:disable-next-line: no-submodule-imports // tslint:disable-next-line: no-submodule-imports
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import { EarthMap } from '../earthmap';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
import DOM from '../utils/dom'; import DOM from '../utils/dom';
@ -62,7 +63,7 @@ export interface IHandlerOptions {
} }
class HandlerManager { class HandlerManager {
private map: Map; private map: Map | EarthMap;
private el: HTMLElement; private el: HTMLElement;
private handlers: Array<{ private handlers: Array<{
handlerName: string; handlerName: string;
@ -82,7 +83,7 @@ class HandlerManager {
[HTMLElement, string, void | { passive?: boolean; capture?: boolean }] [HTMLElement, string, void | { passive?: boolean; capture?: boolean }]
>; >;
constructor(map: Map, options: IHandlerOptions) { constructor(map: Map | EarthMap, options: IHandlerOptions) {
this.map = map; this.map = map;
this.el = this.map.getCanvasContainer(); this.el = this.map.getCanvasContainer();
this.handlers = []; this.handlers = [];

View File

@ -1,3 +1,4 @@
import { EarthMap } from '../earthmap';
import { Map } from '../map'; import { Map } from '../map';
const defaultOptions = { const defaultOptions = {
@ -106,7 +107,7 @@ class KeyboardHandler {
} }
return { return {
cameraAnimation: (map: Map) => { cameraAnimation: (map: Map | EarthMap) => {
const zoom = map.getZoom(); const zoom = map.getZoom();
map.easeTo( map.easeTo(
{ {

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../earthmap';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
import { MapMouseEvent, MapTouchEvent, MapWheelEvent } from './events'; import { MapMouseEvent, MapTouchEvent, MapWheelEvent } from './events';
@ -6,14 +7,15 @@ import { MapMouseEvent, MapTouchEvent, MapWheelEvent } from './events';
export default class MapEventHandler { export default class MapEventHandler {
private mousedownPos: Point; private mousedownPos: Point;
private clickTolerance: number; private clickTolerance: number;
private map: Map; private map: Map | EarthMap;
constructor(map: Map, options: { clickTolerance: number }) { constructor(map: Map | EarthMap, options: { clickTolerance: number }) {
this.map = map; this.map = map;
this.clickTolerance = options.clickTolerance; this.clickTolerance = options.clickTolerance;
} }
public reset() { public reset() {
// @ts-ignore
delete this.mousedownPos; delete this.mousedownPos;
} }

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../earthmap';
import LngLat from '../geo/lng_lat'; import LngLat from '../geo/lng_lat';
import Point from '../geo/point'; import Point from '../geo/point';
import { Map } from '../map'; import { Map } from '../map';
@ -22,7 +23,7 @@ const maxScalePerFrame = 2;
* The `ScrollZoomHandler` allows the user to zoom the map by scrolling. * The `ScrollZoomHandler` allows the user to zoom the map by scrolling.
*/ */
class ScrollZoomHandler { class ScrollZoomHandler {
private map: Map; private map: Map | EarthMap;
private el: HTMLElement; private el: HTMLElement;
private enabled: boolean; private enabled: boolean;
private active: boolean; private active: boolean;
@ -57,7 +58,7 @@ class ScrollZoomHandler {
/** /**
* @private * @private
*/ */
constructor(map: Map, handler: HandlerManager) { constructor(map: Map | EarthMap, handler: HandlerManager) {
this.map = map; this.map = map;
this.el = map.getCanvasContainer(); this.el = map.getCanvasContainer();
this.handler = handler; this.handler = handler;
@ -287,7 +288,9 @@ class ScrollZoomHandler {
this.finishTimeout = setTimeout(() => { this.finishTimeout = setTimeout(() => {
this.zooming = false; this.zooming = false;
this.handler.triggerRenderFrame(); this.handler.triggerRenderFrame();
// @ts-ignore
delete this.targetZoom; delete this.targetZoom;
// @ts-ignore
delete this.finishTimeout; delete this.finishTimeout;
}, 200); }, 200);
} }
@ -325,6 +328,7 @@ class ScrollZoomHandler {
if (this.finishTimeout) { if (this.finishTimeout) {
clearTimeout(this.finishTimeout); clearTimeout(this.finishTimeout);
// @ts-ignore
delete this.finishTimeout; delete this.finishTimeout;
} }

View File

@ -1,4 +1,5 @@
// @ts-ignore // @ts-ignore
import { EarthMap } from '../../earthmap';
import Point from '../../geo/point'; import Point from '../../geo/point';
import { Map } from '../../map'; import { Map } from '../../map';
import TapRecognizer from './tap_recognizer'; import TapRecognizer from './tap_recognizer';
@ -48,7 +49,7 @@ export default class TapZoomHandler {
e.preventDefault(); e.preventDefault();
setTimeout(() => this.reset(), 0); setTimeout(() => this.reset(), 0);
return { return {
cameraAnimation: (map: Map) => cameraAnimation: (map: Map | EarthMap) =>
map.easeTo( map.easeTo(
{ {
duration: 300, duration: 300,
@ -63,7 +64,7 @@ export default class TapZoomHandler {
e.preventDefault(); e.preventDefault();
setTimeout(() => this.reset(), 0); setTimeout(() => this.reset(), 0);
return { return {
cameraAnimation: (map: Map) => cameraAnimation: (map: Map | EarthMap) =>
map.easeTo( map.easeTo(
{ {
duration: 300, duration: 300,

View File

@ -1,6 +1,7 @@
// @ts-ignore // @ts-ignore
// tslint:disable-next-line:no-submodule-imports // tslint:disable-next-line:no-submodule-imports
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import { EarthMap } from './earthmap';
import { Map } from './map'; import { Map } from './map';
/* /*
@ -10,7 +11,7 @@ import { Map } from './map';
* @returns {Hash} `this` * @returns {Hash} `this`
*/ */
class Hash { class Hash {
private map: Map; private map: Map | EarthMap;
private updateHash: () => number | void; private updateHash: () => number | void;
private hashName?: string; private hashName?: string;
@ -20,17 +21,19 @@ class Hash {
// Mobile Safari doesn't allow updating the hash more than 100 times per 30 seconds. // Mobile Safari doesn't allow updating the hash more than 100 times per 30 seconds.
this.updateHash = throttle(this.updateHashUnthrottled, (30 * 1000) / 100); this.updateHash = throttle(this.updateHashUnthrottled, (30 * 1000) / 100);
} }
public addTo(map: Map) { public addTo(map: Map | EarthMap) {
this.map = map; this.map = map;
window.addEventListener('hashchange', this.onHashChange, false); window.addEventListener('hashchange', this.onHashChange, false);
// @ts-ignore
this.map.on('moveend', this.updateHash); this.map.on('moveend', this.updateHash);
return this; return this;
} }
public remove() { public remove() {
window.removeEventListener('hashchange', this.onHashChange, false); window.removeEventListener('hashchange', this.onHashChange, false);
// @ts-ignore
this.map.off('moveend', this.updateHash); this.map.off('moveend', this.updateHash);
// clearTimeout(this.updateHash()); // clearTimeout(this.updateHash());
// @ts-ignore
delete this.map; delete this.map;
return this; return this;
} }

View File

@ -1 +1,2 @@
export * from './map'; export * from './map';
export * from './earthmap';

View File

@ -85,6 +85,10 @@ export default class Viewport implements IViewport {
return this.projectionMatrix; return this.projectionMatrix;
} }
public getModelMatrix(): number[] {
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
}
public getViewMatrix(): number[] { public getViewMatrix(): number[] {
// @ts-ignore // @ts-ignore
return this.viewMatrix; return this.viewMatrix;

View File

@ -97,6 +97,10 @@ export default class Viewport implements IViewport {
return this.projectionMatrix; return this.projectionMatrix;
} }
public getModelMatrix(): number[] {
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
}
public getViewMatrix(): number[] { public getViewMatrix(): number[] {
// @ts-ignore // @ts-ignore
return this.viewMatrix; return this.viewMatrix;

View File

@ -0,0 +1,143 @@
import { IMapCamera, IViewport } from '@antv/l7-core';
import { mat4, vec3 } from 'gl-matrix';
import WebMercatorViewport from 'viewport-mercator-project';
export default class Viewport implements IViewport {
// TODO: 初始化相机的姿态 看向地球
private xzReg: number = -Math.PI * 0.6;
private yReg: number = Math.PI * 0.2;
// 默认的地球相机半径、地球相机缩放层级
private earthCameraRadius: number = 200;
private earthCameraZoom: number = 1;
private cameraPosition: vec3 = vec3.create();
private viewport: WebMercatorViewport;
private projectionMatrix: mat4 = mat4.create();
private modelMatrix: mat4 = mat4.create();
private viewMatrix: mat4 = mat4.create();
private viewProjectionMatrix: mat4 = mat4.create();
private ViewProjectionMatrixUncentered: mat4 = mat4.create();
private viewUncenteredMatrix: mat4 = mat4.create();
public syncWithMapCamera(mapCamera: Partial<IMapCamera>) {
const { viewportHeight = 1, viewportWidth = 1 } = mapCamera;
const aspect = viewportWidth / viewportHeight;
const near = 0.1;
const far = 10000;
const fov = 20;
// 计算透视投影矩阵 projectionMatrix
mat4.perspective(this.projectionMatrix, fov, aspect, near, far);
// 计算相机矩阵 viewMatrix
const x = this.earthCameraRadius * Math.cos(this.xzReg);
const z = this.earthCameraRadius * Math.sin(this.xzReg);
const y = this.earthCameraRadius * Math.sin(this.yReg);
this.cameraPosition = vec3.fromValues(x, y, z);
vec3.normalize(this.cameraPosition, this.cameraPosition);
vec3.multiply(
this.cameraPosition,
this.cameraPosition,
vec3.fromValues(
this.earthCameraRadius,
this.earthCameraRadius,
this.earthCameraRadius,
),
);
vec3.scale(this.cameraPosition, this.cameraPosition, this.earthCameraZoom);
const crossY = vec3.create();
vec3.cross(crossY, this.cameraPosition, vec3.fromValues(0, 1, 0));
const up = vec3.fromValues(0, 1, 0);
vec3.cross(up, crossY, this.cameraPosition);
const target = vec3.fromValues(0, 0, 0);
mat4.lookAt(this.viewMatrix, this.cameraPosition, target, up);
this.viewUncenteredMatrix = mat4.clone(this.viewMatrix);
mat4.multiply(
this.viewProjectionMatrix,
this.projectionMatrix,
this.viewMatrix,
);
mat4.multiply(
this.ViewProjectionMatrixUncentered,
this.projectionMatrix,
this.viewMatrix,
);
}
/**
* Y
* @param r
*/
public rotateY(r: number) {
this.xzReg += r * Math.min(this.earthCameraZoom * this.earthCameraZoom, 1);
}
/**
* X
* @param r
*/
public rotateX(r: number) {
this.yReg += r * Math.min(this.earthCameraZoom * this.earthCameraZoom, 1);
}
/**
*
* @param z
*/
public scaleZoom(z: number) {
this.earthCameraZoom += z;
this.earthCameraZoom = Math.max(this.earthCameraZoom, 0.6);
}
public getZoom(): number {
return 4;
}
public getZoomScale(): number {
return Math.pow(2, this.getZoom());
}
public getCenter(): [number, number] {
return [0, 0];
}
public getProjectionMatrix(): number[] {
return this.projectionMatrix as number[];
}
public getModelMatrix(): number[] {
return this.modelMatrix as number[];
}
public getViewMatrix(): number[] {
return this.viewMatrix as number[];
}
public getViewMatrixUncentered(): number[] {
return this.viewMatrix as number[];
}
public getViewProjectionMatrix(): number[] {
return this.viewProjectionMatrix as number[];
}
public getViewProjectionMatrixUncentered(): number[] {
return this.viewProjectionMatrix as number[];
}
public getFocalDistance() {
return 1;
}
public projectFlat(
lngLat: [number, number],
scale?: number | undefined,
): [number, number] {
return this.viewport.projectFlat(lngLat, scale);
}
}

View File

@ -0,0 +1,8 @@
import { Map } from '@antv/l7-map';
import BaseMapWrapper from '../BaseMapWrapper';
import MapService from './map';
export default class MapboxWrapper extends BaseMapWrapper<Map> {
protected getServiceConstructor() {
return MapService;
}
}

View File

@ -0,0 +1,413 @@
/**
* MapboxService
*/
import {
Bounds,
CoordinateSystem,
ICoordinateSystemService,
IGlobalConfigService,
ILngLat,
IMapConfig,
IMapService,
IMercator,
IPoint,
IStatusOptions,
IViewport,
MapServiceEvent,
MapStyle,
TYPES,
} from '@antv/l7-core';
import { EarthMap, Map } from '@antv/l7-map';
import { DOM } from '@antv/l7-utils';
import { inject, injectable } from 'inversify';
import 'reflect-metadata';
import { Version } from '../version';
import Viewport from './Viewport';
const EventMap: {
[key: string]: any;
} = {
mapmove: 'move',
camerachange: 'move',
zoomchange: 'zoom',
dragging: 'drag',
};
import { MapTheme } from './theme';
const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12;
/**
* AMapService
*/
@injectable()
export default class L7MapService implements IMapService<Map> {
public version: string = Version.GLOBEL;
public map: Map;
@inject(TYPES.MapConfig)
private readonly config: Partial<IMapConfig>;
@inject(TYPES.IGlobalConfigService)
private readonly configService: IGlobalConfigService;
@inject(TYPES.ICoordinateSystemService)
private readonly coordinateSystemService: ICoordinateSystemService;
@inject(TYPES.IEventEmitter)
private eventEmitter: any;
private viewport: Viewport;
private markerContainer: HTMLElement;
private cameraChangedCallback: (viewport: IViewport) => void;
private $mapContainer: HTMLElement | null;
// T: 用于记录鼠标对相机的控制
private handleCameraChanging: boolean;
private handleCameraTimer: any;
// init
public addMarkerContainer(): void {
const container = this.map.getCanvasContainer();
this.markerContainer = DOM.create('div', 'l7-marker-container', container);
this.markerContainer.setAttribute('tabindex', '-1');
}
public getMarkerContainer(): HTMLElement {
return this.markerContainer;
}
// map event
public on(type: string, handle: (...args: any[]) => void): void {
if (MapServiceEvent.indexOf(type) !== -1) {
this.eventEmitter.on(type, handle);
} else {
// 统一事件名称
this.map.on(EventMap[type] || type, handle);
}
}
public off(type: string, handle: (...args: any[]) => void): void {
this.map.off(EventMap[type] || type, handle);
}
public getContainer(): HTMLElement | null {
return this.map.getContainer();
}
public getMapCanvasContainer(): HTMLElement {
return this.map.getCanvasContainer() as HTMLElement;
}
public getSize(): [number, number] {
const size = this.map.transform;
return [size.width, size.height];
}
// get mapStatus method
public getType() {
return 'default';
}
public getZoom(): number {
return this.map.getZoom();
}
public setZoom(zoom: number) {
return this.map.setZoom(zoom);
}
public getCenter(): ILngLat {
return this.map.getCenter();
}
public setCenter(lnglat: [number, number]): void {
this.map.setCenter(lnglat);
}
public getPitch(): number {
return this.map.getPitch();
}
public getRotation(): number {
return this.map.getBearing();
}
public getBounds(): Bounds {
return this.map.getBounds().toArray() as Bounds;
}
public getMinZoom(): number {
return this.map.getMinZoom();
}
public getMaxZoom(): number {
return this.map.getMaxZoom();
}
public setRotation(rotation: number): void {
this.map.setBearing(rotation);
}
public zoomIn(option?: any, eventData?: any): void {
this.map.zoomIn(option, eventData);
}
public zoomOut(option?: any, eventData?: any): void {
this.map.zoomOut(option, eventData);
}
public setPitch(pitch: number) {
return this.map.setPitch(pitch);
}
public panTo(p: [number, number]): void {
this.map.panTo(p);
}
public panBy(pixel: [number, number]): void {
this.panTo(pixel);
}
public fitBounds(bound: Bounds, fitBoundsOptions?: any): void {
this.map.fitBounds(bound, fitBoundsOptions);
}
public setMaxZoom(max: number): void {
this.map.setMaxZoom(max);
}
public setMinZoom(min: number): void {
this.map.setMinZoom(min);
}
public setMapStatus(option: Partial<IStatusOptions>): void {
if (option.doubleClickZoom === true) {
this.map.doubleClickZoom.enable();
}
if (option.doubleClickZoom === false) {
this.map.doubleClickZoom.disable();
}
if (option.dragEnable === false) {
this.map.dragPan.disable();
}
if (option.dragEnable === true) {
this.map.dragPan.enable();
}
if (option.rotateEnable === false) {
this.map.dragRotate.disable();
}
if (option.dragEnable === true) {
this.map.dragRotate.enable();
}
if (option.keyboardEnable === false) {
this.map.keyboard.disable();
}
if (option.keyboardEnable === true) {
this.map.keyboard.enable();
}
if (option.zoomEnable === false) {
this.map.scrollZoom.disable();
}
if (option.zoomEnable === true) {
this.map.scrollZoom.enable();
}
}
public setZoomAndCenter(zoom: number, center: [number, number]): void {
this.map.flyTo({
zoom,
center,
});
}
public setMapStyle(style: any): void {
this.map.setStyle(this.getMapStyle(style));
}
// TODO: 计算像素坐标
public pixelToLngLat(pixel: [number, number]): ILngLat {
return this.map.unproject(pixel);
}
public lngLatToPixel(lnglat: [number, number]): IPoint {
return this.map.project(lnglat);
}
public containerToLngLat(pixel: [number, number]): ILngLat {
return this.map.unproject(pixel);
}
public lngLatToContainer(lnglat: [number, number]): IPoint {
return this.map.project(lnglat);
}
public lngLatToMercator(
lnglat: [number, number],
altitude: number,
): IMercator {
throw new Error('not implement');
}
public getModelMatrix(
lnglat: [number, number],
altitude: number,
rotate: [number, number, number],
scale: [number, number, number] = [1, 1, 1],
origin: IMercator = { x: 0, y: 0, z: 0 },
): number[] {
throw new Error('not implement');
}
public async init(): Promise<void> {
const {
id = 'map',
attributionControl = false,
style = 'light',
rotation = 0,
mapInstance,
...rest
} = this.config;
this.viewport = new Viewport();
this.$mapContainer = this.creatAmapContainer(id);
// @ts-ignore
this.map = new EarthMap({
container: this.$mapContainer,
style: this.getMapStyle(style),
bearing: rotation,
...rest,
});
this.map.on('load', this.handleCameraChanged);
this.map.on('move', this.handleCameraChanged);
// 不同于高德地图,需要手动触发首次渲染
this.handleCameraChanged({});
}
public destroy() {
// TODO: 销毁地图可视化层的容器
this.$mapContainer?.parentNode?.removeChild(this.$mapContainer);
this.eventEmitter.removeAllListeners();
if (this.map) {
this.map.remove();
this.$mapContainer = null;
}
}
public emit(name: string, ...args: any[]) {
this.eventEmitter.emit(name, ...args);
}
public once(name: string, ...args: any[]) {
this.eventEmitter.once(name, ...args);
}
public getMapContainer() {
return this.$mapContainer;
}
public exportMap(type: 'jpg' | 'png'): string {
const renderCanvas = this.map.getCanvas();
const layersPng =
type === 'jpg'
? (renderCanvas?.toDataURL('image/jpeg') as string)
: (renderCanvas?.toDataURL('image/png') as string);
return layersPng;
}
public onCameraChanged(callback: (viewport: IViewport) => void): void {
this.cameraChangedCallback = callback;
}
/**
* Y
* @returns
*/
public rotateY(option: { force: boolean; reg: number }) {
const { force = false, reg = 0.01 } = option || {};
// TODO: 让旋转方法与
if (this.handleCameraChanging && !force) {
return;
}
if (this.viewport) {
this.viewport.rotateY(reg);
this.viewport.syncWithMapCamera({
bearing: this.map.getBearing(),
viewportHeight: this.map.transform.height,
pitch: this.map.getPitch(),
viewportWidth: this.map.transform.width,
zoom: this.map.getZoom(),
// mapbox 中固定相机高度为 viewport 高度的 1.5 倍
cameraHeight: 0,
});
}
}
private handleCameraChanged = (e: any) => {
const DELAY_TIME = 2000;
this.handleCameraChanging = true;
if (this.handleCameraTimer) {
clearTimeout(this.handleCameraTimer);
}
this.handleCameraTimer = setTimeout(() => {
this.handleCameraChanging = false;
}, DELAY_TIME);
// 定义鼠标相机控制
const rotateStep = 0.02;
if (e.type && e.originalEvent) {
if (e.originalEvent.type === 'wheel') {
this.viewport.scaleZoom(
0.01 * Math.sign(e.originalEvent.wheelDelta) * -1,
);
}
if (
Math.abs(e.originalEvent.movementX) >
Math.abs(e.originalEvent.movementY)
) {
if (e.originalEvent.movementX > 0) {
this.viewport.rotateY(rotateStep);
} else if (e.originalEvent.movementX < 0) {
this.viewport.rotateY(-rotateStep);
}
} else {
if (e.originalEvent.movementY > 0) {
this.viewport.rotateX(rotateStep);
} else if (e.originalEvent.movementY < 0) {
this.viewport.rotateX(-rotateStep);
}
}
}
const { offsetCoordinate = true } = this.config;
// resync
this.viewport.syncWithMapCamera({
bearing: this.map.getBearing(),
viewportHeight: this.map.transform.height,
pitch: this.map.getPitch(),
viewportWidth: this.map.transform.width,
zoom: this.map.getZoom(),
// mapbox 中固定相机高度为 viewport 高度的 1.5 倍
cameraHeight: 0,
});
// set coordinate system
if (
this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD &&
offsetCoordinate
) {
this.coordinateSystemService.setCoordinateSystem(
CoordinateSystem.LNGLAT_OFFSET,
);
} else {
this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.LNGLAT);
}
this.cameraChangedCallback(this.viewport);
};
private creatAmapContainer(id: string | HTMLDivElement) {
let $wrapper = id as HTMLDivElement;
if (typeof id === 'string') {
$wrapper = document.getElementById(id) as HTMLDivElement;
}
return $wrapper;
}
private getMapStyle(name: MapStyle) {
if (typeof name !== 'string') {
return name;
}
return MapTheme[name] ? MapTheme[name] : name;
}
}

View File

@ -0,0 +1,23 @@
export const MapTheme: {
[key: string]: any;
} = {
light: 'mapbox://styles/zcxduo/ck2ypyb1r3q9o1co1766dex29',
dark: 'mapbox://styles/zcxduo/ck241p6413s0b1cpayzldv7x7',
normal: 'mapbox://styles/mapbox/streets-v11',
blank: {
version: 8,
// sprite: 'https://lzxue.github.io/font-glyphs/sprite/sprite',
// glyphs:
// 'https://gw.alipayobjects.com/os/antvdemo/assets/mapbox/glyphs/{fontstack}/{range}.pbf',
sources: {},
layers: [
{
id: 'background',
type: 'background',
layout: {
visibility: 'none',
},
},
],
},
};

View File

@ -2,7 +2,8 @@
import GaodeMap from './amap/'; import GaodeMap from './amap/';
// import GaodeMapV1 from './amap/'; // import GaodeMapV1 from './amap/';
import GaodeMapV2 from './amap2/'; import GaodeMapV2 from './amap2/';
import Earth from './earth/';
import Map from './map/'; import Map from './map/';
import Mapbox from './mapbox/'; import Mapbox from './mapbox/';
export { GaodeMap, GaodeMapV2, Mapbox, Map }; export { GaodeMap, GaodeMapV2, Mapbox, Map, Earth };

View File

@ -45,6 +45,10 @@ export default class Viewport implements IViewport {
return this.viewport.projectionMatrix; return this.viewport.projectionMatrix;
} }
public getModelMatrix(): number[] {
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
}
public getViewMatrix(): number[] { public getViewMatrix(): number[] {
return this.viewport.viewMatrix; return this.viewport.viewMatrix;
} }

View File

@ -45,6 +45,10 @@ export default class Viewport implements IViewport {
return this.viewport.projectionMatrix; return this.viewport.projectionMatrix;
} }
public getModelMatrix(): number[] {
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
}
public getViewMatrix(): number[] { public getViewMatrix(): number[] {
return this.viewport.viewMatrix; return this.viewport.viewMatrix;
} }

View File

@ -6,4 +6,5 @@ export enum Version {
'GAODE2.x' = 'GAODE2.x', 'GAODE2.x' = 'GAODE2.x',
'MAPBOX' = 'MAPBOX', 'MAPBOX' = 'MAPBOX',
'L7MAP' = 'L7MAP', 'L7MAP' = 'L7MAP',
'GLOBEL' = 'GLOBEL',
} }

View File

@ -0,0 +1,409 @@
// @ts-ignore
import { Scene } from '@antv/l7';
import { PointLayer, EarthLayer } from '@antv/l7-layers';
import { Earth } from '@antv/l7-maps';
import * as React from 'react';
export default class ScaleComponent extends React.Component {
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new Earth({
center: [120, 30],
pitch: 0,
zoom: 3,
}),
});
var d = [
{ lng: 127.657407, lat: 49.76027 },
{ lng: 129.397818, lat: 49.4406 },
{ lng: 130.582293, lat: 48.729687 },
{ lng: 130.987282, lat: 47.790132 },
{ lng: 132.506672, lat: 47.78897 },
{ lng: 133.373596, lat: 48.183442 },
{ lng: 135.026311, lat: 48.47823 },
{ lng: 134.500814, lat: 47.57844 },
{ lng: 134.112362, lat: 47.212467 },
{ lng: 133.769644, lat: 46.116927 },
{ lng: 133.097127, lat: 45.144066 },
{ lng: 131.883454, lat: 45.321162 },
{ lng: 131.025212, lat: 44.967953 },
{ lng: 131.288555, lat: 44.11152 },
{ lng: 131.144688, lat: 42.92999 },
{ lng: 130.633866, lat: 42.903015 },
{ lng: 130.640016, lat: 42.395009 },
{ lng: 129.994267, lat: 42.985387 },
{ lng: 129.596669, lat: 42.424982 },
{ lng: 128.052215, lat: 41.994285 },
{ lng: 128.208433, lat: 41.466772 },
{ lng: 127.343783, lat: 41.503152 },
{ lng: 126.869083, lat: 41.816569 },
{ lng: 126.182045, lat: 41.107336 },
{ lng: 125.079942, lat: 40.569824 },
{ lng: 124.265625, lat: 39.928493 },
{ lng: 122.86757, lat: 39.637788 },
{ lng: 122.131388, lat: 39.170452 },
{ lng: 121.054554, lat: 38.897471 },
{ lng: 121.585995, lat: 39.360854 },
{ lng: 121.376757, lat: 39.750261 },
{ lng: 122.168595, lat: 40.422443 },
{ lng: 121.640359, lat: 40.94639 },
{ lng: 120.768629, lat: 40.593388 },
{ lng: 119.639602, lat: 39.898056 },
{ lng: 119.023464, lat: 39.252333 },
{ lng: 118.042749, lat: 39.204274 },
{ lng: 117.532702, lat: 38.737636 },
{ lng: 118.059699, lat: 38.061476 },
{ lng: 118.87815, lat: 37.897325 },
{ lng: 118.911636, lat: 37.448464 },
{ lng: 119.702802, lat: 37.156389 },
{ lng: 120.823457, lat: 37.870428 },
{ lng: 121.711259, lat: 37.481123 },
{ lng: 122.357937, lat: 37.454484 },
{ lng: 122.519995, lat: 36.930614 },
{ lng: 121.104164, lat: 36.651329 },
{ lng: 120.637009, lat: 36.11144 },
{ lng: 119.664562, lat: 35.609791 },
{ lng: 119.151208, lat: 34.909859 },
{ lng: 120.227525, lat: 34.360332 },
{ lng: 120.620369, lat: 33.376723 },
{ lng: 121.229014, lat: 32.460319 },
{ lng: 121.908146, lat: 31.692174 },
{ lng: 121.891919, lat: 30.949352 },
{ lng: 121.264257, lat: 30.676267 },
{ lng: 121.503519, lat: 30.142915 },
{ lng: 122.092114, lat: 29.83252 },
{ lng: 121.938428, lat: 29.018022 },
{ lng: 121.684439, lat: 28.225513 },
{ lng: 121.125661, lat: 28.135673 },
{ lng: 120.395473, lat: 27.053207 },
{ lng: 119.585497, lat: 25.740781 },
{ lng: 118.656871, lat: 24.547391 },
{ lng: 117.281606, lat: 23.624501 },
{ lng: 115.890735, lat: 22.782873 },
{ lng: 114.763827, lat: 22.668074 },
{ lng: 114.152547, lat: 22.22376 },
{ lng: 113.80678, lat: 22.54834 },
{ lng: 113.241078, lat: 22.051367 },
{ lng: 111.843592, lat: 21.550494 },
{ lng: 110.785466, lat: 21.397144 },
{ lng: 110.444039, lat: 20.341033 },
{ lng: 109.889861, lat: 20.282457 },
{ lng: 109.627655, lat: 21.008227 },
{ lng: 109.864488, lat: 21.395051 },
{ lng: 108.522813, lat: 21.715212 },
{ lng: 108.05018, lat: 21.55238 },
{ lng: 107.04342, lat: 21.811899 },
{ lng: 106.567273, lat: 22.218205 },
{ lng: 106.725403, lat: 22.794268 },
{ lng: 105.811247, lat: 22.976892 },
{ lng: 105.329209, lat: 23.352063 },
{ lng: 104.476858, lat: 22.81915 },
{ lng: 103.504515, lat: 22.703757 },
{ lng: 102.706992, lat: 22.708795 },
{ lng: 102.170436, lat: 22.464753 },
{ lng: 101.652018, lat: 22.318199 },
{ lng: 101.80312, lat: 21.174367 },
{ lng: 101.270026, lat: 21.201652 },
{ lng: 101.180005, lat: 21.436573 },
{ lng: 101.150033, lat: 21.849984 },
{ lng: 100.416538, lat: 21.558839 },
{ lng: 99.983489, lat: 21.742937 },
{ lng: 99.240899, lat: 22.118314 },
{ lng: 99.531992, lat: 22.949039 },
{ lng: 98.898749, lat: 23.142722 },
{ lng: 98.660262, lat: 24.063286 },
{ lng: 97.60472, lat: 23.897405 },
{ lng: 97.724609, lat: 25.083637 },
{ lng: 98.671838, lat: 25.918703 },
{ lng: 98.712094, lat: 26.743536 },
{ lng: 98.68269, lat: 27.508812 },
{ lng: 98.246231, lat: 27.747221 },
{ lng: 97.911988, lat: 28.335945 },
{ lng: 97.327114, lat: 28.261583 },
{ lng: 96.248833, lat: 28.411031 },
{ lng: 96.586591, lat: 28.83098 },
{ lng: 96.117679, lat: 29.452802 },
{ lng: 95.404802, lat: 29.031717 },
{ lng: 94.56599, lat: 29.277438 },
{ lng: 93.413348, lat: 28.640629 },
{ lng: 92.503119, lat: 27.896876 },
{ lng: 91.696657, lat: 27.771742 },
{ lng: 91.258854, lat: 28.040614 },
{ lng: 90.730514, lat: 28.064954 },
{ lng: 90.015829, lat: 28.296439 },
{ lng: 89.47581, lat: 28.042759 },
{ lng: 88.814248, lat: 27.299316 },
{ lng: 88.730326, lat: 28.086865 },
{ lng: 88.120441, lat: 27.876542 },
{ lng: 86.954517, lat: 27.974262 },
{ lng: 85.82332, lat: 28.203576 },
{ lng: 85.011638, lat: 28.642774 },
{ lng: 84.23458, lat: 28.839894 },
{ lng: 83.898993, lat: 29.320226 },
{ lng: 83.337115, lat: 29.463732 },
{ lng: 82.327513, lat: 30.115268 },
{ lng: 81.525804, lat: 30.422717 },
{ lng: 81.111256, lat: 30.183481 },
{ lng: 79.721367, lat: 30.882715 },
{ lng: 78.738894, lat: 31.515906 },
{ lng: 78.458446, lat: 32.618164 },
{ lng: 79.176129, lat: 32.48378 },
{ lng: 79.208892, lat: 32.994395 },
{ lng: 78.811086, lat: 33.506198 },
{ lng: 78.912269, lat: 34.321936 },
{ lng: 77.837451, lat: 35.49401 },
{ lng: 76.192848, lat: 35.898403 },
{ lng: 75.896897, lat: 36.666806 },
{ lng: 75.158028, lat: 37.133031 },
{ lng: 74.980002, lat: 37.41999 },
{ lng: 74.829986, lat: 37.990007 },
{ lng: 74.864816, lat: 38.378846 },
{ lng: 74.257514, lat: 38.606507 },
{ lng: 73.928852, lat: 38.505815 },
{ lng: 73.675379, lat: 39.431237 },
{ lng: 73.960013, lat: 39.660008 },
{ lng: 73.822244, lat: 39.893973 },
{ lng: 74.776862, lat: 40.366425 },
{ lng: 75.467828, lat: 40.562072 },
{ lng: 76.526368, lat: 40.427946 },
{ lng: 76.904484, lat: 41.066486 },
{ lng: 78.187197, lat: 41.185316 },
{ lng: 78.543661, lat: 41.582243 },
{ lng: 80.11943, lat: 42.123941 },
{ lng: 80.25999, lat: 42.349999 },
{ lng: 80.18015, lat: 42.920068 },
{ lng: 80.866206, lat: 43.180362 },
{ lng: 79.966106, lat: 44.917517 },
{ lng: 81.947071, lat: 45.317027 },
{ lng: 82.458926, lat: 45.53965 },
{ lng: 83.180484, lat: 47.330031 },
{ lng: 85.16429, lat: 47.000956 },
{ lng: 85.720484, lat: 47.452969 },
{ lng: 85.768233, lat: 48.455751 },
{ lng: 86.598776, lat: 48.549182 },
{ lng: 87.35997, lat: 49.214981 },
{ lng: 87.751264, lat: 49.297198 },
{ lng: 88.013832, lat: 48.599463 },
{ lng: 88.854298, lat: 48.069082 },
{ lng: 90.280826, lat: 47.693549 },
{ lng: 90.970809, lat: 46.888146 },
{ lng: 90.585768, lat: 45.719716 },
{ lng: 90.94554, lat: 45.286073 },
{ lng: 92.133891, lat: 45.115076 },
{ lng: 93.480734, lat: 44.975472 },
{ lng: 94.688929, lat: 44.352332 },
{ lng: 95.306875, lat: 44.241331 },
{ lng: 95.762455, lat: 43.319449 },
{ lng: 96.349396, lat: 42.725635 },
{ lng: 97.451757, lat: 42.74889 },
{ lng: 99.515817, lat: 42.524691 },
{ lng: 100.845866, lat: 42.663804 },
{ lng: 101.83304, lat: 42.514873 },
{ lng: 103.312278, lat: 41.907468 },
{ lng: 104.522282, lat: 41.908347 },
{ lng: 104.964994, lat: 41.59741 },
{ lng: 106.129316, lat: 42.134328 },
{ lng: 107.744773, lat: 42.481516 },
{ lng: 109.243596, lat: 42.519446 },
{ lng: 110.412103, lat: 42.871234 },
{ lng: 111.129682, lat: 43.406834 },
{ lng: 111.829588, lat: 43.743118 },
{ lng: 111.667737, lat: 44.073176 },
{ lng: 111.348377, lat: 44.457442 },
{ lng: 111.873306, lat: 45.102079 },
{ lng: 112.436062, lat: 45.011646 },
{ lng: 113.463907, lat: 44.808893 },
{ lng: 114.460332, lat: 45.339817 },
{ lng: 115.985096, lat: 45.727235 },
{ lng: 116.717868, lat: 46.388202 },
{ lng: 117.421701, lat: 46.672733 },
{ lng: 118.874326, lat: 46.805412 },
{ lng: 119.66327, lat: 46.69268 },
{ lng: 119.772824, lat: 47.048059 },
{ lng: 118.866574, lat: 47.74706 },
{ lng: 118.064143, lat: 48.06673 },
{ lng: 117.295507, lat: 47.697709 },
{ lng: 116.308953, lat: 47.85341 },
{ lng: 115.742837, lat: 47.726545 },
{ lng: 115.485282, lat: 48.135383 },
{ lng: 116.191802, lat: 49.134598 },
{ lng: 116.678801, lat: 49.888531 },
{ lng: 117.879244, lat: 49.510983 },
{ lng: 119.288461, lat: 50.142883 },
{ lng: 119.279366, lat: 50.582908 },
{ lng: 120.18205, lat: 51.643566 },
{ lng: 120.738191, lat: 51.964115 },
{ lng: 120.725789, lat: 52.516226 },
{ lng: 120.177089, lat: 52.753886 },
{ lng: 121.003085, lat: 53.251401 },
{ lng: 122.245748, lat: 53.431726 },
{ lng: 123.571507, lat: 53.458804 },
{ lng: 125.068211, lat: 53.161045 },
{ lng: 125.946349, lat: 52.792799 },
{ lng: 126.564399, lat: 51.784255 },
{ lng: 126.939157, lat: 51.353894 },
{ lng: 127.287456, lat: 50.739797 },
{ lng: 127.657407, lat: 49.76027 },
];
let pointlayer = new PointLayer()
.source(
d,
// [
// {"lng":10,"lat":0},
// {"lng":20,"lat":0},
// {"lng":30,"lat":0},
// {"lng":40,"lat":0},
// {"lng":50,"lat":0},
// {"lng":60,"lat":0},
// {"lng":70,"lat":0},
// {"lng":80,"lat":0},
// {"lng":90,"lat":0},
// {"lng":100,"lat":0},
// {"lng":110,"lat":0},
// {"lng":120,"lat":0},
// {"lng":130,"lat":0},
// {"lng":140,"lat":0},
// {"lng":150,"lat":0},
// {"lng":160,"lat":0},
// {"lng":170,"lat":0},
// {"lng":180,"lat":0},
// {"lng":190,"lat":0},
// {"lng":200,"lat":0},
// {"lng":210,"lat":0},
// {"lng":220,"lat":0},
// {"lng":230,"lat":0},
// {"lng":240,"lat":0},
// {"lng":250,"lat":0},
// {"lng":260,"lat":0},
// {"lng":270,"lat":0},
// {"lng":280,"lat":0},
// {"lng":290,"lat":0},
// {"lng":300,"lat":0},
// {"lng":310,"lat":0},
// {"lng":320,"lat":0},
// {"lng":330,"lat":0},
// {"lng":340,"lat":0},
// {"lng":350,"lat":0},
// {"lng":360,"lat":0},
// {"lng":0,"lat":0},
// {"lng":0,"lat":10},
// {"lng":0,"lat":20},
// {"lng":0,"lat":30},
// {"lng":0,"lat":40},
// {"lng":0,"lat":50},
// {"lng":0,"lat":60},
// {"lng":0,"lat":70},
// {"lng":0,"lat":80},
// {"lng":0,"lat":90},
// {"lng":0,"lat":-10},
// {"lng":0,"lat":-20},
// {"lng":0,"lat":-30},
// {"lng":0,"lat":-40},
// {"lng":0,"lat":-50},
// {"lng":0,"lat":-60},
// {"lng":0,"lat":-70},
// {"lng":0,"lat":-80},
// {"lng":0,"lat":-90},
// ],
{
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
},
)
.shape('circle')
// .shape('cylinder')
.color('#f00')
.size(10)
.style({
opacity: 0.6,
})
.active(true);
// scene.addLayer(pointlayer);
// let pointlayer = new PointLayer()
// .source(
// [
// {
// lng: 120,
// lat: 30,
// },
// ],
// {
// parser: {
// type: 'json',
// x: 'lng',
// y: 'lat',
// },
// },
// )
// .shape('circle')
// .color('rgba(255, 0, 0, 1.0)')
// .size(200);
const earthlayer = new EarthLayer()
.source(
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*3-3NSpqRqUoAAAAAAAAAAAAAARQnAQ',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
)
.color('#2E8AE6')
.shape('fill')
.style({
opacity: 1.0,
radius: 40,
globelOtions: {
ambientRatio: 0.6, // 环境光
diffuseRatio: 0.4, // 漫反射
specularRatio: 0.1, // 高光反射
// earthTime: 4.0
earthTime: 0.1,
},
})
.animate(true);
// earthlayer.setEarthTime(4.0)
scene.on('loaded', () => {
scene.addLayer(earthlayer);
scene.addLayer(pointlayer);
earthlayer.setEarthTime(4.0);
});
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -3,6 +3,9 @@ import * as React from 'react';
import Map from './components/Map'; import Map from './components/Map';
import Map2 from './components/Map2'; import Map2 from './components/Map2';
import Earth from './components/earth'
// @ts-ignore // @ts-ignore
storiesOf('自定义地图', module).add('地图', () => <Map />) storiesOf('自定义地图', module)
.add('Earth', () => <Earth />)
.add('地图', () => <Map />)
.add('地图2', () => <Map2 />); .add('地图2', () => <Map2 />);