mirror of https://gitee.com/antv-l7/antv-l7
Shihui dev (#808)
* feat: add getModelMatrix into viewport * feat: 新增地球模式 (初步构建) * feat: 完善地球交互 * style: lint style * feat: 调整地球图层缩放的方向 * style: lint style * feat: 增加地球模式的 pointLayer/fill 图层 * style: lint style * feat: 增加地球、太阳的简单运动系统,优化部分代码结构 * fix: 修复时间点击出错 * style: lint style * fix: 修复地图 panBy 方法参数错误 * style: lint style * feat: pointLayer/cylinder 圆柱兼容地球模式 * style: lint style * feat: 修复 pointLayer/fill 在拾取是破面严重的情况 * style: lint style * feat: 增加 arc 弧度调节 * feat: 增加 lineLayer/arc3d 兼容地球模式 * style: lint style * feat: 增加地球图层 - 大气层 * style: lint style * feat: 增加设置可视化层背景色的能力 * style: lint style * feat: 增加地球外发光效果 * style: lint style * feat: 允许用户不使用 layer 的 source 方法 - 地球模式下光晕图层不需要传数据 * style: lint style * feat: 调整光晕的 shader 计算 * feat: 调整地球大气层的渲染层级 * style: lint style * feat: 调整案例 * style: lint style * feat: 增加地球图层的默认参数、调整部分代码 * style: lint style * feat: imageTile developinging * style: lint style * feat: 新增图片瓦片地图 * fix: 修复 amap2 新增样式导致的 marker 失效 * feat: 修复 amap2 的 amap-maps 新增 z-index=0; 引发的marker 显示层级失效 * feat: amap2 的 amap-maps 新增 z-index=0; 样式,让 marker 中 zIndex 失效 * style: lint style * chore: update version 2.5.36 -> 2.5.37 * feat: 补全瓦片地图中的类型引用 * style: lint style * feat: 增加 demo * style: lint style * feat: 修复 varying 传递 float 总数在部分终端设备 ios13 上突破限制的问题 * chore: update version 2.5.37 -> 2.5.38 * feat: add stoty demo * style: lint style * feat: 调整 aspace demo * feat: 调整 aspace demo * style: lint style
This commit is contained in:
parent
7f6f07462c
commit
e94cd2ca0f
|
@ -197,5 +197,7 @@
|
||||||
"tnpm": {
|
"tnpm": {
|
||||||
"mode": "yarn"
|
"mode": "yarn"
|
||||||
},
|
},
|
||||||
"dependencies": {}
|
"dependencies": {
|
||||||
|
"@antv/geo-coord": "^1.0.8"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,8 @@ export interface ILayer {
|
||||||
layerModelNeedUpdate: boolean;
|
layerModelNeedUpdate: boolean;
|
||||||
styleNeedUpdate: boolean;
|
styleNeedUpdate: boolean;
|
||||||
layerModel: ILayerModel;
|
layerModel: ILayerModel;
|
||||||
|
layerChildren: ILayer[]; // 在图层中添加子图层
|
||||||
|
sceneContainer: Container | undefined;
|
||||||
dataState: IDataState; // 数据流状态
|
dataState: IDataState; // 数据流状态
|
||||||
pickedFeatureID: number | null;
|
pickedFeatureID: number | null;
|
||||||
hooks: {
|
hooks: {
|
||||||
|
@ -137,7 +139,7 @@ export interface ILayer {
|
||||||
needPick(type: string): boolean;
|
needPick(type: string): boolean;
|
||||||
getLayerConfig(): Partial<ILayerConfig & ISceneConfig>;
|
getLayerConfig(): Partial<ILayerConfig & ISceneConfig>;
|
||||||
getContainer(): Container;
|
getContainer(): Container;
|
||||||
setContainer(container: Container): void;
|
setContainer(container: Container, sceneContainer: Container): void;
|
||||||
setCurrentPickId(id: number | null): void;
|
setCurrentPickId(id: number | null): void;
|
||||||
getCurrentPickId(): number | null;
|
getCurrentPickId(): number | null;
|
||||||
setCurrentSelectedId(id: number | null): void;
|
setCurrentSelectedId(id: number | null): void;
|
||||||
|
@ -377,7 +379,7 @@ export interface ILayerService {
|
||||||
getLayers(): ILayer[];
|
getLayers(): ILayer[];
|
||||||
getLayer(id: string): ILayer | undefined;
|
getLayer(id: string): ILayer | undefined;
|
||||||
getLayerByName(name: string): ILayer | undefined;
|
getLayerByName(name: string): ILayer | undefined;
|
||||||
remove(layer: ILayer): void;
|
remove(layer: ILayer, parentLayer?: ILayer): void;
|
||||||
removeAllLayers(): void;
|
removeAllLayers(): void;
|
||||||
updateRenderOrder(): void;
|
updateRenderOrder(): void;
|
||||||
renderLayers(type?: string): void;
|
renderLayers(type?: string): void;
|
||||||
|
|
|
@ -64,11 +64,20 @@ export default class LayerService implements ILayerService {
|
||||||
return this.layers.find((layer) => layer.name === name);
|
return this.layers.find((layer) => layer.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(layer: ILayer): void {
|
public remove(layer: ILayer, parentLayer?: ILayer): void {
|
||||||
const layerIndex = this.layers.indexOf(layer);
|
// Tip: layer.layerChildren 当 layer 存在子图层的情况
|
||||||
if (layerIndex > -1) {
|
if (parentLayer) {
|
||||||
this.layers.splice(layerIndex, 1);
|
const layerIndex = parentLayer.layerChildren.indexOf(layer);
|
||||||
|
if (layerIndex > -1) {
|
||||||
|
parentLayer.layerChildren.splice(layerIndex, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const layerIndex = this.layers.indexOf(layer);
|
||||||
|
if (layerIndex > -1) {
|
||||||
|
this.layers.splice(layerIndex, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
layer.emit('remove', null);
|
layer.emit('remove', null);
|
||||||
layer.destroy();
|
layer.destroy();
|
||||||
this.renderLayers();
|
this.renderLayers();
|
||||||
|
@ -91,17 +100,29 @@ export default class LayerService implements ILayerService {
|
||||||
this.alreadyInRendering = true;
|
this.alreadyInRendering = true;
|
||||||
this.clear();
|
this.clear();
|
||||||
this.updateRenderOrder();
|
this.updateRenderOrder();
|
||||||
|
|
||||||
this.layers
|
this.layers
|
||||||
.filter((layer) => layer.inited)
|
.filter((layer) => layer.inited)
|
||||||
.filter((layer) => layer.isVisible())
|
.filter((layer) => layer.isVisible())
|
||||||
.forEach((layer) => {
|
.forEach((layer) => {
|
||||||
// trigger hooks
|
// Tip: 渲染 layer 的子图层 默认 layerChildren 为空数组 表示没有子图层 目前只有 ImageTileLayer 有子图层
|
||||||
layer.hooks.beforeRenderData.call();
|
renderLayerEvent(layer.layerChildren);
|
||||||
layer.hooks.beforeRender.call();
|
renderLayerEvent([layer]);
|
||||||
layer.render();
|
|
||||||
layer.hooks.afterRender.call();
|
|
||||||
});
|
});
|
||||||
this.alreadyInRendering = false;
|
this.alreadyInRendering = false;
|
||||||
|
|
||||||
|
function renderLayerEvent(layers: ILayer[]) {
|
||||||
|
layers
|
||||||
|
.filter((layer) => layer.inited)
|
||||||
|
.filter((layer) => layer.isVisible())
|
||||||
|
.forEach((layer) => {
|
||||||
|
// trigger hooks
|
||||||
|
layer.hooks.beforeRenderData.call();
|
||||||
|
layer.hooks.beforeRender.call();
|
||||||
|
layer.render();
|
||||||
|
layer.hooks.afterRender.call();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateRenderOrder() {
|
public updateRenderOrder() {
|
||||||
|
@ -111,7 +132,14 @@ export default class LayerService implements ILayerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
this.layers.forEach((layer) => layer.destroy());
|
this.layers.forEach((layer) => {
|
||||||
|
// Tip: layer.layerChildren 当 layer 存在子图层的情况
|
||||||
|
if (layer.layerChildren) {
|
||||||
|
layer.layerChildren.forEach((child) => child.destroy());
|
||||||
|
layer.layerChildren = [];
|
||||||
|
}
|
||||||
|
layer.destroy();
|
||||||
|
});
|
||||||
this.layers = [];
|
this.layers = [];
|
||||||
this.renderLayers();
|
this.renderLayers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ export interface IEarthService<RawMap = {}> {
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapServiceEvent = ['mapload'];
|
export const MapServiceEvent = ['mapload', 'mapchange'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 地图初始化配置项
|
* 地图初始化配置项
|
||||||
|
|
|
@ -113,6 +113,11 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
|
|
||||||
public layerModel: ILayerModel;
|
public layerModel: ILayerModel;
|
||||||
|
|
||||||
|
// TODO: 记录 sceneContainer 供创建子图层的时候使用 如 imageTileLayer
|
||||||
|
public sceneContainer: Container | undefined;
|
||||||
|
// TODO: 用于保存子图层对象
|
||||||
|
public layerChildren: ILayer[] = [];
|
||||||
|
|
||||||
@lazyInject(TYPES.IGlobalConfigService)
|
@lazyInject(TYPES.IGlobalConfigService)
|
||||||
protected readonly configService: IGlobalConfigService;
|
protected readonly configService: IGlobalConfigService;
|
||||||
|
|
||||||
|
@ -222,8 +227,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||||
* -> SceneContainer 1.*
|
* -> SceneContainer 1.*
|
||||||
* -> LayerContainer 1.*
|
* -> LayerContainer 1.*
|
||||||
*/
|
*/
|
||||||
public setContainer(container: Container) {
|
public setContainer(container: Container, sceneContainer: Container) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
|
this.sceneContainer = sceneContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getContainer() {
|
public getContainer() {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import BaseLayer from '../core/BaseLayer';
|
||||||
|
import ImageTileModels, { ImageTileModelType } from './models/index';
|
||||||
|
interface IImageLayerStyleOptions {
|
||||||
|
opacity: number;
|
||||||
|
}
|
||||||
|
export default class ImageTileLayer extends BaseLayer<IImageLayerStyleOptions> {
|
||||||
|
public type: string = 'ImageTileLayer';
|
||||||
|
public buildModels() {
|
||||||
|
const modelType = this.getModelType();
|
||||||
|
this.layerModel = new ImageTileModels[modelType](this);
|
||||||
|
this.models = this.layerModel.initModels();
|
||||||
|
}
|
||||||
|
public rebuildModels() {
|
||||||
|
this.models = this.layerModel.buildModels();
|
||||||
|
}
|
||||||
|
protected getConfigSchema() {
|
||||||
|
return {
|
||||||
|
properties: {
|
||||||
|
opacity: {
|
||||||
|
type: 'number',
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
protected getDefaultConfig() {
|
||||||
|
const type = this.getModelType();
|
||||||
|
const defaultConfig = {
|
||||||
|
imageTile: {},
|
||||||
|
};
|
||||||
|
return defaultConfig[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getModelType(): ImageTileModelType {
|
||||||
|
return 'imageTile';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
import {
|
||||||
|
AttributeType,
|
||||||
|
gl,
|
||||||
|
IEncodeFeature,
|
||||||
|
ILayer,
|
||||||
|
ILayerPlugin,
|
||||||
|
IModel,
|
||||||
|
IModelUniform,
|
||||||
|
IRasterParserDataItem,
|
||||||
|
IStyleAttributeService,
|
||||||
|
ITexture2D,
|
||||||
|
lazyInject,
|
||||||
|
TYPES,
|
||||||
|
} from '@antv/l7-core';
|
||||||
|
import BaseModel from '../../core/BaseModel';
|
||||||
|
import { RasterImageTriangulation } from '../../core/triangulation';
|
||||||
|
import ImageTileFrag from './shaders/imagetile_frag.glsl';
|
||||||
|
import ImageTileVert from './shaders/imagetile_vert.glsl';
|
||||||
|
|
||||||
|
import Tile from '../utils/Tile';
|
||||||
|
|
||||||
|
interface IImageLayerStyleOptions {
|
||||||
|
resolution: string;
|
||||||
|
maxSourceZoom: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ImageTileModel extends BaseModel {
|
||||||
|
public tileLayer: any;
|
||||||
|
public getUninforms(): IModelUniform {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 临时的瓦片测试方法
|
||||||
|
public tile() {
|
||||||
|
const [WS, EN] = this.mapService.getBounds();
|
||||||
|
const NE = { lng: EN[0], lat: EN[1] };
|
||||||
|
const SW = { lng: WS[0], lat: WS[1] };
|
||||||
|
this.tileLayer.calCurrentTiles({
|
||||||
|
NE,
|
||||||
|
SW,
|
||||||
|
tileCenter: this.mapService.getCenter(),
|
||||||
|
currentZoom: this.mapService.getZoom(),
|
||||||
|
minSourceZoom: this.mapService.getMinZoom(),
|
||||||
|
minZoom: this.mapService.getMinZoom(),
|
||||||
|
maxZoom: this.mapService.getMaxZoom(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public initModels() {
|
||||||
|
// TODO: 瓦片组件默认在最下层
|
||||||
|
this.layer.zIndex = -999;
|
||||||
|
const {
|
||||||
|
resolution = 'low',
|
||||||
|
maxSourceZoom = 17,
|
||||||
|
} = this.layer.getLayerConfig() as IImageLayerStyleOptions;
|
||||||
|
const source = this.layer.getSource();
|
||||||
|
// 当存在 url 的时候生效
|
||||||
|
if (source.data.tileurl) {
|
||||||
|
this.tileLayer = new Tile({
|
||||||
|
url: source.data.tileurl,
|
||||||
|
layerService: this.layerService,
|
||||||
|
layer: this.layer,
|
||||||
|
resolution,
|
||||||
|
maxSourceZoom,
|
||||||
|
// Tip: 当前为 default
|
||||||
|
crstype: 'epsg3857',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tile();
|
||||||
|
let t = new Date().getTime();
|
||||||
|
this.mapService.on('mapchange', () => {
|
||||||
|
const newT = new Date().getTime();
|
||||||
|
const cutT = newT - t;
|
||||||
|
t = newT;
|
||||||
|
if (cutT < 16) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.tile();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
this.layer.buildLayerModel({
|
||||||
|
moduleName: 'ImageTileLayer',
|
||||||
|
vertexShader: ImageTileVert,
|
||||||
|
fragmentShader: ImageTileFrag,
|
||||||
|
triangulation: RasterImageTriangulation,
|
||||||
|
primitive: gl.TRIANGLES,
|
||||||
|
depth: { enable: false },
|
||||||
|
blend: this.getBlend(),
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearModels() {
|
||||||
|
this.tileLayer.removeTiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
public buildModels() {
|
||||||
|
return this.initModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected registerBuiltinAttributes() {
|
||||||
|
// point layer size;
|
||||||
|
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]];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import ImageTileModel from './imagetile';
|
||||||
|
export type ImageTileModelType = 'imageTile';
|
||||||
|
|
||||||
|
const ImageTileModels: { [key in ImageTileModelType]: any } = {
|
||||||
|
imageTile: ImageTileModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageTileModels;
|
|
@ -0,0 +1,4 @@
|
||||||
|
precision mediump float;
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
precision highp float;
|
||||||
|
uniform mat4 u_ModelMatrix;
|
||||||
|
uniform mat4 u_Mvp;
|
||||||
|
attribute vec3 a_Position;
|
||||||
|
#pragma include "projection"
|
||||||
|
void main() {
|
||||||
|
vec4 project_pos = project_position(vec4(a_Position, 1.0));
|
||||||
|
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
|
||||||
|
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
|
||||||
|
gl_Position = u_Mvp * (vec4(project_pos.xy,0., 1.0));
|
||||||
|
} else {
|
||||||
|
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
precision mediump float;
|
||||||
|
uniform float u_opacity: 1.0;
|
||||||
|
uniform sampler2D u_texture;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
void main() {
|
||||||
|
vec4 color = texture2D(u_texture,vec2(v_texCoord.x,v_texCoord.y));
|
||||||
|
gl_FragColor = color;
|
||||||
|
gl_FragColor.a *= u_opacity;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
precision highp float;
|
||||||
|
uniform mat4 u_ModelMatrix;
|
||||||
|
uniform mat4 u_Mvp;
|
||||||
|
attribute vec3 a_Position;
|
||||||
|
attribute vec2 a_Uv;
|
||||||
|
varying vec2 v_texCoord;
|
||||||
|
#pragma include "projection"
|
||||||
|
void main() {
|
||||||
|
v_texCoord = a_Uv;
|
||||||
|
vec4 project_pos = project_position(vec4(a_Position, 1.0));
|
||||||
|
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
|
||||||
|
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
|
||||||
|
gl_Position = u_Mvp * (vec4(project_pos.xy,0., 1.0));
|
||||||
|
} else {
|
||||||
|
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { LngLatBounds, toBounds, toLngLatBounds } from '@antv/geo-coord';
|
||||||
|
import { Container } from 'inversify';
|
||||||
|
import ImageLayer from '../../image';
|
||||||
|
|
||||||
|
interface IUrlParams {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
z: number;
|
||||||
|
s?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const r2d = 180 / Math.PI;
|
||||||
|
const tileURLRegex = /\{([zxy])\}/g;
|
||||||
|
|
||||||
|
export default class ImageTile {
|
||||||
|
public tile: number[]; // 当前图片瓦片的索引
|
||||||
|
public name: string;
|
||||||
|
public imageLayer: any;
|
||||||
|
constructor(
|
||||||
|
key: string,
|
||||||
|
url: string,
|
||||||
|
container: Container,
|
||||||
|
sceneContainer: Container,
|
||||||
|
) {
|
||||||
|
this.name = key;
|
||||||
|
this.tile = key.split('_').map((v) => Number(v));
|
||||||
|
|
||||||
|
const urlParams = {
|
||||||
|
x: this.tile[0],
|
||||||
|
y: this.tile[1],
|
||||||
|
z: this.tile[2],
|
||||||
|
};
|
||||||
|
const imageSrc = this.getTileURL(urlParams, url);
|
||||||
|
|
||||||
|
const lnglatBounds = this.tileLnglatBounds(this.tile);
|
||||||
|
const west = lnglatBounds.getWest();
|
||||||
|
const south = lnglatBounds.getSouth();
|
||||||
|
const east = lnglatBounds.getEast();
|
||||||
|
const north = lnglatBounds.getNorth();
|
||||||
|
|
||||||
|
const imageLayer = new ImageLayer({});
|
||||||
|
imageLayer.source(
|
||||||
|
// 'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
|
||||||
|
imageSrc,
|
||||||
|
{
|
||||||
|
parser: {
|
||||||
|
type: 'image',
|
||||||
|
// extent: [121.168, 30.2828, 121.384, 30.4219],
|
||||||
|
extent: [west, south, east, north],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
imageLayer.setContainer(container, sceneContainer);
|
||||||
|
imageLayer.init();
|
||||||
|
|
||||||
|
this.imageLayer = imageLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
this.imageLayer.clearModels();
|
||||||
|
this.imageLayer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTileURL(urlParams: IUrlParams, path: string) {
|
||||||
|
if (!urlParams.s) {
|
||||||
|
// Default to a random choice of a, b or c
|
||||||
|
urlParams.s = String.fromCharCode(97 + Math.floor(Math.random() * 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
tileURLRegex.lastIndex = 0;
|
||||||
|
return path.replace(tileURLRegex, (value, key: any) => {
|
||||||
|
// @ts-ignore
|
||||||
|
return urlParams[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get tile bounds in WGS84 coordinates
|
||||||
|
public tileLnglatBounds(tile: number[]) {
|
||||||
|
const e = this.tile2lng(tile[0] + 1, tile[2]);
|
||||||
|
const w = this.tile2lng(tile[0], tile[2]);
|
||||||
|
const s = this.tile2lat(tile[1] + 1, tile[2]);
|
||||||
|
const n = this.tile2lat(tile[1], tile[2]);
|
||||||
|
return toLngLatBounds([w, n], [e, s]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public tile2lng(x: number, z: number) {
|
||||||
|
return (x / Math.pow(2, z)) * 360 - 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
public tile2lat(y: number, z: number) {
|
||||||
|
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z);
|
||||||
|
return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,359 @@
|
||||||
|
import { Bounds, GeoCoordinates, Point, toLngLat } from '@antv/geo-coord';
|
||||||
|
import {
|
||||||
|
createLayerContainer,
|
||||||
|
ILayer,
|
||||||
|
ILayerService,
|
||||||
|
ILngLat,
|
||||||
|
} from '@antv/l7-core';
|
||||||
|
import { Container } from 'inversify';
|
||||||
|
|
||||||
|
import ImageTile from './ImageTile';
|
||||||
|
import TileCache from './tileCache';
|
||||||
|
|
||||||
|
// Tip: 瓦片地图的存储上限
|
||||||
|
const CacheLimit = 30;
|
||||||
|
|
||||||
|
export default class Tile {
|
||||||
|
public tileList: any = {};
|
||||||
|
public tileCache: any;
|
||||||
|
|
||||||
|
public updateTileList: any[];
|
||||||
|
public tileZoom: number;
|
||||||
|
public noPruneRange: any;
|
||||||
|
public url: string;
|
||||||
|
public resolution: number;
|
||||||
|
public maxSourceZoom: number;
|
||||||
|
public crstype: string;
|
||||||
|
public currentCrs: any;
|
||||||
|
|
||||||
|
public layerService: ILayerService;
|
||||||
|
public layer: ILayer;
|
||||||
|
constructor(props: any) {
|
||||||
|
this.layerService = props.layerService;
|
||||||
|
this.layer = props.layer;
|
||||||
|
this.url = props.url;
|
||||||
|
this.resolution = props.resolution === 'low' ? -1 : 0;
|
||||||
|
this.maxSourceZoom = props.maxSourceZoom;
|
||||||
|
this.crstype = props.crstype;
|
||||||
|
|
||||||
|
this.currentCrs = new GeoCoordinates.default({
|
||||||
|
start: { x: 0, y: 0 },
|
||||||
|
end: { x: 0, y: 0 },
|
||||||
|
projection: this.crstype,
|
||||||
|
}).crs as any;
|
||||||
|
|
||||||
|
this.destroyTile = this.destroyTile.bind(this);
|
||||||
|
this.tileCache = new TileCache(CacheLimit, this.destroyTile);
|
||||||
|
|
||||||
|
this.updateTileList = [];
|
||||||
|
|
||||||
|
this.removeTiles = this.removeTiles.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public calCurrentTiles(oprions: any) {
|
||||||
|
const {
|
||||||
|
NE,
|
||||||
|
SW,
|
||||||
|
tileCenter,
|
||||||
|
currentZoom,
|
||||||
|
minSourceZoom,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
} = oprions;
|
||||||
|
// TODO: 当前瓦片的层级要比地图底图的层级低
|
||||||
|
if (currentZoom >= this.maxSourceZoom) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const zoom = Math.floor(currentZoom) + this.resolution;
|
||||||
|
|
||||||
|
this.tileZoom = zoom > this.maxSourceZoom ? this.maxSourceZoom : zoom;
|
||||||
|
|
||||||
|
if (
|
||||||
|
currentZoom < minZoom ||
|
||||||
|
currentZoom >= maxZoom ||
|
||||||
|
currentZoom < minSourceZoom
|
||||||
|
) {
|
||||||
|
this.removeTiles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateTileList = [];
|
||||||
|
|
||||||
|
// 计算瓦片中心
|
||||||
|
const centerPoint = this.currentCrs.lngLatToPoint(
|
||||||
|
toLngLat(tileCenter.lng, tileCenter.lat),
|
||||||
|
this.tileZoom,
|
||||||
|
);
|
||||||
|
const centerXY = centerPoint.divideBy(256).floor();
|
||||||
|
|
||||||
|
const pixelBounds = this.getPixelBounds(
|
||||||
|
NE,
|
||||||
|
SW,
|
||||||
|
tileCenter,
|
||||||
|
this.tileZoom,
|
||||||
|
this.currentCrs,
|
||||||
|
); // 计算像素范围
|
||||||
|
const tileRange = this.pxBoundsToTileRange(pixelBounds); // 计算瓦片范围
|
||||||
|
|
||||||
|
const margin = 4;
|
||||||
|
|
||||||
|
this.noPruneRange = new Bounds(
|
||||||
|
tileRange.getBottomLeft().subtract([margin, -margin]),
|
||||||
|
tileRange.getTopRight().add([margin, -margin]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// T: isFinite(n: number) 用于检测 n 是否无穷大
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
isFinite(tileRange.min.x) &&
|
||||||
|
isFinite(tileRange.min.y) &&
|
||||||
|
isFinite(tileRange.max.x) &&
|
||||||
|
isFinite(tileRange.max.y)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error('Attempted to load an infinite number of tiles');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据视野判断新增的瓦片索引
|
||||||
|
for (let j = tileRange.min.y; j <= tileRange.max.y; j++) {
|
||||||
|
for (let i = tileRange.min.x; i <= tileRange.max.x; i++) {
|
||||||
|
const coords = [i, j, this.tileZoom];
|
||||||
|
const tile = this.tileList[coords.join('_')];
|
||||||
|
if (tile) {
|
||||||
|
tile.current = true;
|
||||||
|
} else {
|
||||||
|
this.tileList[coords.join('_')] = {
|
||||||
|
current: true,
|
||||||
|
coords,
|
||||||
|
};
|
||||||
|
this.updateTileList.push(coords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 瓦片列表排序
|
||||||
|
this.updateTileList.sort((a: any, b: any) => {
|
||||||
|
const tile1 = a;
|
||||||
|
const tile2 = b;
|
||||||
|
const d1 =
|
||||||
|
Math.pow(tile1[0] * 1 - centerXY.x, 2) +
|
||||||
|
Math.pow(tile1[1] * 1 - centerXY.y, 2);
|
||||||
|
const d2 =
|
||||||
|
Math.pow(tile2[0] * 1 - centerXY.x, 2) +
|
||||||
|
Math.pow(tile2[1] * 1 - centerXY.y, 2);
|
||||||
|
return d1 - d2;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.pruneTiles();
|
||||||
|
this.updateTileList.forEach((coords: any) => {
|
||||||
|
const key = coords.join('_');
|
||||||
|
if (this.tileList[key].current) {
|
||||||
|
this.requestTile(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public pxBoundsToTileRange(pixelBounds: any) {
|
||||||
|
return new Bounds(
|
||||||
|
pixelBounds.min.divideBy(256).floor(),
|
||||||
|
pixelBounds.max
|
||||||
|
.divideBy(256)
|
||||||
|
.ceil()
|
||||||
|
.subtract([1, 1]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPixelBounds(
|
||||||
|
NE: ILngLat,
|
||||||
|
SW: ILngLat,
|
||||||
|
tileCenter: ILngLat,
|
||||||
|
tileZoom: number,
|
||||||
|
crs: any,
|
||||||
|
) {
|
||||||
|
const zoom = tileZoom;
|
||||||
|
const NEPoint = crs.lngLatToPoint(toLngLat(NE.lng, NE.lat), zoom);
|
||||||
|
const SWPoint = crs.lngLatToPoint(toLngLat(SW.lng, SW.lat), zoom);
|
||||||
|
const centerPoint = crs.lngLatToPoint(
|
||||||
|
toLngLat(tileCenter.lng, tileCenter.lat),
|
||||||
|
zoom,
|
||||||
|
);
|
||||||
|
const topHeight = centerPoint.y - NEPoint.y;
|
||||||
|
const bottomHeight = SWPoint.y - centerPoint.y;
|
||||||
|
// 跨日界线的情况
|
||||||
|
let leftWidth;
|
||||||
|
let rightWidth;
|
||||||
|
if (tileCenter.lng - NE.lng > 0 || tileCenter.lng - SW.lng < 0) {
|
||||||
|
const width =
|
||||||
|
((Math.pow(2, zoom) * 256) / 360) * (180 - NE.lng) +
|
||||||
|
((Math.pow(2, zoom) * 256) / 360) * (SW.lng + 180);
|
||||||
|
if (tileCenter.lng - NE.lng > 0) {
|
||||||
|
// 日界线在右侧
|
||||||
|
leftWidth =
|
||||||
|
((Math.pow(2, zoom) * 256) / 360) * (tileCenter.lng - NE.lng);
|
||||||
|
rightWidth = width - leftWidth;
|
||||||
|
} else {
|
||||||
|
rightWidth =
|
||||||
|
((Math.pow(2, zoom) * 256) / 360) * (SW.lng - tileCenter.lng);
|
||||||
|
leftWidth = width - rightWidth;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 不跨日界线
|
||||||
|
leftWidth = ((Math.pow(2, zoom) * 256) / 360) * (tileCenter.lng - SW.lng);
|
||||||
|
rightWidth =
|
||||||
|
((Math.pow(2, zoom) * 256) / 360) * (NE.lng - tileCenter.lng);
|
||||||
|
}
|
||||||
|
const pixelBounds = new Bounds(
|
||||||
|
centerPoint.subtract(leftWidth, topHeight),
|
||||||
|
centerPoint.add(rightWidth, bottomHeight),
|
||||||
|
);
|
||||||
|
return pixelBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pruneTiles() {
|
||||||
|
Object.keys(this.tileList).map((key) => {
|
||||||
|
const c = this.tileList[key].coords;
|
||||||
|
// 如果不是同一个缩放层级,则将瓦片设为不显示
|
||||||
|
if (
|
||||||
|
c[2] !== this.tileZoom ||
|
||||||
|
!this.noPruneRange.contains(new Point(c[0], c[1]))
|
||||||
|
) {
|
||||||
|
this.tileList[key].current = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(this.tileList).map((key) => {
|
||||||
|
const tile = this.tileList[key];
|
||||||
|
tile.retain = tile.current;
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(this.tileList).map((key) => {
|
||||||
|
const tile = this.tileList[key];
|
||||||
|
if (tile.current && !tile.active) {
|
||||||
|
const [x, y, z] = key.split('_').map((v) => Number(v));
|
||||||
|
|
||||||
|
if (!this.retainParent(x, y, z, z - 5)) {
|
||||||
|
this.retainChildren(x, y, z, z + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.removeOutTiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
public requestTile(key: string) {
|
||||||
|
const t = this.tileList[key];
|
||||||
|
if (!t) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tile = this.tileCache.getTile(key);
|
||||||
|
if (!tile) {
|
||||||
|
const container = createLayerContainer(
|
||||||
|
this.layer.sceneContainer as Container,
|
||||||
|
);
|
||||||
|
tile = new ImageTile(
|
||||||
|
key,
|
||||||
|
this.url,
|
||||||
|
container,
|
||||||
|
this.layer.sceneContainer as Container,
|
||||||
|
);
|
||||||
|
tile.name = key;
|
||||||
|
|
||||||
|
t.current = true;
|
||||||
|
t.retain = true;
|
||||||
|
t.active = true;
|
||||||
|
|
||||||
|
// 往 imageTileLayer 中添加子图层
|
||||||
|
this.layer.layerChildren.push(tile.imageLayer);
|
||||||
|
|
||||||
|
this.tileCache.setTile(tile, key);
|
||||||
|
|
||||||
|
this.pruneTiles();
|
||||||
|
this.layerService.renderLayers();
|
||||||
|
} else {
|
||||||
|
// Tip: show 方法就是将相应的瓦片图片添加到渲染队列
|
||||||
|
tile.imageLayer.show();
|
||||||
|
t.current = true;
|
||||||
|
t.retain = true;
|
||||||
|
t.active = true;
|
||||||
|
|
||||||
|
this.pruneTiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public retainParent(x: number, y: number, z: number, minZoom: number): any {
|
||||||
|
const x2 = Math.floor(x / 2);
|
||||||
|
const y2 = Math.floor(y / 2);
|
||||||
|
const z2 = z - 1;
|
||||||
|
const tile = this.tileList[[x2, y2, z2].join('_')];
|
||||||
|
if (tile && tile.active) {
|
||||||
|
tile.retain = true;
|
||||||
|
return true;
|
||||||
|
} else if (tile && tile.loaded) {
|
||||||
|
tile.retain = true;
|
||||||
|
}
|
||||||
|
if (z2 > minZoom) {
|
||||||
|
return this.retainParent(x2, y2, z2, minZoom);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public retainChildren(x: number, y: number, z: number, maxZoom: number) {
|
||||||
|
for (let i = 2 * x; i < 2 * x + 2; i++) {
|
||||||
|
for (let j = 2 * y; j < 2 * y + 2; j++) {
|
||||||
|
const key = [i, j, z + 1].join('_');
|
||||||
|
const tile = this.tileList[key];
|
||||||
|
if (tile && tile.active) {
|
||||||
|
tile.retain = true;
|
||||||
|
continue;
|
||||||
|
} else if (tile && tile.loaded) {
|
||||||
|
tile.retain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z + 1 < maxZoom) {
|
||||||
|
this.retainChildren(i, j, z + 1, maxZoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroyTile(tile: any) {
|
||||||
|
const layerIndex = this.layer.layerChildren.indexOf(tile.imageLayer);
|
||||||
|
if (layerIndex > -1) {
|
||||||
|
this.layer.layerChildren.splice(layerIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
tile.imageLayer.emit('remove', null);
|
||||||
|
tile.imageLayer.destroy();
|
||||||
|
this.layerService.renderLayers();
|
||||||
|
|
||||||
|
// 清除 tileCache 中的存储 相当于 tileCache.setTile(tile, null)
|
||||||
|
tile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeOutTiles() {
|
||||||
|
for (const key in this.tileList) {
|
||||||
|
if (!this.tileList[key].retain) {
|
||||||
|
// Tip: 不需要显示的瓦片对象
|
||||||
|
const tile = this.tileCache.getTile(key);
|
||||||
|
// Tip: 若是网格对象存在
|
||||||
|
if (tile) {
|
||||||
|
// Tip: hide 方法就是将相应的瓦片图片从渲染队列中剔除
|
||||||
|
tile.imageLayer.hide();
|
||||||
|
}
|
||||||
|
delete this.tileList[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeTiles() {
|
||||||
|
this.layer.layerChildren.forEach((layer: any) => {
|
||||||
|
layer.emit('remove', null);
|
||||||
|
layer.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.layer.layerChildren = [];
|
||||||
|
this.layerService.renderLayers();
|
||||||
|
this.tileList = {};
|
||||||
|
this.tileCache.destory();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/**
|
||||||
|
* LRU Cache class with limit
|
||||||
|
*
|
||||||
|
* Update order for each get/set operation
|
||||||
|
* Delete oldest when reach given limit
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class LRUCache {
|
||||||
|
public limit: number;
|
||||||
|
public order: any[];
|
||||||
|
public cache: any;
|
||||||
|
public destroy: any;
|
||||||
|
constructor(limit = 50, destroy = () => '') {
|
||||||
|
this.limit = limit;
|
||||||
|
this.destroy = destroy;
|
||||||
|
this.order = [];
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.order.forEach((key: any) => {
|
||||||
|
this.delete(key);
|
||||||
|
});
|
||||||
|
this.cache = {};
|
||||||
|
// access/update order, first item is oldest, last item is newest
|
||||||
|
this.order = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key: string) {
|
||||||
|
const value = this.cache[key];
|
||||||
|
if (value) {
|
||||||
|
// update order
|
||||||
|
this.deleteOrder(key);
|
||||||
|
this.appendOrder(key);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(key: string, value: any) {
|
||||||
|
if (!this.cache[key]) {
|
||||||
|
// if reach limit, delete the oldest
|
||||||
|
if (Object.keys(this.cache).length === this.limit) {
|
||||||
|
this.delete(this.order[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache[key] = value;
|
||||||
|
this.appendOrder(key);
|
||||||
|
} else {
|
||||||
|
// if found in cache, delete the old one, insert new one to the first of list
|
||||||
|
this.delete(key);
|
||||||
|
|
||||||
|
this.cache[key] = value;
|
||||||
|
this.appendOrder(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(key: string) {
|
||||||
|
const value = this.cache[key];
|
||||||
|
if (value) {
|
||||||
|
this.deleteCache(key);
|
||||||
|
this.deleteOrder(key);
|
||||||
|
this.destroy(value, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteCache(key: string) {
|
||||||
|
delete this.cache[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteOrder(key: string) {
|
||||||
|
const index = this.order.findIndex((o) => o === key);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.order.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public appendOrder(key: string) {
|
||||||
|
this.order.push(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import LRUCache from './lruCache';
|
||||||
|
export default class TileCache {
|
||||||
|
public cache: any;
|
||||||
|
constructor(limit = 50, tileDestroy: any) {
|
||||||
|
this.cache = new LRUCache(limit, tileDestroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getTile(key: string) {
|
||||||
|
return this.cache.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setTile(tile: any, key: string) {
|
||||||
|
this.cache.set(key, tile);
|
||||||
|
}
|
||||||
|
public destory() {
|
||||||
|
this.cache.clear();
|
||||||
|
}
|
||||||
|
public setNeedUpdate() {
|
||||||
|
this.cache.order.forEach((key: string) => {
|
||||||
|
const tile = this.cache.get(key);
|
||||||
|
tile.needUpdate = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import BaseLayer from './core/BaseLayer';
|
||||||
import './glsl.d';
|
import './glsl.d';
|
||||||
import HeatmapLayer from './heatmap';
|
import HeatmapLayer from './heatmap';
|
||||||
import ImageLayer from './image';
|
import ImageLayer from './image';
|
||||||
|
import ImageTileLayer from './imagetile';
|
||||||
import LineLayer from './line/index';
|
import LineLayer from './line/index';
|
||||||
import PointLayer from './point';
|
import PointLayer from './point';
|
||||||
import PolygonLayer from './polygon';
|
import PolygonLayer from './polygon';
|
||||||
|
@ -25,6 +26,7 @@ import RegisterStyleAttributePlugin from './plugins/RegisterStyleAttributePlugin
|
||||||
import ShaderUniformPlugin from './plugins/ShaderUniformPlugin';
|
import ShaderUniformPlugin from './plugins/ShaderUniformPlugin';
|
||||||
import UpdateModelPlugin from './plugins/UpdateModelPlugin';
|
import UpdateModelPlugin from './plugins/UpdateModelPlugin';
|
||||||
import UpdateStyleAttributePlugin from './plugins/UpdateStyleAttributePlugin';
|
import UpdateStyleAttributePlugin from './plugins/UpdateStyleAttributePlugin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验传入参数配置项的正确性
|
* 校验传入参数配置项的正确性
|
||||||
* @see /dev-docs/ConfigSchemaValidation.md
|
* @see /dev-docs/ConfigSchemaValidation.md
|
||||||
|
@ -137,6 +139,7 @@ export {
|
||||||
LineLayer,
|
LineLayer,
|
||||||
CityBuildingLayer,
|
CityBuildingLayer,
|
||||||
ImageLayer,
|
ImageLayer,
|
||||||
|
ImageTileLayer,
|
||||||
RasterLayer,
|
RasterLayer,
|
||||||
HeatmapLayer,
|
HeatmapLayer,
|
||||||
EarthLayer,
|
EarthLayer,
|
||||||
|
|
|
@ -459,6 +459,8 @@ export default class AMapService
|
||||||
position,
|
position,
|
||||||
} = e.camera;
|
} = e.camera;
|
||||||
const { lng, lat } = this.getCenter();
|
const { lng, lat } = this.getCenter();
|
||||||
|
// Tip: 触发地图变化事件
|
||||||
|
this.emit('mapchange');
|
||||||
if (this.cameraChangedCallback) {
|
if (this.cameraChangedCallback) {
|
||||||
// resync viewport
|
// resync viewport
|
||||||
// console.log('cameraHeight', height)
|
// console.log('cameraHeight', height)
|
||||||
|
|
|
@ -558,6 +558,8 @@ export default class AMapService
|
||||||
// left, right, bottom, top
|
// left, right, bottom, top
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} = this.map.customCoords?.getCameraParams();
|
} = this.map.customCoords?.getCameraParams();
|
||||||
|
// Tip: 统一触发地图变化事件
|
||||||
|
this.emit('mapchange');
|
||||||
// // @ts-ignore
|
// // @ts-ignore
|
||||||
// console.log('this.map.customCoords.getCameraParams()', this.map.customCoords.getCameraParams())
|
// console.log('this.map.customCoords.getCameraParams()', this.map.customCoords.getCameraParams())
|
||||||
// const { left, right, bottom, top, near, far, position } = this.map.customCoords.getCameraParams();
|
// const { left, right, bottom, top, near, far, position } = this.map.customCoords.getCameraParams();
|
||||||
|
@ -620,6 +622,9 @@ export default class AMapService
|
||||||
// left, right, bottom, top
|
// left, right, bottom, top
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} = this.map.customCoords.getCameraParams();
|
} = this.map.customCoords.getCameraParams();
|
||||||
|
// Tip: 统一触发地图变化事件
|
||||||
|
this.emit('mapchange');
|
||||||
|
|
||||||
const { zoom } = e;
|
const { zoom } = e;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const center = this.map.customCoords.getCenter() as [number, number];
|
const center = this.map.customCoords.getCenter() as [number, number];
|
||||||
|
|
|
@ -337,6 +337,8 @@ export default class L7EarthService implements IEarthService<Map> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleCameraChanged = (e: any) => {
|
private handleCameraChanged = (e: any) => {
|
||||||
|
// Tip: 统一触发地图变化事件
|
||||||
|
this.emit('mapchange');
|
||||||
const DELAY_TIME = 2000;
|
const DELAY_TIME = 2000;
|
||||||
this.handleCameraChanging = true;
|
this.handleCameraChanging = true;
|
||||||
if (this.handleCameraTimer) {
|
if (this.handleCameraTimer) {
|
||||||
|
|
|
@ -320,7 +320,8 @@ export default class L7MapService implements IMapService<Map> {
|
||||||
private handleCameraChanged = () => {
|
private handleCameraChanged = () => {
|
||||||
const { lat, lng } = this.map.getCenter();
|
const { lat, lng } = this.map.getCenter();
|
||||||
const { offsetCoordinate = true } = this.config;
|
const { offsetCoordinate = true } = this.config;
|
||||||
|
// Tip: 统一触发地图变化事件
|
||||||
|
this.emit('mapchange');
|
||||||
// resync
|
// resync
|
||||||
this.viewport.syncWithMapCamera({
|
this.viewport.syncWithMapCamera({
|
||||||
bearing: this.map.getBearing(),
|
bearing: this.map.getBearing(),
|
||||||
|
|
|
@ -403,7 +403,8 @@ export default class MapboxService
|
||||||
private handleCameraChanged = () => {
|
private handleCameraChanged = () => {
|
||||||
// @see https://github.com/mapbox/mapbox-gl-js/issues/2572
|
// @see https://github.com/mapbox/mapbox-gl-js/issues/2572
|
||||||
const { lat, lng } = this.map.getCenter().wrap();
|
const { lat, lng } = this.map.getCenter().wrap();
|
||||||
|
// Tip: 统一触发地图变化事件
|
||||||
|
this.emit('mapchange');
|
||||||
// resync
|
// resync
|
||||||
this.viewport.syncWithMapCamera({
|
this.viewport.syncWithMapCamera({
|
||||||
bearing: this.map.getBearing(),
|
bearing: this.map.getBearing(),
|
||||||
|
|
|
@ -162,7 +162,7 @@ class Scene
|
||||||
// 为当前图层创建一个容器
|
// 为当前图层创建一个容器
|
||||||
// TODO: 初始化的时候设置 容器
|
// TODO: 初始化的时候设置 容器
|
||||||
const layerContainer = createLayerContainer(this.container);
|
const layerContainer = createLayerContainer(this.container);
|
||||||
layer.setContainer(layerContainer);
|
layer.setContainer(layerContainer, this.container);
|
||||||
this.sceneService.addLayer(layer);
|
this.sceneService.addLayer(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,8 +178,8 @@ class Scene
|
||||||
return this.layerService.getLayerByName(name);
|
return this.layerService.getLayerByName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeLayer(layer: ILayer): void {
|
public removeLayer(layer: ILayer, parentLayer?: ILayer): void {
|
||||||
this.layerService.remove(layer);
|
this.layerService.remove(layer, parentLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeAllLayer(): void {
|
public removeAllLayer(): void {
|
||||||
|
|
|
@ -207,6 +207,14 @@ export default class Source extends EventEmitter implements ISource {
|
||||||
private excuteParser(): void {
|
private excuteParser(): void {
|
||||||
const parser = this.parser;
|
const parser = this.parser;
|
||||||
const type: string = parser.type || 'geojson';
|
const type: string = parser.type || 'geojson';
|
||||||
|
// TODO: 图片瓦片地图组件只需要使用 url 参数
|
||||||
|
if (type === 'imagetile') {
|
||||||
|
this.data = {
|
||||||
|
tileurl: this.originData,
|
||||||
|
dataArray: [],
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
const sourceParser = getParser(type);
|
const sourceParser = getParser(type);
|
||||||
this.data = sourceParser(this.originData, parser);
|
this.data = sourceParser(this.originData, parser);
|
||||||
// 计算范围
|
// 计算范围
|
||||||
|
|
|
@ -137,7 +137,6 @@ export default class Aspace extends React.Component {
|
||||||
let r = heightData[i * 4];
|
let r = heightData[i * 4];
|
||||||
let g = heightData[i * 4 + 1];
|
let g = heightData[i * 4 + 1];
|
||||||
let b = heightData[i * 4 + 2];
|
let b = heightData[i * 4 + 2];
|
||||||
|
|
||||||
let h =
|
let h =
|
||||||
-10000.0 +
|
-10000.0 +
|
||||||
(r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) *
|
(r * 255.0 * 256.0 * 256.0 + g * 255.0 * 256.0 + b * 255.0) *
|
||||||
|
@ -168,7 +167,6 @@ export default class Aspace extends React.Component {
|
||||||
const antModel = gltf.scene;
|
const antModel = gltf.scene;
|
||||||
layer.adjustMeshToMap(antModel);
|
layer.adjustMeshToMap(antModel);
|
||||||
layer.setMeshScale(antModel, 20, 20, 20);
|
layer.setMeshScale(antModel, 20, 20, 20);
|
||||||
|
|
||||||
layer.setObjectLngLat(
|
layer.setObjectLngLat(
|
||||||
antModel,
|
antModel,
|
||||||
[center.lng - 0.002, center.lat],
|
[center.lng - 0.002, center.lat],
|
||||||
|
@ -178,13 +176,9 @@ export default class Aspace extends React.Component {
|
||||||
const animations = gltf.animations;
|
const animations = gltf.animations;
|
||||||
if (animations && animations.length) {
|
if (animations && animations.length) {
|
||||||
const mixer = new THREE.AnimationMixer(antModel);
|
const mixer = new THREE.AnimationMixer(antModel);
|
||||||
|
|
||||||
const animation = animations[1];
|
const animation = animations[1];
|
||||||
|
|
||||||
const action = mixer.clipAction(animation);
|
const action = mixer.clipAction(animation);
|
||||||
|
|
||||||
action.play();
|
action.play();
|
||||||
|
|
||||||
layer.addAnimateMixer(mixer);
|
layer.addAnimateMixer(mixer);
|
||||||
}
|
}
|
||||||
antModel.rotation.y = Math.PI;
|
antModel.rotation.y = Math.PI;
|
||||||
|
@ -358,8 +352,7 @@ export default class Aspace extends React.Component {
|
||||||
currentView.pitch = scene.getPitch();
|
currentView.pitch = scene.getPitch();
|
||||||
currentView.zoom = scene.getZoom();
|
currentView.zoom = scene.getZoom();
|
||||||
});
|
});
|
||||||
// @ts-ignore
|
scene.getMapService().on('mapchange', (e: any) => {
|
||||||
scene?.map?.on('camerachange', (e: any) => {
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
currentCamera = threeJSLayer.threeRenderService.getRenderCamera();
|
currentCamera = threeJSLayer.threeRenderService.getRenderCamera();
|
||||||
currentView.pitch = scene.getPitch();
|
currentView.pitch = scene.getPitch();
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
// @ts-ignore
|
||||||
|
import { ImageTileLayer, Scene, PointLayer } from '@antv/l7';
|
||||||
|
import { GaodeMap, GaodeMapV2, Map, Mapbox } from '@antv/l7-maps';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
export default class Amap2demo_imageTileLayer extends React.Component {
|
||||||
|
// @ts-ignore
|
||||||
|
private scene: Scene;
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.scene.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
const scene = new Scene({
|
||||||
|
id: 'map',
|
||||||
|
map: new Map({
|
||||||
|
center: [121.268, 30.3628],
|
||||||
|
pitch: 0,
|
||||||
|
style: 'normal',
|
||||||
|
zoom: 10,
|
||||||
|
viewMode: '3D',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
this.scene = scene;
|
||||||
|
|
||||||
|
let originData = [
|
||||||
|
{
|
||||||
|
lng: 121.107846,
|
||||||
|
lat: 30.267069,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lng: 121.107,
|
||||||
|
lat: 30.267069,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lng: 121.107846,
|
||||||
|
lat: 30.26718,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
const layer = new ImageTileLayer({});
|
||||||
|
layer
|
||||||
|
.source(
|
||||||
|
'http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||||
|
{
|
||||||
|
parser: {
|
||||||
|
type: 'imagetile',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.style({
|
||||||
|
resolution: 'low', // low height
|
||||||
|
// resolution: 'height'
|
||||||
|
maxSourceZoom: 17,
|
||||||
|
});
|
||||||
|
|
||||||
|
let pointlayer = new PointLayer()
|
||||||
|
.source(originData, {
|
||||||
|
parser: {
|
||||||
|
type: 'json',
|
||||||
|
x: 'lng',
|
||||||
|
y: 'lat',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.shape('circle')
|
||||||
|
.color('rgba(255, 0, 0, 1.0)')
|
||||||
|
.size(10)
|
||||||
|
.active(true);
|
||||||
|
|
||||||
|
scene.addLayer(pointlayer);
|
||||||
|
scene.addLayer(layer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
id="map"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import Amap2demo_heatmap_hexagon_world from './components/amap2demo_heatmap_hexa
|
||||||
import Amap2demo_heatmap_grid from "./components/amap2demo_heatmap_grid"
|
import Amap2demo_heatmap_grid from "./components/amap2demo_heatmap_grid"
|
||||||
|
|
||||||
import Amap2demo_imageLayer from "./components/amap2demo_imagelayer"
|
import Amap2demo_imageLayer from "./components/amap2demo_imagelayer"
|
||||||
|
import Amap2demo_imageTileLayer from "./components/amap2demo_imageTileLayer"
|
||||||
|
|
||||||
import Amap2demo_rasterLayer from "./components/amap2demo_rasterlayer"
|
import Amap2demo_rasterLayer from "./components/amap2demo_rasterlayer"
|
||||||
|
|
||||||
|
@ -99,7 +100,10 @@ storiesOf('地图方法', module)
|
||||||
.add('高德地图2.0 heatmap3D/hexagon', () => <Amap2demo_heatmap_hexagon />)
|
.add('高德地图2.0 heatmap3D/hexagon', () => <Amap2demo_heatmap_hexagon />)
|
||||||
.add('高德地图2.0 heatmap/hexagon/world', () => <Amap2demo_heatmap_hexagon_world />)
|
.add('高德地图2.0 heatmap/hexagon/world', () => <Amap2demo_heatmap_hexagon_world />)
|
||||||
.add('高德地图2.0 heatmap3D/grid', () => <Amap2demo_heatmap_grid />)
|
.add('高德地图2.0 heatmap3D/grid', () => <Amap2demo_heatmap_grid />)
|
||||||
|
|
||||||
.add('高德地图2.0 imageLayer', () => <Amap2demo_imageLayer />)
|
.add('高德地图2.0 imageLayer', () => <Amap2demo_imageLayer />)
|
||||||
|
.add('高德地图2.0 imageTileLayer', () => <Amap2demo_imageTileLayer />)
|
||||||
|
|
||||||
.add('高德地图2.0 rasterLayer', () => <Amap2demo_rasterLayer />)
|
.add('高德地图2.0 rasterLayer', () => <Amap2demo_rasterLayer />)
|
||||||
.add('高德地图2.0 citybuildLayer', () => <Amap2demo_citybuilding />)
|
.add('高德地图2.0 citybuildLayer', () => <Amap2demo_citybuilding />)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue