From fac48aede1145e48a26133156a4eb6c8b007ed70 Mon Sep 17 00:00:00 2001 From: "@thinkinggis" Date: Tue, 13 Sep 2022 11:25:25 +0800 Subject: [PATCH] Feat custom map (#1326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(map): 地图模块重构 * feat: lealetMap for l7 * chore: amap baseservice 重构 * fix: 修改amap 样式 * fix: 单词拼写 --- dev-demos/gallery/scale/cat.tsx | 4 +- package.json | 2 + packages/core/package.json | 2 +- .../src/services/config/IConfigService.ts | 6 +- packages/core/src/services/map/IMapService.ts | 6 +- .../core/src/services/scene/ISceneService.ts | 6 +- .../core/src/services/scene/SceneService.ts | 56 +- packages/core/typings/l7-tiny-sdf.d.ts | 3 +- packages/layers/package.json | 2 +- packages/layers/src/line/models/tile.ts | 3 - packages/layers/src/line/models/wall.ts | 1 - .../src/raster/buffers/triangulation.ts | 2 +- packages/maps/src/amap/Viewport.ts | 5 +- packages/maps/src/amap/index.ts | 2 +- packages/maps/src/amap/map.ts | 343 +---------- packages/maps/src/amap2/index.ts | 2 +- packages/maps/src/amap2/map.ts | 300 +--------- packages/maps/src/earth/Viewport.ts | 2 +- packages/maps/src/earth/index.ts | 2 +- packages/maps/src/earth/map.ts | 258 +------- packages/maps/src/index.ts | 2 + packages/maps/src/map/index.ts | 2 +- packages/maps/src/map/map.ts | 343 +---------- packages/maps/src/mapbox/Viewport.ts | 2 +- packages/maps/src/mapbox/index.ts | 2 +- packages/maps/src/mapbox/map.ts | 285 +-------- packages/maps/src/utils/BaseMapService.ts | 363 ++++++++++++ .../maps/src/{ => utils}/BaseMapWrapper.ts | 9 - packages/maps/src/{map => utils}/Viewport.ts | 15 +- .../maps/src/utils/amap/AMapBaseService.ts | 559 ++++++++++++++++++ packages/maps/src/utils/amap/Viewport.ts | 135 +++++ packages/maps/src/utils/amap/logo.css | 3 + packages/maps/src/utils/amap/theme.ts | 8 + packages/maps/src/utils/index.ts | 4 + .../maps/src/{ => utils}/simpleMapCoord.ts | 0 packages/maps/src/utils/theme.ts | 23 + packages/maps/src/{ => utils}/utils.ts | 2 +- packages/source/package.json | 2 +- tsconfig.json | 1 + 39 files changed, 1245 insertions(+), 1522 deletions(-) create mode 100644 packages/maps/src/utils/BaseMapService.ts rename packages/maps/src/{ => utils}/BaseMapWrapper.ts (80%) rename packages/maps/src/{map => utils}/Viewport.ts (82%) create mode 100644 packages/maps/src/utils/amap/AMapBaseService.ts create mode 100644 packages/maps/src/utils/amap/Viewport.ts create mode 100644 packages/maps/src/utils/amap/logo.css create mode 100644 packages/maps/src/utils/amap/theme.ts create mode 100644 packages/maps/src/utils/index.ts rename packages/maps/src/{ => utils}/simpleMapCoord.ts (100%) create mode 100644 packages/maps/src/utils/theme.ts rename packages/maps/src/{ => utils}/utils.ts (94%) diff --git a/dev-demos/gallery/scale/cat.tsx b/dev-demos/gallery/scale/cat.tsx index 61034be468..62e791a281 100644 --- a/dev-demos/gallery/scale/cat.tsx +++ b/dev-demos/gallery/scale/cat.tsx @@ -1,5 +1,5 @@ import { PolygonLayer, Scene } from '@antv/l7'; -import { Mapbox } from '@antv/l7-maps'; +import { Map } from '@antv/l7-maps'; import React, { useEffect } from 'react'; import { useData, addLayers } from './useLine'; @@ -9,7 +9,7 @@ export default () => { useEffect(() => { const scene = new Scene({ id: 'map', - map: new Mapbox({ + map: new Map({ pitch: 0, style: 'light', center: [-96, 37.8], diff --git a/package.json b/package.json index fef793d708..4cbadba450 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@types/gl": "^4.1.0", "@types/hoist-non-react-statics": "^3.3.1", "@types/jest": "^25.2.1", + "@types/leaflet": "^1.7.11", "@types/node": "13.11.1", "@types/offscreencanvas": "^2019.7.0", "@types/react-router-dom": "^5.3.2", @@ -101,6 +102,7 @@ "jest": "^24.9.0", "jest-canvas-mock": "^2.4.0", "jest-styled-components": "^6.2.1", + "leaflet": "^1.8.0", "lerc": "^3.0.0", "lerna": "^3.16.4", "lint-staged": "^9.2.4", diff --git a/packages/core/package.json b/packages/core/package.json index b04da37ba3..a480b8410c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,7 +24,7 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@antv/async-hook": "^2.1.0", + "@antv/async-hook": "^2.2.2", "@antv/l7-utils": "2.9.26", "@babel/runtime": "^7.7.7", "@turf/helpers": "^6.1.4", diff --git a/packages/core/src/services/config/IConfigService.ts b/packages/core/src/services/config/IConfigService.ts index 92d441d992..ffbde1d8fa 100644 --- a/packages/core/src/services/config/IConfigService.ts +++ b/packages/core/src/services/config/IConfigService.ts @@ -18,11 +18,7 @@ export interface ISceneConfig extends IRenderConfig { stencil?: boolean; } -// interface IValidateResult { -// valid: boolean; -// errors: Ajv.ErrorObject[] | null | undefined; -// errorText: string | null; -// } + export interface IGlobalConfigService { /** diff --git a/packages/core/src/services/map/IMapService.ts b/packages/core/src/services/map/IMapService.ts index 1b4ce889a6..652e432021 100644 --- a/packages/core/src/services/map/IMapService.ts +++ b/packages/core/src/services/map/IMapService.ts @@ -49,12 +49,12 @@ export interface IMapService { setBgColor(color: string): void; init(): void; initMiniMap?(): void; - initViewPort?(): void; destroy(): void; onCameraChanged(callback: (viewport: IViewport) => void): void; // init map addMarkerContainer(): void; getMarkerContainer(): HTMLElement; + getOverlayContainer(): HTMLElement | undefined; // MapEvent // 定义事件类型 on(type: string, handler: (...args: any[]) => void): void; @@ -89,6 +89,7 @@ export interface IMapService { setZoom(zoom: number): void; setMapStyle(style: any): void; setMapStatus(option: Partial): void; + updateView(viewOption:Partial):void // 更新地图视野 // coordinates methods meterToCoord(center: number[], lnglat: number[]): number; @@ -129,7 +130,6 @@ export interface IEarthService { bgColor: string; setBgColor(color: string): void; init(): void; - initViewPort?(): void; destroy(): void; onCameraChanged(callback: (viewport: IViewport) => void): void; // init map @@ -267,6 +267,8 @@ export interface IMapConfig { offsetZoom?: number; + interactive: boolean;// + [key: string]: any; } diff --git a/packages/core/src/services/scene/ISceneService.ts b/packages/core/src/services/scene/ISceneService.ts index 4d57887320..1658b73c5c 100644 --- a/packages/core/src/services/scene/ISceneService.ts +++ b/packages/core/src/services/scene/ISceneService.ts @@ -1,7 +1,5 @@ import { ISceneConfig } from '../config/IConfigService'; import { ILayer } from '../layer/ILayerService'; -import { IMapConfig } from '../map/IMapService'; -import { IRenderConfig } from '../renderer/IRendererService'; export interface ISceneService { destroyed: boolean; @@ -12,8 +10,8 @@ export interface ISceneService { once(type: string, handle: (...args: any[]) => void): void; off(type: string, handle: (...args: any[]) => void): void; removeAllListeners(event?: string): this; - init(config: IMapConfig & IRenderConfig): void; - initMiniScene(config: IMapConfig & IRenderConfig): void; + init(config: ISceneConfig): void; + initMiniScene(config: ISceneConfig): void; addLayer(layer: ILayer): void; addMask(mask: ILayer): void; getSceneConfig(): Partial; diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index bb8cba7ec6..8146f1ac2b 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -1,5 +1,5 @@ // @ts-ignore -import { AsyncParallelHook } from '@antv/async-hook'; +import { AsyncSeriesHook } from '@antv/async-hook'; import { $window, DOM } from '@antv/l7-utils'; import elementResizeEvent, { unbind } from 'element-resize-event'; import { EventEmitter } from 'eventemitter3'; @@ -106,7 +106,7 @@ export default class Scene extends EventEmitter implements ISceneService { private markerContainer: HTMLElement; private hooks: { - init: AsyncParallelHook; + init: AsyncSeriesHook; }; public constructor() { @@ -119,7 +119,7 @@ export default class Scene extends EventEmitter implements ISceneService { * 2. initRenderer:初始化渲染引擎 * 3. initWorker:初始化 Worker */ - init: new AsyncParallelHook(), + init: new AsyncSeriesHook(), }; } @@ -144,31 +144,15 @@ export default class Scene extends EventEmitter implements ISceneService { this.map.onCameraChanged((viewport: IViewport) => { this.cameraService.init(); this.cameraService.update(viewport); - if (this.map.version !== 'GAODE2.x') { - // not amap2 - resolve(); - } - }); - - if (this.map.version !== 'GAODE2.x') { - // not amap2 - this.map.init(); - } else { - // amap2 resolve(); - } + }); + this.map.init(); }); - - if (this.map.version === 'GAODE2.x' && this.map.initViewPort) { - // amap2 - await this.map.init(); - this.map.initViewPort(); - } - + + // 重新绑定非首次相机更新事件 this.map.onCameraChanged(this.handleMapCameraChanged); this.map.addMarkerContainer(); - // 初始化未加载的marker; this.markerService.addMarkers(); this.markerService.addMarkerLayers(); @@ -185,16 +169,23 @@ export default class Scene extends EventEmitter implements ISceneService { * 初始化渲染引擎 */ this.hooks.init.tapPromise('initRenderer', async () => { + const renderContainer = this.map.getOverlayContainer(); + + if(renderContainer) { + this.$container = renderContainer as HTMLDivElement; + } else { + this.$container = createRendererContainer( + this.configService.getSceneConfig(this.id).id || '', + ); + + } + + // 创建底图之上的 container - const $container = createRendererContainer( - this.configService.getSceneConfig(this.id).id || '', - ); + - // 添加marker container; - this.$container = $container; - - if ($container) { - this.canvas = DOM.create('canvas', '', $container) as HTMLCanvasElement; + if (this.$container) { + this.canvas = DOM.create('canvas', '', this.$container) as HTMLCanvasElement; this.setCanvas(); await this.rendererService.init( // @ts-ignore @@ -321,7 +312,7 @@ export default class Scene extends EventEmitter implements ISceneService { if (!this.inited) { // 还未初始化完成需要等待 - await this.initPromise; + await this.initPromise; // 初始化地图和渲染 if (this.destroyed) { this.destroy(); } @@ -336,6 +327,7 @@ export default class Scene extends EventEmitter implements ISceneService { console.warn(e); } } + // FIXME: 初始化 marker 容器,可以放到 map 初始化方法中? this.layerService.initLayers(); this.controlService.addControls(); diff --git a/packages/core/typings/l7-tiny-sdf.d.ts b/packages/core/typings/l7-tiny-sdf.d.ts index fb17951346..0edfa828fe 100644 --- a/packages/core/typings/l7-tiny-sdf.d.ts +++ b/packages/core/typings/l7-tiny-sdf.d.ts @@ -1,3 +1,2 @@ declare module 'l7hammerjs'; -declare module 'l7-tiny-sdf'; -declare module '@antv/l7-maps'; \ No newline at end of file +declare module 'l7-tiny-sdf'; \ No newline at end of file diff --git a/packages/layers/package.json b/packages/layers/package.json index 81b0ca8788..fcd913255a 100644 --- a/packages/layers/package.json +++ b/packages/layers/package.json @@ -26,7 +26,7 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@antv/async-hook": "^2.1.0", + "@antv/async-hook": "^2.2.2", "@antv/l7-core": "2.9.26", "@antv/l7-maps": "2.9.26", "@antv/l7-source": "2.9.26", diff --git a/packages/layers/src/line/models/tile.ts b/packages/layers/src/line/models/tile.ts index 73c5834045..4a8a83fd7a 100644 --- a/packages/layers/src/line/models/tile.ts +++ b/packages/layers/src/line/models/tile.ts @@ -208,9 +208,6 @@ export default class LineModel extends BaseModel { size: 2, update: ( feature: IEncodeFeature, - featureIdx: number, - vertex: number[], - attributeIdx: number, ) => { const { size = 1 } = feature; return Array.isArray(size) ? [size[0], size[1]] : [size as number, 0]; diff --git a/packages/layers/src/line/models/wall.ts b/packages/layers/src/line/models/wall.ts index 6b3c1794e2..58e9bbc4e4 100644 --- a/packages/layers/src/line/models/wall.ts +++ b/packages/layers/src/line/models/wall.ts @@ -151,7 +151,6 @@ export default class LineWallModel extends BaseModel { feature: IEncodeFeature, featureIdx: number, vertex: number[], - attributeIdx: number, ) => { return [vertex[3]]; }, diff --git a/packages/layers/src/raster/buffers/triangulation.ts b/packages/layers/src/raster/buffers/triangulation.ts index 6d3c279be1..490c7b1aeb 100644 --- a/packages/layers/src/raster/buffers/triangulation.ts +++ b/packages/layers/src/raster/buffers/triangulation.ts @@ -2,7 +2,7 @@ import { IParseDataItem } from '@antv/l7-core'; // @ts-ignore import Martini from '@mapbox/martini'; export function RasterTriangulation(parserData: IParseDataItem) { - const { coordinates, data, min, max, width, height } = parserData; + const { data,width, height } = parserData; const maxlength = Math.max(width, height); const gridSize = Math.pow(2, Math.ceil(Math.log2(maxlength))) + 1; const terrain = new Float32Array(gridSize * gridSize); diff --git a/packages/maps/src/amap/Viewport.ts b/packages/maps/src/amap/Viewport.ts index 50c761138c..674e5bb6ce 100644 --- a/packages/maps/src/amap/Viewport.ts +++ b/packages/maps/src/amap/Viewport.ts @@ -115,10 +115,7 @@ export default class Viewport implements IViewport { /** * P20 坐标系,固定 scale */ - public projectFlat( - lngLat: [number, number], - scale?: number | undefined, - ): [number, number] { + public projectFlat(lngLat: [number, number]): [number, number] { const maxs = 85.0511287798; const lat = Math.max(Math.min(maxs, lngLat[1]), -maxs); // tslint:disable-next-line:no-bitwise diff --git a/packages/maps/src/amap/index.ts b/packages/maps/src/amap/index.ts index 245811aad8..4706844f3d 100644 --- a/packages/maps/src/amap/index.ts +++ b/packages/maps/src/amap/index.ts @@ -1,5 +1,5 @@ import { IAMapInstance } from '../../typings/index'; -import BaseMapWrapper from '../BaseMapWrapper'; +import BaseMapWrapper from '../utils/BaseMapWrapper'; import AMapService from './map'; export default class AMapWrapper extends BaseMapWrapper< diff --git a/packages/maps/src/amap/map.ts b/packages/maps/src/amap/map.ts index 7b183ad44e..7d81f40055 100644 --- a/packages/maps/src/amap/map.ts +++ b/packages/maps/src/amap/map.ts @@ -3,42 +3,25 @@ */ import AMapLoader from '@amap/amap-jsapi-loader'; import { - Bounds, CoordinateSystem, - ICameraOptions, - ICoordinateSystemService, - IGlobalConfigService, - ILngLat, - IMapConfig, IMapService, - IMercator, - IPoint, - IStatusOptions, IViewport, - MapServiceEvent, - TYPES, + IMapCamera, } from '@antv/l7-core'; -import { DOM } from '@antv/l7-utils'; import { mat4, vec3 } from 'gl-matrix'; -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import 'reflect-metadata'; import { IAMapEvent, IAMapInstance } from '../../typings/index'; -import { ISimpleMapCoord, SimpleMapCoord } from '../simpleMapCoord'; -import { toPaddingOptions } from '../utils'; import { Version } from '../version'; -import './logo.css'; -import { MapTheme } from './theme'; + import Viewport from './Viewport'; -let mapdivCount = 0; +import AMapBaseService from '../utils/amap/AMapBaseService'; // @ts-ignore window.forceWebGL = true; const AMAP_API_KEY: string = '15cd8a57710d40c9b7c0e3cc120f1200'; const AMAP_VERSION: string = '1.4.15'; -/** - * 确保多个场景只引入一个高德地图脚本 - */ -const AMAP_SCRIPT_ID: string = 'amap-script'; + /** * 高德地图脚本是否加载完毕 */ @@ -53,263 +36,16 @@ const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; // 暂时关闭 fix 统一不同坐标 * AMapService */ @injectable() -export default class AMapService +export default class AMapService extends AMapBaseService implements IMapService { public version: string = Version['GAODE1.x']; - public simpleMapCoord: ISimpleMapCoord = new SimpleMapCoord(); - /** - * 原始地图实例 - */ - public map: AMap.Map & IAMapInstance; - - // 背景色 - public bgColor: string = 'rgba(0, 0, 0, 0)'; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @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 setBgColor(color: string) { - this.bgColor = color; - } - - 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 getMapCanvasContainer(): HTMLElement { - return this.map - .getContainer() - ?.getElementsByClassName('amap-maps')[0] as HTMLElement; - } - - 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 { - // 统一设置 Mapbox 缩放等级 - return this.map.setZoom(zoom + 1); - } - - public getCenter(options?: ICameraOptions): ILngLat { - if (options?.padding) { - const originCenter = this.getCenter(); - const [w, h] = this.getSize(); - const padding = toPaddingOptions(options.padding); - const px = this.lngLatToPixel([originCenter.lng, originCenter.lat]); - const offsetPx = [ - (padding.right - padding.left) / 2, - (padding.bottom - padding.top) / 2, - ]; - - const newCenter = this.pixelToLngLat([ - px.x - offsetPx[0], - px.y - offsetPx[1], - ]); - return newCenter; - } - const center = this.map.getCenter(); - return { - lng: center.getLng(), - lat: center.getLat(), - }; - } - public setCenter(lnglat: [number, number], options?: ICameraOptions): void { - if (options?.padding) { - const padding = toPaddingOptions(options.padding); - const px = this.lngLatToPixel(lnglat); - const offsetPx = [ - (padding.right - padding.left) / 2, - (padding.bottom - padding.top) / 2, - ]; - const newCenter = this.pixelToLngLat([ - px.x + offsetPx[0], - px.y + offsetPx[1], - ]); - this.map.setCenter([newCenter.lng, newCenter.lat]); - } else { - this.map.setCenter(lnglat); - } - } - 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 setPitch(pitch: number) { - return this.map.setPitch(pitch); - } - public zoomIn(): void { - this.map.zoomIn(); - } - - public zoomOut(): void { - this.map.zoomOut(); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - - public panBy(x: number = 0, y: number = 0): void { - this.map.panBy(x, y); - } - - 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 + 1, center); - } - - public setMapStyle(style: string): void { - this.map.setMapStyle(this.getMapStyle(style)); - } - - public setMapStatus(option: Partial): void { - this.map.setStatus(option); - } - 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 lngLatToCoord(lnglat: [number, number]): any { - // @ts-ignore - const { x, y } = this.map.lngLatToGeodeticCoord(lnglat); - return [x, -y]; - } - - public lngLatToMercator( - lnglat: [number, number], - altitude: number, - ): IMercator { - return { - x: 0, - y: 0, - z: 0, - }; - } + protected viewport: IViewport; public getModelMatrix( lnglat: [number, number], altitude: number, rotate: [number, number, number], scale: [number, number, number] = [1, 1, 1], - origin: IMercator = { x: 0, y: 0, z: 0 }, ): number[] { const flat = this.viewport.projectFlat(lnglat); // @ts-ignore @@ -356,7 +92,7 @@ export default class AMapService resolve(); }, 30); } else { - this.$mapContainer = this.creatAmapContainer( + this.$mapContainer = this.creatMapContainer( id as string | HTMLDivElement, ); const mapConstructorOptions = { @@ -436,6 +172,12 @@ export default class AMapService return coordDis / meterDis; } + public updateView(viewOption: Partial): void {} + + public getOverlayContainer(): HTMLElement | undefined { + return undefined; + } + public exportMap(type: 'jpg' | 'png'): string { const renderCanvas = this.getContainer()?.getElementsByClassName( 'amap-layer', @@ -447,41 +189,11 @@ export default class AMapService return layersPng; } - 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(); - - // TODO: 销毁地图可视化层的容器 - this.$mapContainer?.parentNode?.removeChild(this.$mapContainer); - - // @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 handleAfterMapChange() { - this.emit('mapAfterFrameChange'); - } - - private handleCameraChanged = (e: IAMapEvent): void => { + protected handleCameraChanged = (e: IAMapEvent): void => { const { fov, near, @@ -497,11 +209,6 @@ export default class AMapService this.emit('mapchange'); if (this.cameraChangedCallback) { - // resync viewport - // console.log('cameraHeight', height) - // console.log('pitch', pitch) - // console.log('rotation', rotation) - // console.log('zoom', this.map.getZoom()) this.viewport.syncWithMapCamera({ aspect, // AMap 定义 rotation 为顺时针方向,而 Mapbox 为逆时针 @@ -530,24 +237,4 @@ export default class AMapService 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; - } } diff --git a/packages/maps/src/amap2/index.ts b/packages/maps/src/amap2/index.ts index 1661938a64..d131a6ff79 100644 --- a/packages/maps/src/amap2/index.ts +++ b/packages/maps/src/amap2/index.ts @@ -1,5 +1,5 @@ import { IAMapInstance } from '../../typings/index'; -import BaseMapWrapper from '../BaseMapWrapper'; +import BaseMapWrapper from '../utils/BaseMapWrapper'; import AMapService from './map'; export default class AMapWrapper2 extends BaseMapWrapper< diff --git a/packages/maps/src/amap2/map.ts b/packages/maps/src/amap2/map.ts index a7b9e9d21d..8185101af2 100644 --- a/packages/maps/src/amap2/map.ts +++ b/packages/maps/src/amap2/map.ts @@ -5,32 +5,21 @@ import AMapLoader from '@amap/amap-jsapi-loader'; import { Bounds, CoordinateSystem, - ICameraOptions, - ICoordinateSystemService, - IGlobalConfigService, - ILngLat, - IMapConfig, - IMapService, - IMercator, IPoint, - IStatusOptions, IViewport, - MapServiceEvent, - TYPES, + IMapCamera, } from '@antv/l7-core'; import { amap2Project, DOM } from '@antv/l7-utils'; import { mat4, vec2, vec3 } from 'gl-matrix'; -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import 'reflect-metadata'; import { IAMapInstance } from '../../typings/index'; -import { SimpleMapCoord } from '../simpleMapCoord'; -import { toPaddingOptions } from '../utils'; import { Version } from '../version'; import './logo.css'; import { MapTheme } from './theme'; import Viewport from './Viewport'; +import AMapBaseService from '../utils/amap/AMapBaseService'; -let mapdivCount = 0; // @ts-ignore window.forceWebGL = true; @@ -38,10 +27,7 @@ window.forceWebGL = true; const AMAP_API_KEY: string = 'ff533602d57df6f8ab3b0fea226ae52f'; // const AMAP_VERSION: string = '1.4.15'; const AMAP_VERSION: string = '2.0.5'; -/** - * 确保多个场景只引入一个高德地图脚本 - */ -const AMAP_SCRIPT_ID: string = 'amap-script'; + /** * 高德地图脚本是否加载完毕 */ @@ -55,14 +41,8 @@ let pendingResolveQueue: Array<() => void> = []; * AMapService */ @injectable() -export default class AMapService - implements IMapService { +export default class AMapService extends AMapBaseService { public version: string = Version['GAODE2.x']; - public simpleMapCoord: SimpleMapCoord = new SimpleMapCoord(); - /** - * 原始地图实例 - */ - public map: AMap.Map & IAMapInstance; /** * 用于 customCooords 数据的计算 @@ -70,30 +50,7 @@ export default class AMapService public sceneCenter!: [number, number]; // 一般使用用户数据的第一个 public sceneCenterMKT!: [number, number]; // 莫卡托 - // 背景色 - public bgColor: string = 'rgba(0, 0, 0, 0)'; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @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 setBgColor(color: string) { - this.bgColor = color; - } + protected viewport: Viewport; /** * 设置数据的绘制中心 高德2.0 @@ -182,116 +139,24 @@ export default class AMapService // TODO: amap2 的 amap-maps 新增 z-index=0; 样式,让 marker 中 zIndex 失效 amap.style.zIndex = 'auto'; this.markerContainer = DOM.create('div', 'l7-marker-container2', amap); - // this.markerContainer = DOM.create( - // 'div', - // 'l7-marker-container2', - // mapContainer, - // ); - // this.markerContainer = mapContainer; - } - } - 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 updateView(_viewOption: Partial): void {} + + public getOverlayContainer(): HTMLElement | undefined { + return undefined; } - public getMapCanvasContainer(): HTMLElement { - return this.map - .getContainer() - ?.getElementsByClassName('amap-maps')[0] as HTMLElement; - } - - public getSize(): [number, number] { - const size = this.map.getSize(); - return [size.getWidth(), size.getHeight()]; + protected getMapStyle(name: string): string { + return MapTheme[name] ? MapTheme[name] : name; } public getType() { return 'amap2'; } - public getZoom(): number { - // 统一返回 Mapbox 缩放等级 - return this.map.getZoom() - 1; - } - - public setZoom(zoom: number): void { - // 统一设置 Mapbox 缩放等级 - return this.map.setZoom(zoom + 1); - } - - public getCenter(options?: ICameraOptions): ILngLat { - if (options?.padding) { - const originCenter = this.getCenter(); - const [w, h] = this.getSize(); - const padding = toPaddingOptions(options.padding); - const px = this.lngLatToPixel([originCenter.lng, originCenter.lat]); - const offsetPx = [ - (padding.right - padding.left) / 2, - (padding.bottom - padding.top) / 2, - ]; - - const newCenter = this.pixelToLngLat([ - px.x - offsetPx[0], - px.y - offsetPx[1], - ]); - return newCenter; - } - const center = this.map.getCenter(); - return { - lng: center.getLng(), - lat: center.getLat(), - }; - } - public setCenter(lnglat: [number, number], options?: ICameraOptions): void { - if (options?.padding) { - const padding = toPaddingOptions(options.padding); - const px = this.lngLatToPixel(lnglat); - const offsetPx = [ - (padding.right - padding.left) / 2, - (padding.bottom - padding.top) / 2, - ]; - const newCenter = this.pixelToLngLat([ - px.x + offsetPx[0], - px.y + offsetPx[1], - ]); - this.map.setCenter([newCenter.lng, newCenter.lat]); - } else { - this.map.setCenter(lnglat); - } - } - 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 bounds = this.map.getBounds(); // @ts-ignore @@ -323,61 +188,7 @@ export default class AMapService const zooms = this.map.getZooms() as [number, number]; return zooms[1] - 1; } - public setRotation(rotation: number): void { - return this.map.setRotation(rotation); - } - public setPitch(pitch: number) { - return this.map.setPitch(pitch); - } - public zoomIn(): void { - this.map.zoomIn(); - } - - public zoomOut(): void { - this.map.zoomOut(); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - public panBy(x: number = 0, y: number = 0): void { - this.map.panBy(x, y); - } - 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 + 1, center); - } - public setMapStyle(style: string): void { - this.map.setMapStyle(this.getMapStyle(style)); - } - - public setMapStatus(option: Partial): void { - this.map.setStatus(option); - } - 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 pixel = this.map.lngLatToContainer(lnglat); return { @@ -386,23 +197,11 @@ export default class AMapService }; } - public lngLatToMercator( - lnglat: [number, number], - altitude: number, - ): IMercator { - return { - x: 0, - y: 0, - z: 0, - }; - } - public getModelMatrix( lnglat: [number, number], altitude: number, rotate: [number, number, number], scale: [number, number, number] = [1, 1, 1], - origin: IMercator = { x: 0, y: 0, z: 0 }, ): number[] { // const flat = this.viewport.projectFlat(lnglat); // @ts-ignore @@ -465,7 +264,7 @@ export default class AMapService resolve(); }, 30); } else { - this.$mapContainer = this.creatAmapContainer( + this.$mapContainer = this.creatMapContainer( id as string | HTMLDivElement, ); const mapConstructorOptions = { @@ -527,55 +326,7 @@ export default class AMapService } } }); - } - - public meterToCoord(center: [number, number], outer: [number, number]) { - // 统一根据经纬度来转化 - // Tip: 实际米距离 unit meter - const meterDis = AMap.GeometryUtil.distance( - new AMap.LngLat(...center), - new AMap.LngLat(...outer), - ); - - // Tip: 三维世界坐标距离 - const [x1, y1] = this.lngLatToCoord(center); - const [x2, y2] = this.lngLatToCoord(outer); - const coordDis = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); - - return coordDis / meterDis; - } - - public exportMap(type: 'jpg' | 'png'): string { - const renderCanvas = this.getContainer()?.getElementsByClassName( - 'amap-layer', - )[0] as HTMLCanvasElement; - const layersPng = - type === 'jpg' - ? (renderCanvas?.toDataURL('image/jpeg') as string) - : (renderCanvas?.toDataURL('image/png') as string); - return layersPng; - } - - 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(); - - // TODO: 销毁地图可视化层的容器 - this.$mapContainer?.parentNode?.removeChild(this.$mapContainer); - - // @ts-ignore - delete window.initAMap; - const $jsapi = document.getElementById(AMAP_SCRIPT_ID); - if ($jsapi) { - document.head.removeChild($jsapi); - } + this.initViewPort(); } public getMapContainer() { @@ -586,7 +337,7 @@ export default class AMapService this.cameraChangedCallback = callback; } - public initViewPort() { + private initViewPort() { // @ts-ignore const { // @ts-ignore @@ -699,25 +450,4 @@ export default class AMapService 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; - } } diff --git a/packages/maps/src/earth/Viewport.ts b/packages/maps/src/earth/Viewport.ts index e2f4c4fceb..8d4cff7e42 100644 --- a/packages/maps/src/earth/Viewport.ts +++ b/packages/maps/src/earth/Viewport.ts @@ -17,7 +17,7 @@ export default class Viewport implements IViewport { private cameraPosition: vec3 = vec3.create(); - private viewport: WebMercatorViewport; + protected viewport: WebMercatorViewport; private projectionMatrix: mat4 = mat4.create(); private modelMatrix: mat4 = mat4.create(); diff --git a/packages/maps/src/earth/index.ts b/packages/maps/src/earth/index.ts index 6ca53d102b..4df9904cf2 100644 --- a/packages/maps/src/earth/index.ts +++ b/packages/maps/src/earth/index.ts @@ -1,5 +1,5 @@ import { Map } from '@antv/l7-map'; -import BaseMapWrapper from '../BaseMapWrapper'; +import BaseMapWrapper from '../utils/BaseMapWrapper'; import MapService from './map'; export default class EarthWrapper extends BaseMapWrapper { protected getServiceConstructor() { diff --git a/packages/maps/src/earth/map.ts b/packages/maps/src/earth/map.ts index 745e4995f8..2797b201f2 100644 --- a/packages/maps/src/earth/map.ts +++ b/packages/maps/src/earth/map.ts @@ -2,27 +2,18 @@ * MapboxService */ import { - Bounds, CoordinateSystem, - ICoordinateSystemService, IEarthService, - IGlobalConfigService, - ILngLat, - IMapConfig, IMercator, - IPoint, - IStatusOptions, IViewport, MapServiceEvent, - MapStyle, - TYPES, } from '@antv/l7-core'; import { EarthMap, Map } from '@antv/l7-map'; -import { DOM } from '@antv/l7-utils'; -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import 'reflect-metadata'; import { Version } from '../version'; import Viewport from './Viewport'; +import BaseMapService from '../utils/BaseMapService'; const EventMap: { [key: string]: any; } = { @@ -31,56 +22,38 @@ const EventMap: { zoomchange: 'zoom', dragging: 'drag', }; -import { ISimpleMapCoord, SimpleMapCoord } from '../simpleMapCoord'; -import { MapTheme } from './theme'; const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; /** * EarthService */ @injectable() -export default class L7EarthService implements IEarthService { +export default class L7EarthService extends BaseMapService + implements IEarthService { + public lngLatToMercator( + lnglat: [number, number], + altitude: number, + ): IMercator { + throw new Error('Method not implemented.'); + } + public getModelMatrix( + lnglat: [number, number], + altitude: number, + rotate: [number, number, number], + scale: [number, number, number], + origin: IMercator, + ): number[] { + throw new Error('Method not implemented.'); + } public version: string = Version.GLOBEL; - public map: Map; - public simpleMapCoord: ISimpleMapCoord = new SimpleMapCoord(); - // TODO: 判断地图是否正在拖拽 public dragging: boolean = false; + public viewport: Viewport; + protected cameraChangedCallback: (viewport: IViewport) => void; - // 背景色 - public bgColor: string = '#000'; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.ICoordinateSystemService) - private readonly coordinateSystemService: ICoordinateSystemService; - - @inject(TYPES.IEventEmitter) - private eventEmitter: any; - private viewport: Viewport; - private markerContainer: HTMLElement; - private cameraChangedCallback: (viewport: IViewport) => void; - private $mapContainer: HTMLElement | null; // T: 用于记录鼠标对相机的控制 private handleCameraChanging: boolean; private handleCameraTimer: any; - public setBgColor(color: string) { - this.bgColor = color; - } - // init - public addMarkerContainer(): void { - const container = this.map.getCanvasContainer(); - this.markerContainer = DOM.create('div', 'l7-marker-container', container); - this.markerContainer.setAttribute('tabindex', '-1'); - } - - public getMarkerContainer(): HTMLElement { - return this.markerContainer; - } // map event public on(type: string, handle: (...args: any[]) => void): void { @@ -96,10 +69,6 @@ export default class L7EarthService implements IEarthService { this.eventEmitter.off(type, handle); } - public getContainer(): HTMLElement | null { - return this.map.getContainer(); - } - public getMapCanvasContainer(): HTMLElement { return this.map.getCanvasContainer() as HTMLElement; } @@ -111,170 +80,15 @@ export default class L7EarthService implements IEarthService { // get mapStatus method public getType() { - return 'default'; - } - - public getZoom(): number { - return this.map.getZoom(); - } - - public setZoom(zoom: number) { - return this.map.setZoom(zoom); - } - - public getCenter(): ILngLat { - return this.map.getCenter(); - } - - public setCenter(lnglat: [number, number]): void { - this.map.setCenter(lnglat); - } - - public getPitch(): number { - return this.map.getPitch(); - } - - public getRotation(): number { - return this.map.getBearing(); - } - - public getBounds(): Bounds { - return this.map.getBounds().toArray() as Bounds; - } - - public getMinZoom(): number { - return this.map.getMinZoom(); - } - - public getMaxZoom(): number { - return this.map.getMaxZoom(); - } - - public setRotation(rotation: number): void { - this.map.setBearing(rotation); - } - - public zoomIn(option?: any, eventData?: any): void { - this.map.zoomIn(option, eventData); - } - public zoomOut(option?: any, eventData?: any): void { - this.map.zoomOut(option, eventData); - } - public setPitch(pitch: number) { - return this.map.setPitch(pitch); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - - public panBy(x: number = 0, y: number = 0): void { - this.panTo([x, y]); - } - - public fitBounds(bound: Bounds, fitBoundsOptions?: any): void { - this.map.fitBounds(bound, fitBoundsOptions); - } - - public setMaxZoom(max: number): void { - this.map.setMaxZoom(max); - } - - public setMinZoom(min: number): void { - this.map.setMinZoom(min); - } - public setMapStatus(option: Partial): void { - if (option.doubleClickZoom === true) { - this.map.doubleClickZoom.enable(); - } - if (option.doubleClickZoom === false) { - this.map.doubleClickZoom.disable(); - } - if (option.dragEnable === false) { - this.map.dragPan.disable(); - } - if (option.dragEnable === true) { - this.map.dragPan.enable(); - } - if (option.rotateEnable === false) { - this.map.dragRotate.disable(); - } - if (option.dragEnable === true) { - this.map.dragRotate.enable(); - } - if (option.keyboardEnable === false) { - this.map.keyboard.disable(); - } - if (option.keyboardEnable === true) { - this.map.keyboard.enable(); - } - if (option.zoomEnable === false) { - this.map.scrollZoom.disable(); - } - if (option.zoomEnable === true) { - this.map.scrollZoom.enable(); - } - } - - public setZoomAndCenter(zoom: number, center: [number, number]): void { - this.map.flyTo({ - zoom, - center, - }); - } - - public setMapStyle(style: any): void { - this.map.setStyle(this.getMapStyle(style)); - } - // TODO: 计算像素坐标 - public pixelToLngLat(pixel: [number, number]): ILngLat { - return this.map.unproject(pixel); - } - - public meterToCoord(center: [number, number], outer: [number, number]) { - return 1.0; - } - - public lngLatToPixel(lnglat: [number, number]): IPoint { - return this.map.project(lnglat); - } - - public containerToLngLat(pixel: [number, number]): ILngLat { - return this.map.unproject(pixel); - } - - public lngLatToContainer(lnglat: [number, number]): IPoint { - return this.map.project(lnglat); - } - public lngLatToMercator( - lnglat: [number, number], - altitude: number, - ): IMercator { - throw new Error('not implement'); - } - public getModelMatrix( - lnglat: [number, number], - altitude: number, - rotate: [number, number, number], - scale: [number, number, number] = [1, 1, 1], - origin: IMercator = { x: 0, y: 0, z: 0 }, - ): number[] { - throw new Error('not implement'); + return 'earth'; } public async init(): Promise { - const { - id = 'map', - attributionControl = false, - style = 'light', - rotation = 0, - mapInstance, - ...rest - } = this.config; + const { id = 'map', style = 'light', rotation = 0, ...rest } = this.config; this.viewport = new Viewport(); - this.$mapContainer = this.creatAmapContainer(id); + this.$mapContainer = this.creatMapContainer(id); // @ts-ignore this.map = new EarthMap({ container: this.$mapContainer, @@ -311,14 +125,6 @@ export default class L7EarthService implements IEarthService { return this.$mapContainer; } - public exportMap(type: 'jpg' | 'png'): string { - const renderCanvas = this.map.getCanvas(); - const layersPng = - type === 'jpg' - ? (renderCanvas?.toDataURL('image/jpeg') as string) - : (renderCanvas?.toDataURL('image/png') as string); - return layersPng; - } public onCameraChanged(callback: (viewport: IViewport) => void): void { this.cameraChangedCallback = callback; } @@ -346,7 +152,7 @@ export default class L7EarthService implements IEarthService { } } - private handleCameraChanged = (e: any) => { + protected handleCameraChanged = (e: any) => { // Tip: 统一触发地图变化事件 this.emit('mapchange'); const DELAY_TIME = 2000; @@ -405,18 +211,4 @@ export default class L7EarthService implements IEarthService { this.cameraChangedCallback(this.viewport); }; - - private creatAmapContainer(id: string | HTMLDivElement) { - let $wrapper = id as HTMLDivElement; - if (typeof id === 'string') { - $wrapper = document.getElementById(id) as HTMLDivElement; - } - return $wrapper; - } - private getMapStyle(name: MapStyle) { - if (typeof name !== 'string') { - return name; - } - return MapTheme[name] ? MapTheme[name] : name; - } } diff --git a/packages/maps/src/index.ts b/packages/maps/src/index.ts index b670d1a063..026268cfd6 100644 --- a/packages/maps/src/index.ts +++ b/packages/maps/src/index.ts @@ -10,3 +10,5 @@ import { Version } from './version'; export { Version, GaodeMap, GaodeMapV2, Mapbox, Map, Earth }; // export { GaodeMap, GaodeMapV2, Mapbox, Map }; // export { Map }; + +export * from './utils' diff --git a/packages/maps/src/map/index.ts b/packages/maps/src/map/index.ts index 5fa4227acd..22d6164463 100644 --- a/packages/maps/src/map/index.ts +++ b/packages/maps/src/map/index.ts @@ -1,5 +1,5 @@ import { Map } from '@antv/l7-map'; -import BaseMapWrapper from '../BaseMapWrapper'; +import BaseMapWrapper from '../utils/BaseMapWrapper'; import MapService from './map'; export default class MapboxWrapper extends BaseMapWrapper { protected getServiceConstructor() { diff --git a/packages/maps/src/map/map.ts b/packages/maps/src/map/map.ts index 200dd60bea..fc83fc277a 100644 --- a/packages/maps/src/map/map.ts +++ b/packages/maps/src/map/map.ts @@ -1,275 +1,41 @@ /** * MapboxService */ -import { - Bounds, - CoordinateSystem, - ICoordinateSystemService, - IGlobalConfigService, - ILngLat, - IMapConfig, - IMapService, - IMercator, - IPoint, - IStatusOptions, - IViewport, - MapServiceEvent, - MapStyle, - TYPES, -} from '@antv/l7-core'; +import { CoordinateSystem, IMercator } from '@antv/l7-core'; import { Map } from '@antv/l7-map'; -import { $window, DOM } from '@antv/l7-utils'; -import { inject, injectable } from 'inversify'; +import { $window } from '@antv/l7-utils'; +import { injectable } from 'inversify'; import 'reflect-metadata'; -import { ISimpleMapCoord, SimpleMapCoord } from '../simpleMapCoord'; import { Version } from '../version'; -import Viewport from './Viewport'; -const EventMap: { - [key: string]: any; -} = { - mapmove: 'move', - camerachange: 'move', - zoomchange: 'zoom', - dragging: 'drag', -}; -import { MapTheme } from './theme'; +import Viewport from '../utils/Viewport'; +import BaseMapService from '../utils/BaseMapService'; const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; /** * AMapService */ @injectable() -export default class L7MapService implements IMapService { - public version: string = Version.L7MAP; - public map: Map; - public simpleMapCoord: ISimpleMapCoord = new SimpleMapCoord(); - // 背景色 - public bgColor: string = 'rgba(0.0, 0.0, 0.0, 0.0)'; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.ICoordinateSystemService) - private readonly coordinateSystemService: ICoordinateSystemService; - - @inject(TYPES.IEventEmitter) - private eventEmitter: any; - private viewport: Viewport; - private markerContainer: HTMLElement; - private cameraChangedCallback: (viewport: IViewport) => void; - private $mapContainer: HTMLElement | null; - public setBgColor(color: string) { - this.bgColor = color; - } - - // init - public addMarkerContainer(): void { - const container = this.map.getCanvasContainer(); - this.markerContainer = DOM.create('div', 'l7-marker-container', container); - this.markerContainer.setAttribute('tabindex', '-1'); - } - - public getMarkerContainer(): HTMLElement { - return this.markerContainer; - } - - // map event - public on(type: string, handle: (...args: any[]) => void): void { - if (MapServiceEvent.indexOf(type) !== -1) { - this.eventEmitter.on(type, handle); - } else { - // 统一事件名称 - this.map.on(EventMap[type] || type, handle); - } - } - public off(type: string, handle: (...args: any[]) => void): void { - this.map.off(EventMap[type] || type, handle); - this.eventEmitter.off(type, handle); - } - - public getContainer(): HTMLElement | null { - return this.map.getContainer(); - } - - public getMapCanvasContainer(): HTMLElement { - return this.map.getCanvasContainer() as HTMLElement; - } - - public getSize(): [number, number] { - if (this.version === Version.SIMPLE) { - return this.simpleMapCoord.getSize(); - } - const size = this.map.transform; - - return [size.width, size.height]; - } - // get mapStatus method - - public getType() { - return 'default'; - } - - public getZoom(): number { - return this.map.getZoom(); - } - - public setZoom(zoom: number) { - return this.map.setZoom(zoom); - } - - public getCenter(): ILngLat { - return this.map.getCenter(); - } - - public setCenter(lnglat: [number, number]): void { - this.map.setCenter(lnglat); - } - - public getPitch(): number { - return this.map.getPitch(); - } - - public getRotation(): number { - return this.map.getBearing(); - } - - public getBounds(): Bounds { - return this.map.getBounds().toArray() as Bounds; - } - - public getMinZoom(): number { - return this.map.getMinZoom(); - } - - public getMaxZoom(): number { - return this.map.getMaxZoom(); - } - - public setRotation(rotation: number): void { - this.map.setBearing(rotation); - } - - public zoomIn(option?: any, eventData?: any): void { - this.map.zoomIn(option, eventData); - } - public zoomOut(option?: any, eventData?: any): void { - this.map.zoomOut(option, eventData); - } - public setPitch(pitch: number) { - return this.map.setPitch(pitch); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - - public panBy(x: number = 0, y: number = 0): void { - this.panTo([x, y]); - } - - public fitBounds(bound: Bounds, fitBoundsOptions?: any): void { - this.map.fitBounds(bound, fitBoundsOptions); - } - - public setMaxZoom(max: number): void { - this.map.setMaxZoom(max); - } - - public setMinZoom(min: number): void { - this.map.setMinZoom(min); - } - public setMapStatus(option: Partial): void { - if (option.doubleClickZoom === true) { - this.map.doubleClickZoom.enable(); - } - if (option.doubleClickZoom === false) { - this.map.doubleClickZoom.disable(); - } - if (option.dragEnable === false) { - this.map.dragPan.disable(); - } - if (option.dragEnable === true) { - this.map.dragPan.enable(); - } - if (option.rotateEnable === false) { - this.map.dragRotate.disable(); - } - if (option.dragEnable === true) { - this.map.dragRotate.enable(); - } - if (option.keyboardEnable === false) { - this.map.keyboard.disable(); - } - if (option.keyboardEnable === true) { - this.map.keyboard.enable(); - } - if (option.zoomEnable === false) { - this.map.scrollZoom.disable(); - } - if (option.zoomEnable === true) { - this.map.scrollZoom.enable(); - } - } - - public setZoomAndCenter(zoom: number, center: [number, number]): void { - this.map.flyTo({ - zoom, - center, - }); - } - - public setMapStyle(style: any): void { - this.map.setStyle(this.getMapStyle(style)); - } - - public meterToCoord(center: [number, number], outer: [number, number]) { - return 1.0; - } - - // 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); - } +export default class L7MapService extends BaseMapService { public lngLatToMercator( - lnglat: [number, number], - altitude: number, + _lnglat: [number, number], + _altitude: number, ): IMercator { - throw new Error('not implement'); + throw new Error('Method not implemented.'); } - public getModelMatrix( - lnglat: [number, number], - altitude: number, - rotate: [number, number, number], - scale: [number, number, number] = [1, 1, 1], - origin: IMercator = { x: 0, y: 0, z: 0 }, - ): number[] { - throw new Error('not implement'); + public getModelMatrix(): number[] { + throw new Error('Method not implemented.'); } + public viewport: Viewport; public async init(): Promise { const { id = 'map', - attributionControl = false, style = 'light', rotation = 0, mapInstance, version = 'L7MAP', mapSize = 10000, + interactive = true, ...rest } = this.config; @@ -277,26 +43,17 @@ export default class L7MapService implements IMapService { this.version = version; this.simpleMapCoord.setSize(mapSize); - // console.log('this.config.center', this.config.center) if (version === Version.SIMPLE && rest.center) { rest.center = this.simpleMapCoord.unproject( rest.center as [number, number], ); } - // console.log(this.simpleMapCoord.project(this.config.center as [number, number])) - // console.log(this.simpleMapCoord.unproject([500, 500])) - // console.log(this.simpleMapCoord.project([0, 0])) - // console.log(this.simpleMapCoord.unproject([5000, 5000])) - - // console.log(this.simpleMapCoord.unproject([200, 200])) - // console.log(this.simpleMapCoord.unproject([1000, 1000])) - if (mapInstance) { // @ts-ignore this.map = mapInstance; this.$mapContainer = this.map.getContainer(); } else { - this.$mapContainer = this.creatAmapContainer(id); + this.$mapContainer = this.creatMapContainer(id); // @ts-ignore this.map = new Map({ container: this.$mapContainer, @@ -307,7 +64,10 @@ export default class L7MapService implements IMapService { } this.map.on('load', this.handleCameraChanged); - this.map.on('move', this.handleCameraChanged); + if (interactive) { + // L7 作为第三方地图插件时关闭重绘 + this.map.on('move', this.handleCameraChanged); + } // 不同于高德地图,需要手动触发首次渲染 this.handleCameraChanged(); @@ -317,7 +77,6 @@ export default class L7MapService implements IMapService { public async initMiniMap(): Promise { const { id = 'map', - attributionControl = false, style = 'light', rotation = 0, mapInstance, @@ -372,24 +131,6 @@ export default class L7MapService implements IMapService { } } - public destroy() { - this.eventEmitter.removeAllListeners(); - if (this.map) { - this.map.remove(); - this.$mapContainer = null; - } - } - public emit(name: string, ...args: any[]) { - this.eventEmitter.emit(name, ...args); - } - public once(name: string, ...args: any[]) { - this.eventEmitter.once(name, ...args); - } - - public getMapContainer() { - return this.$mapContainer; - } - public exportMap(type: 'jpg' | 'png'): string { const renderCanvas = this.map.getCanvas(); const layersPng = @@ -398,9 +139,6 @@ export default class L7MapService implements IMapService { : (renderCanvas?.toDataURL('image/png') as string); return layersPng; } - public onCameraChanged(callback: (viewport: IViewport) => void): void { - this.cameraChangedCallback = callback; - } // TODO: 处理小程序中有底图模式下的相机跟新 private handleMiniCameraChanged = ( @@ -439,49 +177,4 @@ export default class L7MapService implements IMapService { this.cameraChangedCallback(this.viewport); }; - - 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(), - 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 && - offsetCoordinate - ) { - this.coordinateSystemService.setCoordinateSystem( - CoordinateSystem.LNGLAT_OFFSET, - ); - } else { - this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.LNGLAT); - } - - this.cameraChangedCallback(this.viewport); - }; - - private creatAmapContainer(id: string | HTMLDivElement) { - let $wrapper = id as HTMLDivElement; - if (typeof id === 'string') { - $wrapper = document.getElementById(id) as HTMLDivElement; - } - return $wrapper; - } - private getMapStyle(name: MapStyle) { - if (typeof name !== 'string') { - return name; - } - return MapTheme[name] ? MapTheme[name] : name; - } } diff --git a/packages/maps/src/mapbox/Viewport.ts b/packages/maps/src/mapbox/Viewport.ts index 27f80593d1..13233f4999 100644 --- a/packages/maps/src/mapbox/Viewport.ts +++ b/packages/maps/src/mapbox/Viewport.ts @@ -2,7 +2,7 @@ import { IMapCamera, IViewport } from '@antv/l7-core'; import WebMercatorViewport from 'viewport-mercator-project'; export default class Viewport implements IViewport { - private viewport: WebMercatorViewport; + public viewport: WebMercatorViewport; public syncWithMapCamera(mapCamera: Partial) { const { diff --git a/packages/maps/src/mapbox/index.ts b/packages/maps/src/mapbox/index.ts index 3bb739a920..4dfaf20cc5 100644 --- a/packages/maps/src/mapbox/index.ts +++ b/packages/maps/src/mapbox/index.ts @@ -1,6 +1,6 @@ import { Map } from 'mapbox-gl'; import { IMapboxInstance } from '../../typings/index'; -import BaseMapWrapper from '../BaseMapWrapper'; +import BaseMapWrapper from '../utils/BaseMapWrapper'; import './logo.css'; import MapboxService from './map'; export default class MapboxWrapper extends BaseMapWrapper< diff --git a/packages/maps/src/mapbox/map.ts b/packages/maps/src/mapbox/map.ts index 21b3298ace..437424d081 100644 --- a/packages/maps/src/mapbox/map.ts +++ b/packages/maps/src/mapbox/map.ts @@ -1,252 +1,36 @@ /** * MapboxService */ -import { - Bounds, - CoordinateSystem, - ICoordinateSystemService, - IGlobalConfigService, - ILngLat, - IMapConfig, - IMapService, - IMercator, - IPoint, - IStatusOptions, - IViewport, - MapServiceEvent, - MapStyle, - TYPES, -} from '@antv/l7-core'; -import { DOM } from '@antv/l7-utils'; +import { IMercator } from '@antv/l7-core'; import { mat4, vec3 } from 'gl-matrix'; -import { inject, injectable } from 'inversify'; +import { injectable } from 'inversify'; import mapboxgl, { Map } from 'mapbox-gl'; // tslint:disable-next-line:no-submodule-imports import 'mapbox-gl/dist/mapbox-gl.css'; import 'reflect-metadata'; import { IMapboxInstance } from '../../typings/index'; -import { Version } from '../version'; import Viewport from './Viewport'; +import BaseMapService from '../utils/BaseMapService'; window.mapboxgl = mapboxgl; -const EventMap: { - [key: string]: any; -} = { - mapmove: 'move', - camerachange: 'move', - zoomchange: 'zoom', - dragging: 'drag', -}; -import { ISimpleMapCoord, SimpleMapCoord } from '../simpleMapCoord'; -import { MapTheme } from './theme'; + let mapdivCount = 0; -const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; const MAPBOX_API_KEY = - // 'pk.eyJ1IjoibHp4dWUiLCJhIjoiY2tvaWZuM2s4MWZuYjJ1dHI5ZGduYTlrdiJ9.DQCfMRbZzx0VSwecQ69McA'; 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2s5OXVzdHlzMDVneDNscDVjdzVmeXl0dyJ9.81SQ5qaJS0xExYLbDZAGpQ'; /** * AMapService */ @injectable() -export default class MapboxService - implements IMapService { - public version: string = Version.MAPBOX; - public map: Map & IMapboxInstance; - public simpleMapCoord: ISimpleMapCoord = new SimpleMapCoord(); - - // 背景色 - public bgColor: string = 'rgba(0.0, 0.0, 0.0, 0.0)'; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.ICoordinateSystemService) - private readonly coordinateSystemService: ICoordinateSystemService; - - @inject(TYPES.IEventEmitter) - private eventEmitter: any; - private viewport: Viewport; - private markerContainer: HTMLElement; - private cameraChangedCallback: (viewport: IViewport) => void; - private $mapContainer: HTMLElement | null; - public setBgColor(color: string) { - this.bgColor = color; - } - - // init - public addMarkerContainer(): void { - const container = this.map.getCanvasContainer(); - this.markerContainer = DOM.create('div', 'l7-marker-container', container); - this.markerContainer.setAttribute('tabindex', '-1'); - } - - public getMarkerContainer(): HTMLElement { - return this.markerContainer; - } - - // map event - public on(type: string, handle: (...args: any[]) => void): void { - if (MapServiceEvent.indexOf(type) !== -1) { - this.eventEmitter.on(type, handle); - } else { - // 统一事件名称 - this.map.on(EventMap[type] || type, handle); - } - } - public off(type: string, handle: (...args: any[]) => void): void { - this.map.off(EventMap[type] || type, handle); - this.eventEmitter.off(type, handle); - } - - public getContainer(): HTMLElement | null { - return this.map.getContainer(); - } - - public getMapCanvasContainer(): HTMLElement { - return this.map.getCanvasContainer() as HTMLElement; - } - - public getSize(): [number, number] { - const size = this.map.transform; - return [size.width, size.height]; - } +export default class MapboxService extends BaseMapService< + Map & IMapboxInstance +> { // get mapStatus method + public viewport: Viewport; + 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 setCenter(lnglat: [number, number]): void { - this.map.setCenter(lnglat); - } - - public getPitch(): number { - return this.map.getPitch(); - } - - public getRotation(): number { - return this.map.getBearing(); - } - - public getBounds(): Bounds { - return this.map.getBounds().toArray() as Bounds; - } - - public getMinZoom(): number { - return this.map.getMinZoom(); - } - - public getMaxZoom(): number { - return this.map.getMaxZoom(); - } - - public setRotation(rotation: number): void { - this.map.setBearing(rotation); - } - - public zoomIn(option?: any, eventData?: any): void { - this.map.zoomIn(option, eventData); - } - public zoomOut(option?: any, eventData?: any): void { - this.map.zoomOut(option, eventData); - } - public setPitch(pitch: number) { - return this.map.setPitch(pitch); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - - public panBy(x: number, y: number): void { - this.panTo([x, y]); - } - - public fitBounds(bound: Bounds, fitBoundsOptions?: unknown): void { - this.map.fitBounds(bound, fitBoundsOptions as mapboxgl.FitBoundsOptions); - } - - public setMaxZoom(max: number): void { - this.map.setMaxZoom(max); - } - - public setMinZoom(min: number): void { - this.map.setMinZoom(min); - } - public setMapStatus(option: Partial): void { - if (option.doubleClickZoom === true) { - this.map.doubleClickZoom.enable(); - } - if (option.doubleClickZoom === false) { - this.map.doubleClickZoom.disable(); - } - if (option.dragEnable === false) { - this.map.dragPan.disable(); - } - if (option.dragEnable === true) { - this.map.dragPan.enable(); - } - if (option.rotateEnable === false) { - this.map.dragRotate.disable(); - } - if (option.rotateEnable === true) { - this.map.dragRotate.enable(); - } - if (option.keyboardEnable === false) { - this.map.keyboard.disable(); - } - if (option.keyboardEnable === true) { - this.map.keyboard.enable(); - } - if (option.zoomEnable === false) { - this.map.scrollZoom.disable(); - } - if (option.zoomEnable === true) { - this.map.scrollZoom.enable(); - } - } - - public setZoomAndCenter(zoom: number, center: [number, number]): void { - this.map.flyTo({ - zoom, - center, - }); - } - - public setMapStyle(style: any): void { - this.map.setStyle(this.getMapStyle(style)); - } - // TODO: 计算像素坐标 - public pixelToLngLat(pixel: [number, number]): ILngLat { - return this.map.unproject(pixel); - } - - public lngLatToPixel(lnglat: [number, number]): IPoint { - return this.map.project(lnglat); - } - - public containerToLngLat(pixel: [number, number]): ILngLat { - return this.map.unproject(pixel); - } - - public lngLatToContainer(lnglat: [number, number]): IPoint { - return this.map.project(lnglat); - } - /** * 将经纬度转成墨卡托坐标 * @param lnglat @@ -354,7 +138,7 @@ export default class MapboxService this.map = mapInstance; this.$mapContainer = this.map.getContainer(); } else { - this.$mapContainer = this.creatAmapContainer(id); + this.$mapContainer = this.creatMapContainer(id); // @ts-ignore this.map = new window.mapboxgl.Map({ container: this.$mapContainer, @@ -427,49 +211,8 @@ export default class MapboxService : (renderCanvas?.toDataURL('image/png') as string); return layersPng; } - 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(); - // Tip: 统一触发地图变化事件 - this.emit('mapchange'); - // 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, - }); - - const { offsetZoom = LNGLAT_OFFSET_ZOOM_THRESHOLD } = this.config; - - // set coordinate system - if (this.viewport.getZoom() > offsetZoom) { - 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 creatAmapContainer(id: string | HTMLDivElement) { + protected creatMapContainer(id: string | HTMLDivElement) { let $wrapper = id as HTMLDivElement; if (typeof id === 'string') { $wrapper = document.getElementById(id) as HTMLDivElement; @@ -485,10 +228,4 @@ export default class MapboxService $wrapper.appendChild($amapdiv); return $amapdiv; } - private getMapStyle(name: MapStyle) { - if (typeof name !== 'string') { - return name; - } - return MapTheme[name] ? MapTheme[name] : name; - } } diff --git a/packages/maps/src/utils/BaseMapService.ts b/packages/maps/src/utils/BaseMapService.ts new file mode 100644 index 0000000000..4b911d9f11 --- /dev/null +++ b/packages/maps/src/utils/BaseMapService.ts @@ -0,0 +1,363 @@ +/** + * MapboxService + */ +import { + Bounds, + CoordinateSystem, + ICoordinateSystemService, + IGlobalConfigService, + ILngLat, + IMapCamera, + IMapConfig, + IMapService, + IMercator, + IPoint, + IStatusOptions, + IViewport, + MapServiceEvent, + MapStyle, + TYPES, +} from '@antv/l7-core'; +import { Map } from '@antv/l7-map'; +import { DOM } from '@antv/l7-utils'; +import { inject, injectable } from 'inversify'; +import 'reflect-metadata'; +import { ISimpleMapCoord, SimpleMapCoord } from './simpleMapCoord'; +import { Version } from '../version'; +const EventMap: { + [key: string]: any; +} = { + mapmove: 'move', + camerachange: 'move', + zoomchange: 'zoom', + dragging: 'drag', +}; +import { MapTheme } from './theme'; + +const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; +/** + * AMapService + */ +@injectable() +export default abstract class BaseMapService implements IMapService { + public version: string = Version.L7MAP; + public map: Map & T; + protected viewport: IViewport | unknown; + public simpleMapCoord: ISimpleMapCoord = new SimpleMapCoord(); + // 背景色 + public bgColor: string = 'rgba(0.0, 0.0, 0.0, 0.0)'; + + @inject(TYPES.MapConfig) + protected readonly config: Partial; + + @inject(TYPES.IGlobalConfigService) + protected readonly configService: IGlobalConfigService; + + @inject(TYPES.ICoordinateSystemService) + protected readonly coordinateSystemService: ICoordinateSystemService; + + @inject(TYPES.IEventEmitter) + protected eventEmitter: any; + + protected markerContainer: HTMLElement; + protected cameraChangedCallback: (viewport: IViewport) => void; + protected $mapContainer: HTMLElement | null; + public setBgColor(color: string) { + this.bgColor = color; + } + + // init + public addMarkerContainer(): void { + const container = this.map.getCanvasContainer(); + this.markerContainer = DOM.create('div', 'l7-marker-container', container); + this.markerContainer.setAttribute('tabindex', '-1'); + } + + public getMarkerContainer(): HTMLElement { + return this.markerContainer; + } + public getOverlayContainer(): HTMLElement | undefined { + return undefined; + } + + // 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); + this.eventEmitter.off(type, handle); + } + + public getContainer(): HTMLElement | null { + return this.map.getContainer(); + } + + public getMapCanvasContainer(): HTMLElement { + return this.map.getCanvasContainer() as HTMLElement; + } + + public getSize(): [number, number] { + if (this.version === Version.SIMPLE) { + return this.simpleMapCoord.getSize(); + } + const size = this.map.transform; + + return [size.width, size.height]; + } + // get mapStatus method + + public getType() { + return 'default'; + } + + public getZoom(): number { + return this.map.getZoom(); + } + + public setZoom(zoom: number) { + return this.map.setZoom(zoom); + } + + public getCenter(): ILngLat { + return this.map.getCenter(); + } + + public setCenter(lnglat: [number, number]): void { + this.map.setCenter(lnglat); + } + + public getPitch(): number { + return this.map.getPitch(); + } + + public getRotation(): number { + return this.map.getBearing(); + } + + public getBounds(): Bounds { + return this.map.getBounds().toArray() as Bounds; + } + + public getMinZoom(): number { + return this.map.getMinZoom(); + } + + public getMaxZoom(): number { + return this.map.getMaxZoom(); + } + + public setRotation(rotation: number): void { + this.map.setBearing(rotation); + } + + public zoomIn(option?: any, eventData?: any): void { + this.map.zoomIn(option, eventData); + } + public zoomOut(option?: any, eventData?: any): void { + this.map.zoomOut(option, eventData); + } + public setPitch(pitch: number) { + return this.map.setPitch(pitch); + } + + public panTo(p: [number, number]): void { + this.map.panTo(p); + } + + public panBy(x: number = 0, y: number = 0): void { + this.panTo([x, y]); + } + + public fitBounds(bound: Bounds, fitBoundsOptions?: any): void { + this.map.fitBounds(bound, fitBoundsOptions); + } + + public setMaxZoom(max: number): void { + this.map.setMaxZoom(max); + } + + public setMinZoom(min: number): void { + this.map.setMinZoom(min); + } + public setMapStatus(option: Partial): void { + if (option.doubleClickZoom === true) { + this.map.doubleClickZoom.enable(); + } + if (option.doubleClickZoom === false) { + this.map.doubleClickZoom.disable(); + } + if (option.dragEnable === false) { + this.map.dragPan.disable(); + } + if (option.dragEnable === true) { + this.map.dragPan.enable(); + } + if (option.rotateEnable === false) { + this.map.dragRotate.disable(); + } + if (option.dragEnable === true) { + this.map.dragRotate.enable(); + } + if (option.keyboardEnable === false) { + this.map.keyboard.disable(); + } + if (option.keyboardEnable === true) { + this.map.keyboard.enable(); + } + if (option.zoomEnable === false) { + this.map.scrollZoom.disable(); + } + if (option.zoomEnable === true) { + this.map.scrollZoom.enable(); + } + } + + public setZoomAndCenter(zoom: number, center: [number, number]): void { + this.map.flyTo({ + zoom, + center, + }); + } + + public setMapStyle(style: any): void { + this.map.setStyle(this.getMapStyle(style)); + } + + public meterToCoord(center: [number, number], outer: [number, number]) { + return 1.0; + } + + // 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 abstract lngLatToMercator( + lnglat: [number, number], + altitude: number, + ): IMercator; + + public abstract getModelMatrix( + lnglat: [number, number], + altitude: number, + rotate: [number, number, number], + scale: [number, number, number], + origin: IMercator, + ): number[]; + + public abstract init(): Promise; + + public destroy() { + this.eventEmitter.removeAllListeners(); + if (this.map) { + this.map.remove(); + this.$mapContainer = null; + } + } + public emit(name: string, ...args: any[]) { + this.eventEmitter.emit(name, ...args); + } + public once(name: string, ...args: any[]) { + this.eventEmitter.once(name, ...args); + } + + public getMapContainer() { + return this.$mapContainer; + } + + public exportMap(type: 'jpg' | 'png'): string { + const renderCanvas = this.map.getCanvas(); + const layersPng = + type === 'jpg' + ? (renderCanvas?.toDataURL('image/jpeg') as string) + : (renderCanvas?.toDataURL('image/png') as string); + return layersPng; + } + public onCameraChanged(callback: (viewport: IViewport) => void): void { + this.cameraChangedCallback = callback; + } + + protected handleCameraChanged = (e?: any) => { + const { lat, lng } = this.map.getCenter(); + // Tip: 统一触发地图变化事件 + this.emit('mapchange'); + // resync + (this.viewport as IViewport).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, + }); + + this.updateCoordinateSystemService(); + this.cameraChangedCallback(this.viewport as IViewport); + }; + + protected creatMapContainer(id: string | HTMLDivElement) { + let $wrapper = id as HTMLDivElement; + if (typeof id === 'string') { + $wrapper = document.getElementById(id) as HTMLDivElement; + } + return $wrapper; + } + public updateView(viewOption: Partial) { + // Tip: 统一触发地图变化事件 + this.emit('mapchange'); + // resync + (this.viewport as IViewport).syncWithMapCamera({ + bearing: viewOption.bearing, + center: viewOption.center, + viewportHeight: viewOption.viewportHeight, + pitch: viewOption.pitch, + viewportWidth: viewOption.viewportWidth, + zoom: viewOption.zoom, + // mapbox 中固定相机高度为 viewport 高度的 1.5 倍 + cameraHeight: 0, + }); + this.updateCoordinateSystemService(); + this.cameraChangedCallback(this.viewport as IViewport); + } + + protected updateCoordinateSystemService() { + const { offsetCoordinate = true } = this.config; + // set coordinate system + if ( + (this.viewport as IViewport).getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD && + offsetCoordinate + ) { + this.coordinateSystemService.setCoordinateSystem( + CoordinateSystem.LNGLAT_OFFSET, + ); + } else { + this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.LNGLAT); + } + } + + protected getMapStyle(name: MapStyle) { + if (typeof name !== 'string') { + return name; + } + return MapTheme[name] ? MapTheme[name] : name; + } +} diff --git a/packages/maps/src/BaseMapWrapper.ts b/packages/maps/src/utils/BaseMapWrapper.ts similarity index 80% rename from packages/maps/src/BaseMapWrapper.ts rename to packages/maps/src/utils/BaseMapWrapper.ts index 71aca9ce99..f6a2ba2f30 100644 --- a/packages/maps/src/BaseMapWrapper.ts +++ b/packages/maps/src/utils/BaseMapWrapper.ts @@ -23,15 +23,6 @@ export default class BaseMapWrapper implements IMapWrapper { canvas?: HTMLCanvasElement, hasBaseMap?: boolean, ) { - // // 首先使用全局配置服务校验地图参数 - // const { valid, errorText } = this.configService.validateMapConfig( - // this.config, - // ); - - // if (!valid) { - // this.logger.error(errorText || ''); - // return; - // } // 绑定用户传入的原始地图参数 sceneContainer.bind>(TYPES.MapConfig).toConstantValue({ ...this.config, diff --git a/packages/maps/src/map/Viewport.ts b/packages/maps/src/utils/Viewport.ts similarity index 82% rename from packages/maps/src/map/Viewport.ts rename to packages/maps/src/utils/Viewport.ts index 6bbada973a..9ef3484885 100644 --- a/packages/maps/src/map/Viewport.ts +++ b/packages/maps/src/utils/Viewport.ts @@ -2,7 +2,7 @@ import { IMapCamera, IViewport } from '@antv/l7-core'; import WebMercatorViewport from 'viewport-mercator-project'; export default class Viewport implements IViewport { - private viewport: WebMercatorViewport; + public viewport: WebMercatorViewport; public syncWithMapCamera(mapCamera: Partial) { const { @@ -14,11 +14,24 @@ export default class Viewport implements IViewport { viewportWidth, } = mapCamera; + const preView = this.viewport + ? { + width: this.viewport.width, + height: this.viewport.height, + longitude: this.viewport.center[0], + latitude: this.viewport.center[1], + zoom: this.viewport.zoom, + pitch: this.viewport.pitch, + bearing: this.viewport.bearing, + } + : {}; + /** * Deck.gl 使用的也是 Mapbox 同步相机,相机参数保持一致 * 例如相机高度固定为 height * 1.5,因此不需要传 */ this.viewport = new WebMercatorViewport({ + ...preView, width: viewportWidth, height: viewportHeight, longitude: center && center[0], diff --git a/packages/maps/src/utils/amap/AMapBaseService.ts b/packages/maps/src/utils/amap/AMapBaseService.ts new file mode 100644 index 0000000000..5bd285977a --- /dev/null +++ b/packages/maps/src/utils/amap/AMapBaseService.ts @@ -0,0 +1,559 @@ +/** + * AMapService + */ +import AMapLoader from '@amap/amap-jsapi-loader'; +import { + Bounds, + CoordinateSystem, + ICameraOptions, + ICoordinateSystemService, + IGlobalConfigService, + ILngLat, + IMapConfig, + IMapService, + IMercator, + IPoint, + IStatusOptions, + IViewport, + MapServiceEvent, + TYPES, + IMapCamera, +} from '@antv/l7-core'; +import { DOM } from '@antv/l7-utils'; +import { mat4, vec3 } from 'gl-matrix'; +import { inject, injectable } from 'inversify'; +import 'reflect-metadata'; +import { IAMapEvent, IAMapInstance } from '../../../typings/index'; +import { ISimpleMapCoord, SimpleMapCoord } from '../simpleMapCoord'; +import { toPaddingOptions } from '../utils'; +import { Version } from '../../version'; +import Viewport from '../Viewport'; +import './logo.css'; +import { MapTheme } from './theme'; +let mapdivCount = 0; +// @ts-ignore +window.forceWebGL = true; + +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 AMapBaseService + implements IMapService { + public version: string = Version['GAODE1.x']; + public simpleMapCoord: ISimpleMapCoord = new SimpleMapCoord(); + /** + * 原始地图实例 + */ + public map: AMap.Map & IAMapInstance; + + // 背景色 + public bgColor: string = 'rgba(0, 0, 0, 0)'; + + @inject(TYPES.IGlobalConfigService) + protected readonly configService: IGlobalConfigService; + + @inject(TYPES.MapConfig) + protected readonly config: Partial; + + @inject(TYPES.ICoordinateSystemService) + protected readonly coordinateSystemService: ICoordinateSystemService; + + @inject(TYPES.IEventEmitter) + protected eventEmitter: any; + + protected markerContainer: HTMLElement; + protected $mapContainer: HTMLElement | null; + + protected viewport: IViewport; + + protected cameraChangedCallback: (viewport: IViewport) => void; + public setBgColor(color: string) { + this.bgColor = color; + } + + 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 getMapCanvasContainer(): HTMLElement { + return this.map + .getContainer() + ?.getElementsByClassName('amap-maps')[0] as HTMLElement; + } + + 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 { + // 统一设置 Mapbox 缩放等级 + return this.map.setZoom(zoom + 1); + } + + public getCenter(options?: ICameraOptions): ILngLat { + if (options?.padding) { + const originCenter = this.getCenter(); + const [w, h] = this.getSize(); + const padding = toPaddingOptions(options.padding); + const px = this.lngLatToPixel([originCenter.lng, originCenter.lat]); + const offsetPx = [ + (padding.right - padding.left) / 2, + (padding.bottom - padding.top) / 2, + ]; + + const newCenter = this.pixelToLngLat([ + px.x - offsetPx[0], + px.y - offsetPx[1], + ]); + return newCenter; + } + const center = this.map.getCenter(); + return { + lng: center.getLng(), + lat: center.getLat(), + }; + } + public setCenter(lnglat: [number, number], options?: ICameraOptions): void { + if (options?.padding) { + const padding = toPaddingOptions(options.padding); + const px = this.lngLatToPixel(lnglat); + const offsetPx = [ + (padding.right - padding.left) / 2, + (padding.bottom - padding.top) / 2, + ]; + const newCenter = this.pixelToLngLat([ + px.x + offsetPx[0], + px.y + offsetPx[1], + ]); + this.map.setCenter([newCenter.lng, newCenter.lat]); + } else { + this.map.setCenter(lnglat); + } + } + 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 setPitch(pitch: number) { + return this.map.setPitch(pitch); + } + public zoomIn(): void { + this.map.zoomIn(); + } + + public zoomOut(): void { + this.map.zoomOut(); + } + + public panTo(p: [number, number]): void { + this.map.panTo(p); + } + + public panBy(x: number = 0, y: number = 0): void { + this.map.panBy(x, y); + } + + 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 + 1, center); + } + + public setMapStyle(style: string): void { + this.map.setMapStyle(this.getMapStyle(style)); + } + + public setMapStatus(option: Partial): void { + this.map.setStatus(option); + } + 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 lngLatToCoord(lnglat: [number, number]): any { + // @ts-ignore + const { x, y } = this.map.lngLatToGeodeticCoord(lnglat); + return [x, -y]; + } + + public lngLatToMercator( + lnglat: [number, number], + altitude: number, + ): IMercator { + return { + x: 0, + y: 0, + z: 0, + }; + } + + public getModelMatrix( + lnglat: [number, number], + altitude: number, + rotate: [number, number, number], + scale: [number, number, number] = [1, 1, 1], + + ): number[] { + const flat = this.viewport.projectFlat(lnglat); + // @ts-ignore + const modelMatrix = mat4.create(); + + mat4.translate( + modelMatrix, + modelMatrix, + vec3.fromValues(flat[0], flat[1], altitude), + ); + mat4.scale( + modelMatrix, + modelMatrix, + vec3.fromValues(scale[0], scale[1], scale[2]), + ); + + mat4.rotateX(modelMatrix, modelMatrix, rotate[0]); + mat4.rotateY(modelMatrix, modelMatrix, rotate[1]); + mat4.rotateZ(modelMatrix, modelMatrix, rotate[2]); + + return (modelMatrix as unknown) as number[]; + } + + public async init(): Promise { + 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.creatMapContainer( + id as string | HTMLDivElement, + ); + const mapConstructorOptions = { + mapStyle: this.getMapStyle(style as string), + zooms: [minZoom, maxZoom], + viewMode: '3D', + ...rest, + }; + if (mapConstructorOptions.zoom) { + // TODO: 高德地图在相同大小下需要比 MapBox 多一个 zoom 层级 + mapConstructorOptions.zoom += 1; + } + // @ts-ignore + const map = new AMap.Map(this.$mapContainer, mapConstructorOptions); + // 监听地图相机事件 + map.on('camerachange', this.handleCameraChanged); + // Tip: 为了兼容开启 MultiPassRender 的情况 + // 修复 MultiPassRender 在高德地图 1.x 的情况下,缩放地图改变 zoom 时存在可视化层和底图不同步的现象 + map.on('camerachange', () => { + setTimeout(() => this.handleAfterMapChange()); + }); + + // @ts-ignore + this.map = map; + setTimeout(() => { + resolve(); + }, 10); + } + }; + if (!amapLoaded && !mapInstance) { + if (token === AMAP_API_KEY) { + console.warn(this.configService.getSceneWarninfo('MapToken')); + } + amapLoaded = true; + plugin.push('Map3D'); + AMapLoader.load({ + key: token, // 申请好的Web端开发者Key,首次调用 load 时必填 + version: AMAP_VERSION, // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 + plugins: plugin, // 需要使用的的插件列表,如比例尺'AMap.Scale'等 + }) + .then((AMap) => { + resolveMap(); + + if (pendingResolveQueue.length) { + pendingResolveQueue.forEach((r) => r()); + pendingResolveQueue = []; + } + }) + .catch((e) => { + throw new Error(e); + }); + } else { + if ((amapLoaded && window.AMap) || mapInstance) { + resolveMap(); + } else { + pendingResolveQueue.push(resolveMap); + } + } + }); + + this.viewport = new Viewport(); + } + + public meterToCoord(center: [number, number], outer: [number, number]) { + // 统一根据经纬度来转化 + // Tip: 实际米距离 unit meter + const meterDis = AMap.GeometryUtil.distance( + new AMap.LngLat(...center), + new AMap.LngLat(...outer), + ); + + // Tip: 三维世界坐标距离 + const [x1, y1] = this.lngLatToCoord(center); + const [x2, y2] = this.lngLatToCoord(outer); + const coordDis = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); + + return coordDis / meterDis; + } + + public updateView(viewOption: Partial): void {} + public getOverlayContainer(): HTMLElement | undefined { + return undefined; + } + + public exportMap(type: 'jpg' | 'png'): string { + const renderCanvas = this.getContainer()?.getElementsByClassName( + 'amap-layer', + )[0] as HTMLCanvasElement; + const layersPng = + type === 'jpg' + ? (renderCanvas?.toDataURL('image/jpeg') as string) + : (renderCanvas?.toDataURL('image/png') as string); + return layersPng; + } + + 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(); + + // TODO: 销毁地图可视化层的容器 + this.$mapContainer?.parentNode?.removeChild(this.$mapContainer); + + // @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; + } + + protected handleAfterMapChange() { + this.emit('mapAfterFrameChange'); + } + + protected handleCameraChanged = (e: IAMapEvent): void => { + const { + fov, + near, + far, + height, + pitch, + rotation, + aspect, + position, + } = e.camera; + const { lng, lat } = this.getCenter(); + // Tip: 触发地图变化事件 + this.emit('mapchange'); + + if (this.cameraChangedCallback) { + // resync viewport + // console.log('cameraHeight', height) + // console.log('pitch', pitch) + // console.log('rotation', rotation) + // console.log('zoom', this.map.getZoom()) + 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], + }); + const { offsetZoom = LNGLAT_OFFSET_ZOOM_THRESHOLD } = this.config; + // console.log('this.viewport', this.viewport) + // set coordinate system + if (this.viewport.getZoom() > offsetZoom) { + this.coordinateSystemService.setCoordinateSystem( + CoordinateSystem.P20_OFFSET, + ); + } else { + this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20); + } + this.cameraChangedCallback(this.viewport); + } + }; + + protected getMapStyle(name: string): string { + return MapTheme[name] ? MapTheme[name] : name; + } + protected creatMapContainer(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; + } +} diff --git a/packages/maps/src/utils/amap/Viewport.ts b/packages/maps/src/utils/amap/Viewport.ts new file mode 100644 index 0000000000..674e5bb6ce --- /dev/null +++ b/packages/maps/src/utils/amap/Viewport.ts @@ -0,0 +1,135 @@ +import { IMapCamera, IViewport } from '@antv/l7-core'; +import { mat4, vec3 } from 'gl-matrix'; + +const DEGREES_TO_RADIANS = Math.PI / 180; + +export default class Viewport implements IViewport { + private projectionMatrix: mat4 = mat4.create(); + private viewMatrix: mat4 = mat4.create(); + private viewProjectionMatrix: mat4 = mat4.create(); + private ViewProjectionMatrixUncentered: mat4 = mat4.create(); + private viewUncenteredMatrix: mat4 = mat4.create(); + private zoom: number; + private center: number[]; + + public syncWithMapCamera(mapCamera: Partial) { + const { + zoom = 1, + pitch = 0, + bearing = 0, + center = [0, 0], + offsetOrigin = [0, 0], + cameraHeight = 1, + aspect = 1, + near = 0.1, + far = 1000, + fov = 0, + } = mapCamera; + this.zoom = zoom; + this.center = center; + + const pitchInRadians = pitch * DEGREES_TO_RADIANS; + const rotationInRadians = (360 - bearing) * DEGREES_TO_RADIANS; + + // 计算透视投影矩阵 projectionMatrix + mat4.perspective(this.projectionMatrix, fov, aspect, near, far); + // 计算相机矩阵 viewMatrix + const eye = vec3.fromValues( + cameraHeight * Math.sin(pitchInRadians) * Math.sin(rotationInRadians), + -cameraHeight * Math.sin(pitchInRadians) * Math.cos(rotationInRadians), + cameraHeight * Math.cos(pitchInRadians), + ); + const up = vec3.fromValues( + -Math.cos(pitchInRadians) * Math.sin(rotationInRadians), + Math.cos(pitchInRadians) * Math.cos(rotationInRadians), + Math.sin(pitchInRadians), + ); + mat4.lookAt(this.viewMatrix, eye, vec3.fromValues(0, 0, 0), up); + this.viewUncenteredMatrix = mat4.clone(this.viewMatrix); + + // 移动相机位置 + mat4.translate( + this.viewMatrix, + this.viewMatrix, + vec3.fromValues(-offsetOrigin[0], offsetOrigin[1], 0), + ); + + mat4.multiply( + this.viewProjectionMatrix, + this.projectionMatrix, + this.viewMatrix, + ); + mat4.multiply( + this.ViewProjectionMatrixUncentered, + this.projectionMatrix, + this.viewMatrix, + ); + } + + public getZoom(): number { + return this.zoom; + } + + public getZoomScale(): number { + // 512 尺寸下的缩放:2 ^ 19 + return 524288; + } + + public getCenter(): [number, number] { + const [lng, lat] = this.center; + return [lng, lat]; + } + + public getProjectionMatrix(): number[] { + // @ts-ignore + return this.projectionMatrix; + } + + public getModelMatrix(): number[] { + return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + } + + public getViewMatrix(): number[] { + // @ts-ignore + return this.viewMatrix; + } + + public getViewMatrixUncentered(): number[] { + // @ts-ignore + return this.viewUncenteredMatrix; + } + public getViewProjectionMatrix(): number[] { + // @ts-ignore + return this.viewProjectionMatrix; + } + + public getViewProjectionMatrixUncentered(): number[] { + // @ts-ignore + return this.ViewProjectionMatrixUncentered; + } + + public getFocalDistance() { + return 1; + } + + /** + * P20 坐标系,固定 scale + */ + public projectFlat(lngLat: [number, number]): [number, number] { + const maxs = 85.0511287798; + const lat = Math.max(Math.min(maxs, lngLat[1]), -maxs); + // tslint:disable-next-line:no-bitwise + const zoomScale = 256 << 20; + let d = Math.PI / 180; + let x = lngLat[0] * d; + let y = lat * d; + y = Math.log(Math.tan(Math.PI / 4 + y / 2)); + const a = 0.5 / Math.PI; + const b = 0.5; + const c = -0.5 / Math.PI; + d = 0.5; + x = zoomScale * (a * x + b) - 215440491; + y = -(zoomScale * (c * y + d) - 106744817); + return [x, y]; + } +} diff --git a/packages/maps/src/utils/amap/logo.css b/packages/maps/src/utils/amap/logo.css new file mode 100644 index 0000000000..5dbe1028c9 --- /dev/null +++ b/packages/maps/src/utils/amap/logo.css @@ -0,0 +1,3 @@ +.amap-logo{ + display: none !important; +} diff --git a/packages/maps/src/utils/amap/theme.ts b/packages/maps/src/utils/amap/theme.ts new file mode 100644 index 0000000000..1a139973d5 --- /dev/null +++ b/packages/maps/src/utils/amap/theme.ts @@ -0,0 +1,8 @@ +export const MapTheme: { + [key: string]: any; +} = { + dark: 'amap://styles/c9f1d10cae34f8ab05e425462c5a58d7?isPublic=true', + light: 'amap://styles/c422f5c0cfced5be9fe3a83f05f28a68?isPublic=true', + normal: 'amap://styles/normal', + blank: 'amap://styles/07c17002b38775b32a7a76c66cf90e99?isPublic=true', +}; diff --git a/packages/maps/src/utils/index.ts b/packages/maps/src/utils/index.ts new file mode 100644 index 0000000000..dc6f661724 --- /dev/null +++ b/packages/maps/src/utils/index.ts @@ -0,0 +1,4 @@ +import Viewport from './Viewport'; +import BaseMapWrapper from './BaseMapWrapper'; +import BaseMapService from './BaseMapService'; +export { Viewport, BaseMapWrapper, BaseMapService }; diff --git a/packages/maps/src/simpleMapCoord.ts b/packages/maps/src/utils/simpleMapCoord.ts similarity index 100% rename from packages/maps/src/simpleMapCoord.ts rename to packages/maps/src/utils/simpleMapCoord.ts diff --git a/packages/maps/src/utils/theme.ts b/packages/maps/src/utils/theme.ts new file mode 100644 index 0000000000..3b29aa499f --- /dev/null +++ b/packages/maps/src/utils/theme.ts @@ -0,0 +1,23 @@ +export const MapTheme: { + [key: string]: any; +} = { + light: 'mapbox://styles/zcxduo/ck2ypyb1r3q9o1co1766dex29', + dark: 'mapbox://styles/zcxduo/ck241p6413s0b1cpayzldv7x7', + normal: 'mapbox://styles/mapbox/streets-v11', + blank: { + version: 8, + // sprite: 'https://lzxue.github.io/font-glyphs/sprite/sprite', + // glyphs: + // 'https://gw.alipayobjects.com/os/antvdemo/assets/mapbox/glyphs/{fontstack}/{range}.pbf', + sources: {}, + layers: [ + { + id: 'background', + type: 'background', + layout: { + visibility: 'none', + }, + }, + ], + }, +}; diff --git a/packages/maps/src/utils.ts b/packages/maps/src/utils/utils.ts similarity index 94% rename from packages/maps/src/utils.ts rename to packages/maps/src/utils/utils.ts index f476b25f98..a1fd9956f2 100644 --- a/packages/maps/src/utils.ts +++ b/packages/maps/src/utils/utils.ts @@ -42,5 +42,5 @@ export function toPaddingOptions(padding: IPadding = {}) { } } - return Object.assign({},defaultPadding,padding) + return Object.assign({}, defaultPadding, padding); } diff --git a/packages/source/package.json b/packages/source/package.json index d74b66bae6..1ca171078a 100644 --- a/packages/source/package.json +++ b/packages/source/package.json @@ -24,7 +24,7 @@ "author": "lzxue", "license": "ISC", "dependencies": { - "@antv/async-hook": "^2.1.0", + "@antv/async-hook": "^2.2.2", "@antv/l7-core": "2.9.26", "@antv/l7-utils": "2.9.26", "@babel/runtime": "^7.7.7", diff --git a/tsconfig.json b/tsconfig.json index 77f40b8b30..a235d2c89c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,6 +31,7 @@ "@antv/l7-utils": ["packages/utils/src"], "@antv/l7-test-utils":["packages/test-utils/src"], "@antv/l7": ["packages/l7/src"], + "@antv/l7-leaflet": ["packages/leaflet/src"], "*": ["packages", "typings/*"] } },