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:
YiQianYao 2021-10-25 17:03:51 +08:00 committed by GitHub
parent 7f6f07462c
commit e94cd2ca0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 954 additions and 28 deletions

View File

@ -197,5 +197,7 @@
"tnpm": {
"mode": "yarn"
},
"dependencies": {}
"dependencies": {
"@antv/geo-coord": "^1.0.8"
}
}

View File

@ -101,6 +101,8 @@ export interface ILayer {
layerModelNeedUpdate: boolean;
styleNeedUpdate: boolean;
layerModel: ILayerModel;
layerChildren: ILayer[]; // 在图层中添加子图层
sceneContainer: Container | undefined;
dataState: IDataState; // 数据流状态
pickedFeatureID: number | null;
hooks: {
@ -137,7 +139,7 @@ export interface ILayer {
needPick(type: string): boolean;
getLayerConfig(): Partial<ILayerConfig & ISceneConfig>;
getContainer(): Container;
setContainer(container: Container): void;
setContainer(container: Container, sceneContainer: Container): void;
setCurrentPickId(id: number | null): void;
getCurrentPickId(): number | null;
setCurrentSelectedId(id: number | null): void;
@ -377,7 +379,7 @@ export interface ILayerService {
getLayers(): ILayer[];
getLayer(id: string): ILayer | undefined;
getLayerByName(name: string): ILayer | undefined;
remove(layer: ILayer): void;
remove(layer: ILayer, parentLayer?: ILayer): void;
removeAllLayers(): void;
updateRenderOrder(): void;
renderLayers(type?: string): void;

View File

@ -64,11 +64,20 @@ export default class LayerService implements ILayerService {
return this.layers.find((layer) => layer.name === name);
}
public remove(layer: ILayer): void {
const layerIndex = this.layers.indexOf(layer);
if (layerIndex > -1) {
this.layers.splice(layerIndex, 1);
public remove(layer: ILayer, parentLayer?: ILayer): void {
// Tip: layer.layerChildren 当 layer 存在子图层的情况
if (parentLayer) {
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.destroy();
this.renderLayers();
@ -91,17 +100,29 @@ export default class LayerService implements ILayerService {
this.alreadyInRendering = true;
this.clear();
this.updateRenderOrder();
this.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();
// Tip: 渲染 layer 的子图层 默认 layerChildren 为空数组 表示没有子图层 目前只有 ImageTileLayer 有子图层
renderLayerEvent(layer.layerChildren);
renderLayerEvent([layer]);
});
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() {
@ -111,7 +132,14 @@ export default class LayerService implements ILayerService {
}
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.renderLayers();
}

View File

@ -187,7 +187,7 @@ export interface IEarthService<RawMap = {}> {
): void;
}
export const MapServiceEvent = ['mapload'];
export const MapServiceEvent = ['mapload', 'mapchange'];
/**
*

View File

@ -113,6 +113,11 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
public layerModel: ILayerModel;
// TODO: 记录 sceneContainer 供创建子图层的时候使用 如 imageTileLayer
public sceneContainer: Container | undefined;
// TODO: 用于保存子图层对象
public layerChildren: ILayer[] = [];
@lazyInject(TYPES.IGlobalConfigService)
protected readonly configService: IGlobalConfigService;
@ -222,8 +227,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
* -> SceneContainer 1.*
* -> LayerContainer 1.*
*/
public setContainer(container: Container) {
public setContainer(container: Container, sceneContainer: Container) {
this.container = container;
this.sceneContainer = sceneContainer;
}
public getContainer() {

View File

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

View File

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

View File

@ -0,0 +1,8 @@
import ImageTileModel from './imagetile';
export type ImageTileModelType = 'imageTile';
const ImageTileModels: { [key in ImageTileModelType]: any } = {
imageTile: ImageTileModel,
};
export default ImageTileModels;

View File

@ -0,0 +1,4 @@
precision mediump float;
void main() {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import BaseLayer from './core/BaseLayer';
import './glsl.d';
import HeatmapLayer from './heatmap';
import ImageLayer from './image';
import ImageTileLayer from './imagetile';
import LineLayer from './line/index';
import PointLayer from './point';
import PolygonLayer from './polygon';
@ -25,6 +26,7 @@ import RegisterStyleAttributePlugin from './plugins/RegisterStyleAttributePlugin
import ShaderUniformPlugin from './plugins/ShaderUniformPlugin';
import UpdateModelPlugin from './plugins/UpdateModelPlugin';
import UpdateStyleAttributePlugin from './plugins/UpdateStyleAttributePlugin';
/**
*
* @see /dev-docs/ConfigSchemaValidation.md
@ -137,6 +139,7 @@ export {
LineLayer,
CityBuildingLayer,
ImageLayer,
ImageTileLayer,
RasterLayer,
HeatmapLayer,
EarthLayer,

View File

@ -459,6 +459,8 @@ export default class AMapService
position,
} = e.camera;
const { lng, lat } = this.getCenter();
// Tip: 触发地图变化事件
this.emit('mapchange');
if (this.cameraChangedCallback) {
// resync viewport
// console.log('cameraHeight', height)

View File

@ -558,6 +558,8 @@ export default class AMapService
// left, right, bottom, top
// @ts-ignore
} = this.map.customCoords?.getCameraParams();
// Tip: 统一触发地图变化事件
this.emit('mapchange');
// // @ts-ignore
// console.log('this.map.customCoords.getCameraParams()', 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
// @ts-ignore
} = this.map.customCoords.getCameraParams();
// Tip: 统一触发地图变化事件
this.emit('mapchange');
const { zoom } = e;
// @ts-ignore
const center = this.map.customCoords.getCenter() as [number, number];

View File

@ -337,6 +337,8 @@ export default class L7EarthService implements IEarthService<Map> {
}
private handleCameraChanged = (e: any) => {
// Tip: 统一触发地图变化事件
this.emit('mapchange');
const DELAY_TIME = 2000;
this.handleCameraChanging = true;
if (this.handleCameraTimer) {

View File

@ -320,7 +320,8 @@ export default class L7MapService implements IMapService<Map> {
private handleCameraChanged = () => {
const { lat, lng } = this.map.getCenter();
const { offsetCoordinate = true } = this.config;
// Tip: 统一触发地图变化事件
this.emit('mapchange');
// resync
this.viewport.syncWithMapCamera({
bearing: this.map.getBearing(),

View File

@ -403,7 +403,8 @@ export default class MapboxService
private handleCameraChanged = () => {
// @see https://github.com/mapbox/mapbox-gl-js/issues/2572
const { lat, lng } = this.map.getCenter().wrap();
// Tip: 统一触发地图变化事件
this.emit('mapchange');
// resync
this.viewport.syncWithMapCamera({
bearing: this.map.getBearing(),

View File

@ -162,7 +162,7 @@ class Scene
// 为当前图层创建一个容器
// TODO: 初始化的时候设置 容器
const layerContainer = createLayerContainer(this.container);
layer.setContainer(layerContainer);
layer.setContainer(layerContainer, this.container);
this.sceneService.addLayer(layer);
}
@ -178,8 +178,8 @@ class Scene
return this.layerService.getLayerByName(name);
}
public removeLayer(layer: ILayer): void {
this.layerService.remove(layer);
public removeLayer(layer: ILayer, parentLayer?: ILayer): void {
this.layerService.remove(layer, parentLayer);
}
public removeAllLayer(): void {

View File

@ -207,6 +207,14 @@ export default class Source extends EventEmitter implements ISource {
private excuteParser(): void {
const parser = this.parser;
const type: string = parser.type || 'geojson';
// TODO: 图片瓦片地图组件只需要使用 url 参数
if (type === 'imagetile') {
this.data = {
tileurl: this.originData,
dataArray: [],
};
return;
}
const sourceParser = getParser(type);
this.data = sourceParser(this.originData, parser);
// 计算范围

View File

@ -137,7 +137,6 @@ export default class Aspace extends React.Component {
let r = heightData[i * 4];
let g = heightData[i * 4 + 1];
let b = heightData[i * 4 + 2];
let h =
-10000.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;
layer.adjustMeshToMap(antModel);
layer.setMeshScale(antModel, 20, 20, 20);
layer.setObjectLngLat(
antModel,
[center.lng - 0.002, center.lat],
@ -178,13 +176,9 @@ export default class Aspace extends React.Component {
const animations = gltf.animations;
if (animations && animations.length) {
const mixer = new THREE.AnimationMixer(antModel);
const animation = animations[1];
const action = mixer.clipAction(animation);
action.play();
layer.addAnimateMixer(mixer);
}
antModel.rotation.y = Math.PI;
@ -358,8 +352,7 @@ export default class Aspace extends React.Component {
currentView.pitch = scene.getPitch();
currentView.zoom = scene.getZoom();
});
// @ts-ignore
scene?.map?.on('camerachange', (e: any) => {
scene.getMapService().on('mapchange', (e: any) => {
// @ts-ignore
currentCamera = threeJSLayer.threeRenderService.getRenderCamera();
currentView.pitch = scene.getPitch();

View File

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

View File

@ -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_imageLayer from "./components/amap2demo_imagelayer"
import Amap2demo_imageTileLayer from "./components/amap2demo_imageTileLayer"
import Amap2demo_rasterLayer from "./components/amap2demo_rasterlayer"
@ -99,7 +100,10 @@ storiesOf('地图方法', module)
.add('高德地图2.0 heatmap3D/hexagon', () => <Amap2demo_heatmap_hexagon />)
.add('高德地图2.0 heatmap/hexagon/world', () => <Amap2demo_heatmap_hexagon_world />)
.add('高德地图2.0 heatmap3D/grid', () => <Amap2demo_heatmap_grid />)
.add('高德地图2.0 imageLayer', () => <Amap2demo_imageLayer />)
.add('高德地图2.0 imageTileLayer', () => <Amap2demo_imageTileLayer />)
.add('高德地图2.0 rasterLayer', () => <Amap2demo_rasterLayer />)
.add('高德地图2.0 citybuildLayer', () => <Amap2demo_citybuilding />)