mirror of https://gitee.com/antv-l7/antv-l7
improvement: 增加L7 logo是否显示属性
This commit is contained in:
parent
705be24204
commit
62da56dc22
|
@ -76,6 +76,22 @@ const scene = new L7.Scene({
|
||||||
|
|
||||||
需传入 dom 容器或者容器 id {domObject || string} [必选]
|
需传入 dom 容器或者容器 id {domObject || string} [必选]
|
||||||
|
|
||||||
|
### logoPosition
|
||||||
|
|
||||||
|
L7 Logo的显示位置 默认左下角
|
||||||
|
|
||||||
|
- bottomright
|
||||||
|
- topright
|
||||||
|
- bottomleft,
|
||||||
|
- topleft`
|
||||||
|
|
||||||
|
|
||||||
|
### logoVisible
|
||||||
|
|
||||||
|
是否显示L7的Logo {boolean} true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### zoom
|
### zoom
|
||||||
|
|
||||||
地图初始显示级别 {number} (0-22)
|
地图初始显示级别 {number} (0-22)
|
||||||
|
|
|
@ -12,6 +12,8 @@ import WarnInfo, { IWarnInfo } from './warnInfo';
|
||||||
*/
|
*/
|
||||||
const defaultSceneConfig: Partial<ISceneConfig> = {
|
const defaultSceneConfig: Partial<ISceneConfig> = {
|
||||||
id: 'map',
|
id: 'map',
|
||||||
|
logoPosition: 'bottomleft',
|
||||||
|
logoVisible: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import Ajv from 'ajv';
|
import Ajv from 'ajv';
|
||||||
|
import { PositionName } from '../component/IControlService';
|
||||||
import { ILayerConfig } from '../layer/ILayerService';
|
import { ILayerConfig } from '../layer/ILayerService';
|
||||||
import { IMapWrapper } from '../map/IMapService';
|
import { IMapWrapper } from '../map/IMapService';
|
||||||
import { IRenderConfig } from '../renderer/IRendererService';
|
import { IRenderConfig } from '../renderer/IRendererService';
|
||||||
|
|
||||||
export interface ISceneConfig extends IRenderConfig {
|
export interface ISceneConfig extends IRenderConfig {
|
||||||
id: string | HTMLDivElement;
|
id: string | HTMLDivElement;
|
||||||
map: IMapWrapper;
|
map: IMapWrapper;
|
||||||
|
logoPosition?: PositionName;
|
||||||
|
logoVisible?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IValidateResult {
|
interface IValidateResult {
|
||||||
|
|
|
@ -10,7 +10,7 @@ export interface ISceneService {
|
||||||
addLayer(layer: ILayer): void;
|
addLayer(layer: ILayer): void;
|
||||||
render(): void;
|
render(): void;
|
||||||
getSceneContainer(): HTMLDivElement;
|
getSceneContainer(): HTMLDivElement;
|
||||||
ExportMap2Png(): string;
|
exportPng(): string;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
// scene 事件
|
// scene 事件
|
||||||
|
|
|
@ -226,10 +226,11 @@ export default class Scene extends EventEmitter implements ISceneService {
|
||||||
return this.$container as HTMLDivElement;
|
return this.$container as HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExportMap2Png(): string {
|
public exportPng(): string {
|
||||||
const renderCanvas = this.$container?.getElementsByTagName('canvas')[0];
|
const renderCanvas = this.$container?.getElementsByTagName('canvas')[0];
|
||||||
this.render();
|
// this.render();
|
||||||
const layersPng = renderCanvas?.toDataURL() as string;
|
DOM.printCanvas(renderCanvas as HTMLCanvasElement);
|
||||||
|
const layersPng = renderCanvas?.toDataURL('image/png') as string;
|
||||||
return layersPng;
|
return layersPng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,11 +238,11 @@ export default class Scene extends EventEmitter implements ISceneService {
|
||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
this.inited = false;
|
this.inited = false;
|
||||||
this.layerService.destroy();
|
this.layerService.destroy();
|
||||||
|
this.rendererService.destroy();
|
||||||
this.interactionService.destroy();
|
this.interactionService.destroy();
|
||||||
this.controlService.destroy();
|
this.controlService.destroy();
|
||||||
this.markerService.destroy();
|
this.markerService.destroy();
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
this.rendererService.destroy();
|
|
||||||
this.map.destroy();
|
this.map.destroy();
|
||||||
unbind(this.$container as HTMLDivElement, this.handleWindowResized);
|
unbind(this.$container as HTMLDivElement, this.handleWindowResized);
|
||||||
window
|
window
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { IAMapInstance } from '../../typings/index';
|
|
||||||
import BaseMapWrapper from '../BaseMapWrapper';
|
|
||||||
import AMapService from './index';
|
|
||||||
|
|
||||||
export default class AMapWrapper extends BaseMapWrapper<
|
|
||||||
AMap.Map & IAMapInstance
|
|
||||||
> {
|
|
||||||
protected getServiceConstructor() {
|
|
||||||
return AMapService;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,393 +1,11 @@
|
||||||
/**
|
import { IAMapInstance } from '../../typings/index';
|
||||||
* AMapService
|
import BaseMapWrapper from '../BaseMapWrapper';
|
||||||
*/
|
import AMapService from './map';
|
||||||
import {
|
|
||||||
Bounds,
|
|
||||||
CoordinateSystem,
|
|
||||||
ICoordinateSystemService,
|
|
||||||
IGlobalConfigService,
|
|
||||||
ILngLat,
|
|
||||||
ILogService,
|
|
||||||
IMapConfig,
|
|
||||||
IMapService,
|
|
||||||
IPoint,
|
|
||||||
IViewport,
|
|
||||||
MapServiceEvent,
|
|
||||||
MapStyle,
|
|
||||||
TYPES,
|
|
||||||
} from '@antv/l7-core';
|
|
||||||
import { DOM } from '@antv/l7-utils';
|
|
||||||
import { inject, injectable } from 'inversify';
|
|
||||||
import { IAMapEvent, IAMapInstance } from '../../typings/index';
|
|
||||||
import { MapTheme } from './theme';
|
|
||||||
import Viewport from './Viewport';
|
|
||||||
let mapdivCount = 0;
|
|
||||||
|
|
||||||
const AMAP_API_KEY: string = '15cd8a57710d40c9b7c0e3cc120f1200';
|
export default class AMapWrapper extends BaseMapWrapper<
|
||||||
const AMAP_VERSION: string = '1.4.15';
|
AMap.Map & IAMapInstance
|
||||||
/**
|
> {
|
||||||
* 确保多个场景只引入一个高德地图脚本
|
protected getServiceConstructor() {
|
||||||
*/
|
return AMapService;
|
||||||
const AMAP_SCRIPT_ID: string = 'amap-script';
|
|
||||||
/**
|
|
||||||
* 高德地图脚本是否加载完毕
|
|
||||||
*/
|
|
||||||
let amapLoaded = false;
|
|
||||||
/**
|
|
||||||
* 高德地图脚本加载成功等待队列,成功之后依次触发
|
|
||||||
*/
|
|
||||||
let pendingResolveQueue: Array<() => void> = [];
|
|
||||||
const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; // 暂时关闭 fix 统一不同坐标系,不同底图的高度位置
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMapService
|
|
||||||
*/
|
|
||||||
@injectable()
|
|
||||||
export default class AMapService
|
|
||||||
implements IMapService<AMap.Map & IAMapInstance> {
|
|
||||||
/**
|
|
||||||
* 原始地图实例
|
|
||||||
*/
|
|
||||||
public map: AMap.Map & IAMapInstance;
|
|
||||||
|
|
||||||
@inject(TYPES.IGlobalConfigService)
|
|
||||||
private readonly configService: IGlobalConfigService;
|
|
||||||
|
|
||||||
@inject(TYPES.ILogService)
|
|
||||||
private readonly logger: ILogService;
|
|
||||||
|
|
||||||
@inject(TYPES.MapConfig)
|
|
||||||
private readonly config: Partial<IMapConfig>;
|
|
||||||
|
|
||||||
@inject(TYPES.ICoordinateSystemService)
|
|
||||||
private readonly coordinateSystemService: ICoordinateSystemService;
|
|
||||||
|
|
||||||
@inject(TYPES.IEventEmitter)
|
|
||||||
private eventEmitter: any;
|
|
||||||
|
|
||||||
private markerContainer: HTMLElement;
|
|
||||||
private $mapContainer: HTMLElement | null;
|
|
||||||
|
|
||||||
private viewport: Viewport;
|
|
||||||
|
|
||||||
private cameraChangedCallback: (viewport: IViewport) => void;
|
|
||||||
|
|
||||||
public addMarkerContainer(): void {
|
|
||||||
const mapContainer = this.map.getContainer();
|
|
||||||
if (mapContainer !== null) {
|
|
||||||
const amap = mapContainer.getElementsByClassName(
|
|
||||||
'amap-maps',
|
|
||||||
)[0] as HTMLElement;
|
|
||||||
this.markerContainer = DOM.create('div', 'l7-marker-container', amap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public getMarkerContainer(): HTMLElement {
|
|
||||||
return this.markerContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// map event
|
|
||||||
public on(type: string, handler: (...args: any[]) => void): void {
|
|
||||||
if (MapServiceEvent.indexOf(type) !== -1) {
|
|
||||||
this.eventEmitter.on(type, handler);
|
|
||||||
} else {
|
|
||||||
this.map.on(type, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public off(type: string, handler: (...args: any[]) => void): void {
|
|
||||||
if (MapServiceEvent.indexOf(type) !== -1) {
|
|
||||||
this.eventEmitter.off(type, handler);
|
|
||||||
} else {
|
|
||||||
this.map.off(type, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getContainer(): HTMLElement | null {
|
|
||||||
return this.map.getContainer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public getSize(): [number, number] {
|
|
||||||
const size = this.map.getSize();
|
|
||||||
return [size.getWidth(), size.getHeight()];
|
|
||||||
}
|
|
||||||
|
|
||||||
public getType() {
|
|
||||||
return 'amap';
|
|
||||||
}
|
|
||||||
public getZoom(): number {
|
|
||||||
// 统一返回 Mapbox 缩放等级
|
|
||||||
return this.map.getZoom() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public setZoom(zoom: number): void {
|
|
||||||
return this.map.setZoom(zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCenter(): ILngLat {
|
|
||||||
const center = this.map.getCenter();
|
|
||||||
return {
|
|
||||||
lng: center.getLng(),
|
|
||||||
lat: center.getLat(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public getPitch(): number {
|
|
||||||
return this.map.getPitch();
|
|
||||||
}
|
|
||||||
|
|
||||||
public getRotation(): number {
|
|
||||||
// 统一返回逆时针旋转角度
|
|
||||||
return 360 - this.map.getRotation();
|
|
||||||
}
|
|
||||||
|
|
||||||
public getBounds(): Bounds {
|
|
||||||
// @ts-ignore
|
|
||||||
const amapBound = this.map.getBounds().toBounds();
|
|
||||||
const NE = amapBound.getNorthEast();
|
|
||||||
const SW = amapBound.getSouthWest();
|
|
||||||
const center = this.getCenter();
|
|
||||||
const maxlng =
|
|
||||||
center.lng > NE.getLng() || center.lng < SW.getLng()
|
|
||||||
? 180 - NE.getLng()
|
|
||||||
: NE.getLng();
|
|
||||||
const minlng = center.lng < SW.getLng() ? SW.getLng() - 180 : SW.getLng();
|
|
||||||
// 兼容 Mapbox,统一返回西南、东北
|
|
||||||
return [
|
|
||||||
[minlng, SW.getLat()],
|
|
||||||
[maxlng, NE.getLat()],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMinZoom(): number {
|
|
||||||
const zooms = this.map.get('zooms') as [number, number];
|
|
||||||
return zooms[0] - 1;
|
|
||||||
}
|
|
||||||
public getMaxZoom(): number {
|
|
||||||
const zooms = this.map.get('zooms') as [number, number];
|
|
||||||
return zooms[1] - 1;
|
|
||||||
}
|
|
||||||
public setRotation(rotation: number): void {
|
|
||||||
return this.map.setRotation(rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public zoomIn(): void {
|
|
||||||
this.map.zoomIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
public zoomOut(): void {
|
|
||||||
this.map.zoomOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
public panTo(p: [number, number]): void {
|
|
||||||
this.map.panTo(p);
|
|
||||||
}
|
|
||||||
public panBy(pixel: [number, number]): void {
|
|
||||||
this.map.panTo(pixel);
|
|
||||||
}
|
|
||||||
public fitBounds(extent: Bounds): void {
|
|
||||||
this.map.setBounds(
|
|
||||||
new AMap.Bounds([extent[0][0], extent[0][1], extent[1][0], extent[1][1]]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public setZoomAndCenter(zoom: number, center: [number, number]): void {
|
|
||||||
this.map.setZoomAndCenter(zoom, center);
|
|
||||||
}
|
|
||||||
public setMapStyle(style: string): void {
|
|
||||||
this.map.setMapStyle(this.getMapStyle(style));
|
|
||||||
}
|
|
||||||
public pixelToLngLat(pixel: [number, number]): ILngLat {
|
|
||||||
const lngLat = this.map.pixelToLngLat(new AMap.Pixel(pixel[0], pixel[1]));
|
|
||||||
return { lng: lngLat.getLng(), lat: lngLat.getLat() };
|
|
||||||
}
|
|
||||||
public lngLatToPixel(lnglat: [number, number]): IPoint {
|
|
||||||
const p = this.map.lnglatToPixel(new AMap.LngLat(lnglat[0], lnglat[1]));
|
|
||||||
return {
|
|
||||||
x: p.getX(),
|
|
||||||
y: p.getY(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public containerToLngLat(pixel: [number, number]): ILngLat {
|
|
||||||
const ll = new AMap.Pixel(pixel[0], pixel[1]);
|
|
||||||
const lngLat = this.map.containerToLngLat(ll);
|
|
||||||
return {
|
|
||||||
lng: lngLat?.getLng(),
|
|
||||||
lat: lngLat?.getLat(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public lngLatToContainer(lnglat: [number, number]): IPoint {
|
|
||||||
const ll = new AMap.LngLat(lnglat[0], lnglat[1]);
|
|
||||||
const pixel = this.map.lngLatToContainer(ll);
|
|
||||||
return {
|
|
||||||
x: pixel.getX(),
|
|
||||||
y: pixel.getY(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
style = 'light',
|
|
||||||
minZoom = 0,
|
|
||||||
maxZoom = 18,
|
|
||||||
token = AMAP_API_KEY,
|
|
||||||
mapInstance,
|
|
||||||
plugin = [],
|
|
||||||
...rest
|
|
||||||
} = this.config;
|
|
||||||
// 高德地图创建独立的container;
|
|
||||||
// tslint:disable-next-line:typedef
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
const resolveMap = () => {
|
|
||||||
if (mapInstance) {
|
|
||||||
this.map = mapInstance as AMap.Map & IAMapInstance;
|
|
||||||
this.$mapContainer = this.map.getContainer();
|
|
||||||
setTimeout(() => {
|
|
||||||
this.map.on('camerachange', this.handleCameraChanged);
|
|
||||||
resolve();
|
|
||||||
}, 30);
|
|
||||||
} else {
|
|
||||||
this.$mapContainer = this.creatAmapContainer(
|
|
||||||
id as string | HTMLDivElement,
|
|
||||||
);
|
|
||||||
|
|
||||||
const map = new AMap.Map(this.$mapContainer, {
|
|
||||||
mapStyle: this.getMapStyle(style as string),
|
|
||||||
zooms: [minZoom, maxZoom],
|
|
||||||
viewMode: '3D',
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
// 监听地图相机事件
|
|
||||||
map.on('camerachange', this.handleCameraChanged);
|
|
||||||
// @ts-ignore
|
|
||||||
this.map = map;
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve();
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!amapLoaded && !mapInstance) {
|
|
||||||
if (token === AMAP_API_KEY) {
|
|
||||||
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
|
|
||||||
}
|
|
||||||
amapLoaded = true;
|
|
||||||
plugin.push('Map3D');
|
|
||||||
this.loadAMapScript(
|
|
||||||
`https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${token}&plugin=${plugin.join(
|
|
||||||
',',
|
|
||||||
)}`,
|
|
||||||
).then(() => {
|
|
||||||
resolveMap();
|
|
||||||
if (pendingResolveQueue.length) {
|
|
||||||
pendingResolveQueue.forEach((r) => r());
|
|
||||||
pendingResolveQueue = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if ((amapLoaded && window.AMap) || mapInstance) {
|
|
||||||
resolveMap();
|
|
||||||
} else {
|
|
||||||
pendingResolveQueue.push(resolveMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.viewport = new Viewport();
|
|
||||||
}
|
|
||||||
public emit(name: string, ...args: any[]) {
|
|
||||||
this.eventEmitter.emit(name, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public once(name: string, ...args: any[]) {
|
|
||||||
this.eventEmitter.once(name, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public destroy() {
|
|
||||||
this.map.destroy();
|
|
||||||
// @ts-ignore
|
|
||||||
delete window.initAMap;
|
|
||||||
const $jsapi = document.getElementById(AMAP_SCRIPT_ID);
|
|
||||||
if ($jsapi) {
|
|
||||||
document.head.removeChild($jsapi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMapContainer() {
|
|
||||||
return this.$mapContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onCameraChanged(callback: (viewport: IViewport) => void): void {
|
|
||||||
this.cameraChangedCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleCameraChanged = (e: IAMapEvent): void => {
|
|
||||||
const {
|
|
||||||
fov,
|
|
||||||
near,
|
|
||||||
far,
|
|
||||||
height,
|
|
||||||
pitch,
|
|
||||||
rotation,
|
|
||||||
aspect,
|
|
||||||
position,
|
|
||||||
} = e.camera;
|
|
||||||
const { lng, lat } = this.getCenter();
|
|
||||||
if (this.cameraChangedCallback) {
|
|
||||||
// resync viewport
|
|
||||||
this.viewport.syncWithMapCamera({
|
|
||||||
aspect,
|
|
||||||
// AMap 定义 rotation 为顺时针方向,而 Mapbox 为逆时针
|
|
||||||
// @see https://docs.mapbox.com/mapbox-gl-js/api/#map#getbearing
|
|
||||||
bearing: 360 - rotation,
|
|
||||||
far,
|
|
||||||
fov,
|
|
||||||
cameraHeight: height,
|
|
||||||
near,
|
|
||||||
pitch,
|
|
||||||
// AMap 定义的缩放等级 与 Mapbox 相差 1
|
|
||||||
zoom: this.map.getZoom() - 1,
|
|
||||||
center: [lng, lat],
|
|
||||||
offsetOrigin: [position.x, position.y],
|
|
||||||
});
|
|
||||||
|
|
||||||
// set coordinate system
|
|
||||||
if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) {
|
|
||||||
this.coordinateSystemService.setCoordinateSystem(
|
|
||||||
CoordinateSystem.P20_OFFSET,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20);
|
|
||||||
}
|
|
||||||
this.cameraChangedCallback(this.viewport);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private getMapStyle(name: string): string {
|
|
||||||
return MapTheme[name] ? MapTheme[name] : name;
|
|
||||||
}
|
|
||||||
private creatAmapContainer(id: string | HTMLDivElement) {
|
|
||||||
let $wrapper = id as HTMLDivElement;
|
|
||||||
if (typeof id === 'string') {
|
|
||||||
$wrapper = document.getElementById(id) as HTMLDivElement;
|
|
||||||
}
|
|
||||||
const $amapdiv = document.createElement('div');
|
|
||||||
$amapdiv.style.cssText += `
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
$amapdiv.id = 'l7_amap_div' + mapdivCount++;
|
|
||||||
$wrapper.appendChild($amapdiv);
|
|
||||||
return $amapdiv;
|
|
||||||
}
|
|
||||||
private loadAMapScript(src: string) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = src;
|
|
||||||
script.onload = () => {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
script.onerror = reject;
|
|
||||||
document.head.appendChild(script);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,393 @@
|
||||||
|
/**
|
||||||
|
* AMapService
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Bounds,
|
||||||
|
CoordinateSystem,
|
||||||
|
ICoordinateSystemService,
|
||||||
|
IGlobalConfigService,
|
||||||
|
ILngLat,
|
||||||
|
ILogService,
|
||||||
|
IMapConfig,
|
||||||
|
IMapService,
|
||||||
|
IPoint,
|
||||||
|
IViewport,
|
||||||
|
MapServiceEvent,
|
||||||
|
MapStyle,
|
||||||
|
TYPES,
|
||||||
|
} from '@antv/l7-core';
|
||||||
|
import { DOM } from '@antv/l7-utils';
|
||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import { IAMapEvent, IAMapInstance } from '../../typings/index';
|
||||||
|
import { MapTheme } from './theme';
|
||||||
|
import Viewport from './Viewport';
|
||||||
|
let mapdivCount = 0;
|
||||||
|
|
||||||
|
const AMAP_API_KEY: string = '15cd8a57710d40c9b7c0e3cc120f1200';
|
||||||
|
const AMAP_VERSION: string = '1.4.15';
|
||||||
|
/**
|
||||||
|
* 确保多个场景只引入一个高德地图脚本
|
||||||
|
*/
|
||||||
|
const AMAP_SCRIPT_ID: string = 'amap-script';
|
||||||
|
/**
|
||||||
|
* 高德地图脚本是否加载完毕
|
||||||
|
*/
|
||||||
|
let amapLoaded = false;
|
||||||
|
/**
|
||||||
|
* 高德地图脚本加载成功等待队列,成功之后依次触发
|
||||||
|
*/
|
||||||
|
let pendingResolveQueue: Array<() => void> = [];
|
||||||
|
const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; // 暂时关闭 fix 统一不同坐标系,不同底图的高度位置
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AMapService
|
||||||
|
*/
|
||||||
|
@injectable()
|
||||||
|
export default class AMapService
|
||||||
|
implements IMapService<AMap.Map & IAMapInstance> {
|
||||||
|
/**
|
||||||
|
* 原始地图实例
|
||||||
|
*/
|
||||||
|
public map: AMap.Map & IAMapInstance;
|
||||||
|
|
||||||
|
@inject(TYPES.IGlobalConfigService)
|
||||||
|
private readonly configService: IGlobalConfigService;
|
||||||
|
|
||||||
|
@inject(TYPES.ILogService)
|
||||||
|
private readonly logger: ILogService;
|
||||||
|
|
||||||
|
@inject(TYPES.MapConfig)
|
||||||
|
private readonly config: Partial<IMapConfig>;
|
||||||
|
|
||||||
|
@inject(TYPES.ICoordinateSystemService)
|
||||||
|
private readonly coordinateSystemService: ICoordinateSystemService;
|
||||||
|
|
||||||
|
@inject(TYPES.IEventEmitter)
|
||||||
|
private eventEmitter: any;
|
||||||
|
|
||||||
|
private markerContainer: HTMLElement;
|
||||||
|
private $mapContainer: HTMLElement | null;
|
||||||
|
|
||||||
|
private viewport: Viewport;
|
||||||
|
|
||||||
|
private cameraChangedCallback: (viewport: IViewport) => void;
|
||||||
|
|
||||||
|
public addMarkerContainer(): void {
|
||||||
|
const mapContainer = this.map.getContainer();
|
||||||
|
if (mapContainer !== null) {
|
||||||
|
const amap = mapContainer.getElementsByClassName(
|
||||||
|
'amap-maps',
|
||||||
|
)[0] as HTMLElement;
|
||||||
|
this.markerContainer = DOM.create('div', 'l7-marker-container', amap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public getMarkerContainer(): HTMLElement {
|
||||||
|
return this.markerContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// map event
|
||||||
|
public on(type: string, handler: (...args: any[]) => void): void {
|
||||||
|
if (MapServiceEvent.indexOf(type) !== -1) {
|
||||||
|
this.eventEmitter.on(type, handler);
|
||||||
|
} else {
|
||||||
|
this.map.on(type, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public off(type: string, handler: (...args: any[]) => void): void {
|
||||||
|
if (MapServiceEvent.indexOf(type) !== -1) {
|
||||||
|
this.eventEmitter.off(type, handler);
|
||||||
|
} else {
|
||||||
|
this.map.off(type, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getContainer(): HTMLElement | null {
|
||||||
|
return this.map.getContainer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSize(): [number, number] {
|
||||||
|
const size = this.map.getSize();
|
||||||
|
return [size.getWidth(), size.getHeight()];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getType() {
|
||||||
|
return 'amap';
|
||||||
|
}
|
||||||
|
public getZoom(): number {
|
||||||
|
// 统一返回 Mapbox 缩放等级
|
||||||
|
return this.map.getZoom() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setZoom(zoom: number): void {
|
||||||
|
return this.map.setZoom(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCenter(): ILngLat {
|
||||||
|
const center = this.map.getCenter();
|
||||||
|
return {
|
||||||
|
lng: center.getLng(),
|
||||||
|
lat: center.getLat(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPitch(): number {
|
||||||
|
return this.map.getPitch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRotation(): number {
|
||||||
|
// 统一返回逆时针旋转角度
|
||||||
|
return 360 - this.map.getRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBounds(): Bounds {
|
||||||
|
// @ts-ignore
|
||||||
|
const amapBound = this.map.getBounds().toBounds();
|
||||||
|
const NE = amapBound.getNorthEast();
|
||||||
|
const SW = amapBound.getSouthWest();
|
||||||
|
const center = this.getCenter();
|
||||||
|
const maxlng =
|
||||||
|
center.lng > NE.getLng() || center.lng < SW.getLng()
|
||||||
|
? 180 - NE.getLng()
|
||||||
|
: NE.getLng();
|
||||||
|
const minlng = center.lng < SW.getLng() ? SW.getLng() - 180 : SW.getLng();
|
||||||
|
// 兼容 Mapbox,统一返回西南、东北
|
||||||
|
return [
|
||||||
|
[minlng, SW.getLat()],
|
||||||
|
[maxlng, NE.getLat()],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMinZoom(): number {
|
||||||
|
const zooms = this.map.get('zooms') as [number, number];
|
||||||
|
return zooms[0] - 1;
|
||||||
|
}
|
||||||
|
public getMaxZoom(): number {
|
||||||
|
const zooms = this.map.get('zooms') as [number, number];
|
||||||
|
return zooms[1] - 1;
|
||||||
|
}
|
||||||
|
public setRotation(rotation: number): void {
|
||||||
|
return this.map.setRotation(rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public zoomIn(): void {
|
||||||
|
this.map.zoomIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public zoomOut(): void {
|
||||||
|
this.map.zoomOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
public panTo(p: [number, number]): void {
|
||||||
|
this.map.panTo(p);
|
||||||
|
}
|
||||||
|
public panBy(pixel: [number, number]): void {
|
||||||
|
this.map.panTo(pixel);
|
||||||
|
}
|
||||||
|
public fitBounds(extent: Bounds): void {
|
||||||
|
this.map.setBounds(
|
||||||
|
new AMap.Bounds([extent[0][0], extent[0][1], extent[1][0], extent[1][1]]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public setZoomAndCenter(zoom: number, center: [number, number]): void {
|
||||||
|
this.map.setZoomAndCenter(zoom, center);
|
||||||
|
}
|
||||||
|
public setMapStyle(style: string): void {
|
||||||
|
this.map.setMapStyle(this.getMapStyle(style));
|
||||||
|
}
|
||||||
|
public pixelToLngLat(pixel: [number, number]): ILngLat {
|
||||||
|
const lngLat = this.map.pixelToLngLat(new AMap.Pixel(pixel[0], pixel[1]));
|
||||||
|
return { lng: lngLat.getLng(), lat: lngLat.getLat() };
|
||||||
|
}
|
||||||
|
public lngLatToPixel(lnglat: [number, number]): IPoint {
|
||||||
|
const p = this.map.lnglatToPixel(new AMap.LngLat(lnglat[0], lnglat[1]));
|
||||||
|
return {
|
||||||
|
x: p.getX(),
|
||||||
|
y: p.getY(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public containerToLngLat(pixel: [number, number]): ILngLat {
|
||||||
|
const ll = new AMap.Pixel(pixel[0], pixel[1]);
|
||||||
|
const lngLat = this.map.containerToLngLat(ll);
|
||||||
|
return {
|
||||||
|
lng: lngLat?.getLng(),
|
||||||
|
lat: lngLat?.getLat(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public lngLatToContainer(lnglat: [number, number]): IPoint {
|
||||||
|
const ll = new AMap.LngLat(lnglat[0], lnglat[1]);
|
||||||
|
const pixel = this.map.lngLatToContainer(ll);
|
||||||
|
return {
|
||||||
|
x: pixel.getX(),
|
||||||
|
y: pixel.getY(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init(): Promise<void> {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
style = 'light',
|
||||||
|
minZoom = 0,
|
||||||
|
maxZoom = 18,
|
||||||
|
token = AMAP_API_KEY,
|
||||||
|
mapInstance,
|
||||||
|
plugin = [],
|
||||||
|
...rest
|
||||||
|
} = this.config;
|
||||||
|
// 高德地图创建独立的container;
|
||||||
|
// tslint:disable-next-line:typedef
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
const resolveMap = () => {
|
||||||
|
if (mapInstance) {
|
||||||
|
this.map = mapInstance as AMap.Map & IAMapInstance;
|
||||||
|
this.$mapContainer = this.map.getContainer();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.map.on('camerachange', this.handleCameraChanged);
|
||||||
|
resolve();
|
||||||
|
}, 30);
|
||||||
|
} else {
|
||||||
|
this.$mapContainer = this.creatAmapContainer(
|
||||||
|
id as string | HTMLDivElement,
|
||||||
|
);
|
||||||
|
|
||||||
|
const map = new AMap.Map(this.$mapContainer, {
|
||||||
|
mapStyle: this.getMapStyle(style as string),
|
||||||
|
zooms: [minZoom, maxZoom],
|
||||||
|
viewMode: '3D',
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
// 监听地图相机事件
|
||||||
|
map.on('camerachange', this.handleCameraChanged);
|
||||||
|
// @ts-ignore
|
||||||
|
this.map = map;
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve();
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!amapLoaded && !mapInstance) {
|
||||||
|
if (token === AMAP_API_KEY) {
|
||||||
|
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
|
||||||
|
}
|
||||||
|
amapLoaded = true;
|
||||||
|
plugin.push('Map3D');
|
||||||
|
this.loadAMapScript(
|
||||||
|
`https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${token}&plugin=${plugin.join(
|
||||||
|
',',
|
||||||
|
)}`,
|
||||||
|
).then(() => {
|
||||||
|
resolveMap();
|
||||||
|
if (pendingResolveQueue.length) {
|
||||||
|
pendingResolveQueue.forEach((r) => r());
|
||||||
|
pendingResolveQueue = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if ((amapLoaded && window.AMap) || mapInstance) {
|
||||||
|
resolveMap();
|
||||||
|
} else {
|
||||||
|
pendingResolveQueue.push(resolveMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.viewport = new Viewport();
|
||||||
|
}
|
||||||
|
public emit(name: string, ...args: any[]) {
|
||||||
|
this.eventEmitter.emit(name, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public once(name: string, ...args: any[]) {
|
||||||
|
this.eventEmitter.once(name, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
this.map.destroy();
|
||||||
|
// @ts-ignore
|
||||||
|
delete window.initAMap;
|
||||||
|
const $jsapi = document.getElementById(AMAP_SCRIPT_ID);
|
||||||
|
if ($jsapi) {
|
||||||
|
document.head.removeChild($jsapi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMapContainer() {
|
||||||
|
return this.$mapContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public onCameraChanged(callback: (viewport: IViewport) => void): void {
|
||||||
|
this.cameraChangedCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCameraChanged = (e: IAMapEvent): void => {
|
||||||
|
const {
|
||||||
|
fov,
|
||||||
|
near,
|
||||||
|
far,
|
||||||
|
height,
|
||||||
|
pitch,
|
||||||
|
rotation,
|
||||||
|
aspect,
|
||||||
|
position,
|
||||||
|
} = e.camera;
|
||||||
|
const { lng, lat } = this.getCenter();
|
||||||
|
if (this.cameraChangedCallback) {
|
||||||
|
// resync viewport
|
||||||
|
this.viewport.syncWithMapCamera({
|
||||||
|
aspect,
|
||||||
|
// AMap 定义 rotation 为顺时针方向,而 Mapbox 为逆时针
|
||||||
|
// @see https://docs.mapbox.com/mapbox-gl-js/api/#map#getbearing
|
||||||
|
bearing: 360 - rotation,
|
||||||
|
far,
|
||||||
|
fov,
|
||||||
|
cameraHeight: height,
|
||||||
|
near,
|
||||||
|
pitch,
|
||||||
|
// AMap 定义的缩放等级 与 Mapbox 相差 1
|
||||||
|
zoom: this.map.getZoom() - 1,
|
||||||
|
center: [lng, lat],
|
||||||
|
offsetOrigin: [position.x, position.y],
|
||||||
|
});
|
||||||
|
|
||||||
|
// set coordinate system
|
||||||
|
if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) {
|
||||||
|
this.coordinateSystemService.setCoordinateSystem(
|
||||||
|
CoordinateSystem.P20_OFFSET,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20);
|
||||||
|
}
|
||||||
|
this.cameraChangedCallback(this.viewport);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private getMapStyle(name: string): string {
|
||||||
|
return MapTheme[name] ? MapTheme[name] : name;
|
||||||
|
}
|
||||||
|
private creatAmapContainer(id: string | HTMLDivElement) {
|
||||||
|
let $wrapper = id as HTMLDivElement;
|
||||||
|
if (typeof id === 'string') {
|
||||||
|
$wrapper = document.getElementById(id) as HTMLDivElement;
|
||||||
|
}
|
||||||
|
const $amapdiv = document.createElement('div');
|
||||||
|
$amapdiv.style.cssText += `
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
$amapdiv.id = 'l7_amap_div' + mapdivCount++;
|
||||||
|
$wrapper.appendChild($amapdiv);
|
||||||
|
return $amapdiv;
|
||||||
|
}
|
||||||
|
private loadAMapScript(src: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = src;
|
||||||
|
script.onload = () => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
script.onerror = reject;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import GaodeMap from './amap/Wrapper';
|
import GaodeMap from './amap/';
|
||||||
import Mapbox from './mapbox/Wrapper';
|
import Mapbox from './mapbox/';
|
||||||
|
|
||||||
export { GaodeMap, Mapbox };
|
export { GaodeMap, Mapbox };
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { Map } from 'mapbox-gl';
|
|
||||||
import { IMapboxInstance } from '../../typings/index';
|
|
||||||
import BaseMapWrapper from '../BaseMapWrapper';
|
|
||||||
import MapboxService from './index';
|
|
||||||
|
|
||||||
export default class MapboxWrapper extends BaseMapWrapper<
|
|
||||||
Map & IMapboxInstance
|
|
||||||
> {
|
|
||||||
protected getServiceConstructor() {
|
|
||||||
return MapboxService;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,328 +1,12 @@
|
||||||
/**
|
import { Map } from 'mapbox-gl';
|
||||||
* MapboxService
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
Bounds,
|
|
||||||
CoordinateSystem,
|
|
||||||
ICoordinateSystemService,
|
|
||||||
IGlobalConfigService,
|
|
||||||
ILngLat,
|
|
||||||
ILogService,
|
|
||||||
IMapConfig,
|
|
||||||
IMapService,
|
|
||||||
IPoint,
|
|
||||||
IViewport,
|
|
||||||
MapServiceEvent,
|
|
||||||
MapStyle,
|
|
||||||
TYPES,
|
|
||||||
} from '@antv/l7-core';
|
|
||||||
import { DOM } from '@antv/l7-utils';
|
|
||||||
import { inject, injectable } from 'inversify';
|
|
||||||
import mapboxgl, { IControl, Map } from 'mapbox-gl';
|
|
||||||
import { IMapboxInstance } from '../../typings/index';
|
import { IMapboxInstance } from '../../typings/index';
|
||||||
import Viewport from './Viewport';
|
import BaseMapWrapper from '../BaseMapWrapper';
|
||||||
const EventMap: {
|
import MapboxService from './map';
|
||||||
[key: string]: any;
|
|
||||||
} = {
|
|
||||||
mapmove: 'move',
|
|
||||||
camerachange: 'move',
|
|
||||||
};
|
|
||||||
import { MapTheme } from './theme';
|
|
||||||
|
|
||||||
const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12;
|
export default class MapboxWrapper extends BaseMapWrapper<
|
||||||
const MAPBOX_API_KEY =
|
Map & IMapboxInstance
|
||||||
'pk.eyJ1IjoibHp4dWUiLCJhIjoiYnhfTURyRSJ9.Ugm314vAKPHBzcPmY1p4KQ';
|
> {
|
||||||
/**
|
protected getServiceConstructor() {
|
||||||
* AMapService
|
return MapboxService;
|
||||||
*/
|
|
||||||
@injectable()
|
|
||||||
export default class MapboxService
|
|
||||||
implements IMapService<Map & IMapboxInstance> {
|
|
||||||
public map: Map & IMapboxInstance;
|
|
||||||
|
|
||||||
@inject(TYPES.MapConfig)
|
|
||||||
private readonly config: Partial<IMapConfig>;
|
|
||||||
|
|
||||||
@inject(TYPES.IGlobalConfigService)
|
|
||||||
private readonly configService: IGlobalConfigService;
|
|
||||||
|
|
||||||
@inject(TYPES.ILogService)
|
|
||||||
private readonly logger: ILogService;
|
|
||||||
@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;
|
|
||||||
|
|
||||||
// init
|
|
||||||
public addMarkerContainer(): void {
|
|
||||||
const container = this.map.getCanvasContainer();
|
|
||||||
this.markerContainer = DOM.create('div', 'l7-marker-container', container);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 getSize(): [number, number] {
|
|
||||||
const size = this.map.transform;
|
|
||||||
return [size.width, size.height];
|
|
||||||
}
|
|
||||||
// get mapStatus method
|
|
||||||
|
|
||||||
public getType() {
|
|
||||||
return 'mapbox';
|
|
||||||
}
|
|
||||||
|
|
||||||
public getZoom(): number {
|
|
||||||
return this.map.getZoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
public setZoom(zoom: number) {
|
|
||||||
return this.map.setZoom(zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getCenter(): ILngLat {
|
|
||||||
return this.map.getCenter();
|
|
||||||
}
|
|
||||||
|
|
||||||
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(): void {
|
|
||||||
this.map.zoomIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
public zoomOut(): void {
|
|
||||||
this.map.zoomOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
public panTo(p: [number, number]): void {
|
|
||||||
this.map.panTo(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
public panBy(pixel: [number, number]): void {
|
|
||||||
this.panTo(pixel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public fitBounds(bound: Bounds): void {
|
|
||||||
this.map.fitBounds(bound);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setMaxZoom(max: number): void {
|
|
||||||
this.map.setMaxZoom(max);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setMinZoom(min: number): void {
|
|
||||||
this.map.setMinZoom(min);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setZoomAndCenter(zoom: number, center: [number, number]): void {
|
|
||||||
this.map.flyTo({
|
|
||||||
zoom,
|
|
||||||
center,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public setMapStyle(style: string): 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 async init(): Promise<void> {
|
|
||||||
const {
|
|
||||||
id = 'map',
|
|
||||||
attributionControl = false,
|
|
||||||
style = 'light',
|
|
||||||
token = MAPBOX_API_KEY,
|
|
||||||
rotation = 0,
|
|
||||||
mapInstance,
|
|
||||||
...rest
|
|
||||||
} = this.config;
|
|
||||||
|
|
||||||
this.viewport = new Viewport();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: 使用 mapbox v0.53.x 版本 custom layer,需要共享 gl context
|
|
||||||
* @see https://github.com/mapbox/mapbox-gl-js/blob/master/debug/threejs.html#L61-L64
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 判断全局 mapboxgl 对象的加载
|
|
||||||
if (!mapInstance && !mapboxgl) {
|
|
||||||
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
|
|
||||||
this.logger.error(this.configService.getSceneWarninfo('SDK'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
token === MAPBOX_API_KEY &&
|
|
||||||
style !== 'blank' &&
|
|
||||||
!mapboxgl.accessToken &&
|
|
||||||
!mapInstance // 如果用户传递了 mapInstance,应该不去干预实例的 accessToken。
|
|
||||||
) {
|
|
||||||
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否设置了 accessToken
|
|
||||||
if (!mapInstance && !mapboxgl.accessToken) {
|
|
||||||
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
|
|
||||||
mapboxgl.accessToken = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mapInstance) {
|
|
||||||
// @ts-ignore
|
|
||||||
this.map = mapInstance;
|
|
||||||
this.$mapContainer = this.map.getContainer();
|
|
||||||
} else {
|
|
||||||
this.$mapContainer = this.creatAmapContainer(id);
|
|
||||||
// @ts-ignore
|
|
||||||
this.map = new mapboxgl.Map({
|
|
||||||
container: id,
|
|
||||||
style: this.getMapStyle(style),
|
|
||||||
attributionControl,
|
|
||||||
bearing: rotation,
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.map.on('load', this.handleCameraChanged);
|
|
||||||
this.map.on('move', this.handleCameraChanged);
|
|
||||||
|
|
||||||
// 不同于高德地图,需要手动触发首次渲染
|
|
||||||
this.handleCameraChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public destroy() {
|
|
||||||
this.eventEmitter.removeAllListeners();
|
|
||||||
if (this.map) {
|
|
||||||
this.map.remove();
|
|
||||||
this.$mapContainer = null;
|
|
||||||
this.removeLogoControl();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 onCameraChanged(callback: (viewport: IViewport) => void): void {
|
|
||||||
this.cameraChangedCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleCameraChanged = () => {
|
|
||||||
// @see https://github.com/mapbox/mapbox-gl-js/issues/2572
|
|
||||||
const { lat, lng } = this.map.getCenter().wrap();
|
|
||||||
|
|
||||||
// resync
|
|
||||||
this.viewport.syncWithMapCamera({
|
|
||||||
bearing: this.map.getBearing(),
|
|
||||||
center: [lng, lat],
|
|
||||||
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) {
|
|
||||||
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 removeLogoControl(): void {
|
|
||||||
// @ts-ignore
|
|
||||||
const controls = this.map._controls as IControl[];
|
|
||||||
const logoCtr = controls.find((ctr: IControl) => {
|
|
||||||
if (ctr.hasOwnProperty('_updateLogo')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (logoCtr) {
|
|
||||||
this.map.removeControl(logoCtr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMapStyle(name: MapStyle) {
|
|
||||||
if (typeof name !== 'string') {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return MapTheme[name] ? MapTheme[name] : name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,328 @@
|
||||||
|
/**
|
||||||
|
* MapboxService
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Bounds,
|
||||||
|
CoordinateSystem,
|
||||||
|
ICoordinateSystemService,
|
||||||
|
IGlobalConfigService,
|
||||||
|
ILngLat,
|
||||||
|
ILogService,
|
||||||
|
IMapConfig,
|
||||||
|
IMapService,
|
||||||
|
IPoint,
|
||||||
|
IViewport,
|
||||||
|
MapServiceEvent,
|
||||||
|
MapStyle,
|
||||||
|
TYPES,
|
||||||
|
} from '@antv/l7-core';
|
||||||
|
import { DOM } from '@antv/l7-utils';
|
||||||
|
import { inject, injectable } from 'inversify';
|
||||||
|
import mapboxgl, { IControl, Map } from 'mapbox-gl';
|
||||||
|
import { IMapboxInstance } from '../../typings/index';
|
||||||
|
import Viewport from './Viewport';
|
||||||
|
const EventMap: {
|
||||||
|
[key: string]: any;
|
||||||
|
} = {
|
||||||
|
mapmove: 'move',
|
||||||
|
camerachange: 'move',
|
||||||
|
};
|
||||||
|
import { MapTheme } from './theme';
|
||||||
|
|
||||||
|
const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12;
|
||||||
|
const MAPBOX_API_KEY =
|
||||||
|
'pk.eyJ1IjoibHp4dWUiLCJhIjoiYnhfTURyRSJ9.Ugm314vAKPHBzcPmY1p4KQ';
|
||||||
|
/**
|
||||||
|
* AMapService
|
||||||
|
*/
|
||||||
|
@injectable()
|
||||||
|
export default class MapboxService
|
||||||
|
implements IMapService<Map & IMapboxInstance> {
|
||||||
|
public map: Map & IMapboxInstance;
|
||||||
|
|
||||||
|
@inject(TYPES.MapConfig)
|
||||||
|
private readonly config: Partial<IMapConfig>;
|
||||||
|
|
||||||
|
@inject(TYPES.IGlobalConfigService)
|
||||||
|
private readonly configService: IGlobalConfigService;
|
||||||
|
|
||||||
|
@inject(TYPES.ILogService)
|
||||||
|
private readonly logger: ILogService;
|
||||||
|
@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;
|
||||||
|
|
||||||
|
// init
|
||||||
|
public addMarkerContainer(): void {
|
||||||
|
const container = this.map.getCanvasContainer();
|
||||||
|
this.markerContainer = DOM.create('div', 'l7-marker-container', container);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 getSize(): [number, number] {
|
||||||
|
const size = this.map.transform;
|
||||||
|
return [size.width, size.height];
|
||||||
|
}
|
||||||
|
// get mapStatus method
|
||||||
|
|
||||||
|
public getType() {
|
||||||
|
return 'mapbox';
|
||||||
|
}
|
||||||
|
|
||||||
|
public getZoom(): number {
|
||||||
|
return this.map.getZoom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setZoom(zoom: number) {
|
||||||
|
return this.map.setZoom(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCenter(): ILngLat {
|
||||||
|
return this.map.getCenter();
|
||||||
|
}
|
||||||
|
|
||||||
|
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(): void {
|
||||||
|
this.map.zoomIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public zoomOut(): void {
|
||||||
|
this.map.zoomOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
public panTo(p: [number, number]): void {
|
||||||
|
this.map.panTo(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public panBy(pixel: [number, number]): void {
|
||||||
|
this.panTo(pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fitBounds(bound: Bounds): void {
|
||||||
|
this.map.fitBounds(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMaxZoom(max: number): void {
|
||||||
|
this.map.setMaxZoom(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMinZoom(min: number): void {
|
||||||
|
this.map.setMinZoom(min);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setZoomAndCenter(zoom: number, center: [number, number]): void {
|
||||||
|
this.map.flyTo({
|
||||||
|
zoom,
|
||||||
|
center,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public setMapStyle(style: string): 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 async init(): Promise<void> {
|
||||||
|
const {
|
||||||
|
id = 'map',
|
||||||
|
attributionControl = false,
|
||||||
|
style = 'light',
|
||||||
|
token = MAPBOX_API_KEY,
|
||||||
|
rotation = 0,
|
||||||
|
mapInstance,
|
||||||
|
...rest
|
||||||
|
} = this.config;
|
||||||
|
|
||||||
|
this.viewport = new Viewport();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: 使用 mapbox v0.53.x 版本 custom layer,需要共享 gl context
|
||||||
|
* @see https://github.com/mapbox/mapbox-gl-js/blob/master/debug/threejs.html#L61-L64
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 判断全局 mapboxgl 对象的加载
|
||||||
|
if (!mapInstance && !mapboxgl) {
|
||||||
|
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
|
||||||
|
this.logger.error(this.configService.getSceneWarninfo('SDK'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
token === MAPBOX_API_KEY &&
|
||||||
|
style !== 'blank' &&
|
||||||
|
!mapboxgl.accessToken &&
|
||||||
|
!mapInstance // 如果用户传递了 mapInstance,应该不去干预实例的 accessToken。
|
||||||
|
) {
|
||||||
|
this.logger.warn(this.configService.getSceneWarninfo('MapToken'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否设置了 accessToken
|
||||||
|
if (!mapInstance && !mapboxgl.accessToken) {
|
||||||
|
// 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。
|
||||||
|
mapboxgl.accessToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapInstance) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.map = mapInstance;
|
||||||
|
this.$mapContainer = this.map.getContainer();
|
||||||
|
} else {
|
||||||
|
this.$mapContainer = this.creatAmapContainer(id);
|
||||||
|
// @ts-ignore
|
||||||
|
this.map = new mapboxgl.Map({
|
||||||
|
container: id,
|
||||||
|
style: this.getMapStyle(style),
|
||||||
|
attributionControl,
|
||||||
|
bearing: rotation,
|
||||||
|
...rest,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.map.on('load', this.handleCameraChanged);
|
||||||
|
this.map.on('move', this.handleCameraChanged);
|
||||||
|
|
||||||
|
// 不同于高德地图,需要手动触发首次渲染
|
||||||
|
this.handleCameraChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
this.eventEmitter.removeAllListeners();
|
||||||
|
if (this.map) {
|
||||||
|
this.map.remove();
|
||||||
|
this.$mapContainer = null;
|
||||||
|
this.removeLogoControl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 onCameraChanged(callback: (viewport: IViewport) => void): void {
|
||||||
|
this.cameraChangedCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCameraChanged = () => {
|
||||||
|
// @see https://github.com/mapbox/mapbox-gl-js/issues/2572
|
||||||
|
const { lat, lng } = this.map.getCenter().wrap();
|
||||||
|
|
||||||
|
// resync
|
||||||
|
this.viewport.syncWithMapCamera({
|
||||||
|
bearing: this.map.getBearing(),
|
||||||
|
center: [lng, lat],
|
||||||
|
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) {
|
||||||
|
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 removeLogoControl(): void {
|
||||||
|
// @ts-ignore
|
||||||
|
const controls = this.map._controls as IControl[];
|
||||||
|
const logoCtr = controls.find((ctr: IControl) => {
|
||||||
|
if (ctr.hasOwnProperty('_updateLogo')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (logoCtr) {
|
||||||
|
this.map.removeControl(logoCtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMapStyle(name: MapStyle) {
|
||||||
|
if (typeof name !== 'string') {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return MapTheme[name] ? MapTheme[name] : name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ class Scene
|
||||||
private container: Container;
|
private container: Container;
|
||||||
|
|
||||||
public constructor(config: ISceneConfig) {
|
public constructor(config: ISceneConfig) {
|
||||||
const { id, map } = config;
|
const { id, map, logoPosition, logoVisible } = config;
|
||||||
|
|
||||||
// 创建场景容器
|
// 创建场景容器
|
||||||
const sceneContainer = createSceneContainer();
|
const sceneContainer = createSceneContainer();
|
||||||
|
@ -93,14 +93,16 @@ class Scene
|
||||||
// 初始化 scene
|
// 初始化 scene
|
||||||
this.sceneService.init(config);
|
this.sceneService.init(config);
|
||||||
// TODO: 初始化组件
|
// TODO: 初始化组件
|
||||||
this.addControl(new Logo());
|
if (logoVisible) {
|
||||||
|
this.addControl(new Logo({ position: logoPosition }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMapService(): IMapService<unknown> {
|
public getMapService(): IMapService<unknown> {
|
||||||
return this.mapService;
|
return this.mapService;
|
||||||
}
|
}
|
||||||
public ExportMap2Png(): string {
|
public exportPng(): string {
|
||||||
return this.sceneService.ExportMap2Png();
|
return this.sceneService.exportPng();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get map() {
|
public get map() {
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default class Point3D extends React.Component {
|
||||||
center: [120.19382669582967, 30.258134],
|
center: [120.19382669582967, 30.258134],
|
||||||
pitch: 0,
|
pitch: 0,
|
||||||
style: 'dark',
|
style: 'dark',
|
||||||
zoom: 3,
|
zoom: 0,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
// scene.on('loaded', () => {
|
// scene.on('loaded', () => {
|
||||||
|
|
Loading…
Reference in New Issue