From 705be24204d4e8a4c5270e85d27eed1a796068e6 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Mon, 3 Feb 2020 21:32:13 +0800 Subject: [PATCH 1/6] chore: update version 2.0.14 --- lerna.json | 2 +- packages/component/package.json | 6 +++--- packages/core/package.json | 4 ++-- packages/l7/package.json | 12 ++++++------ packages/layers/package.json | 8 ++++---- packages/maps/package.json | 6 +++--- packages/renderer/package.json | 4 ++-- packages/scene/package.json | 12 ++++++------ packages/source/package.json | 6 +++--- packages/utils/package.json | 2 +- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/lerna.json b/lerna.json index 595db63121..5ba29a6a9e 100644 --- a/lerna.json +++ b/lerna.json @@ -14,7 +14,7 @@ "message": "chore: publish" } }, - "version": "2.0.13", + "version": "2.0.14", "npmClient": "yarn", "useWorkspaces": true, "publishConfig": { diff --git a/packages/component/package.json b/packages/component/package.json index fc5594b93b..4b06c4b056 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-component", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", @@ -24,8 +24,8 @@ "author": "lzxue", "license": "ISC", "dependencies": { - "@antv/l7-core": "^2.0.13", - "@antv/l7-utils": "^2.0.13", + "@antv/l7-core": "^2.0.14", + "@antv/l7-utils": "^2.0.14", "@babel/runtime": "^7.7.7", "eventemitter3": "^4.0.0", "inversify": "^5.0.1", diff --git a/packages/core/package.json b/packages/core/package.json index b4d09669ef..e88ae42a81 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-core", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", @@ -22,7 +22,7 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@antv/l7-utils": "^2.0.13", + "@antv/l7-utils": "^2.0.14", "@babel/runtime": "^7.7.7", "@mapbox/tiny-sdf": "^1.1.1", "ajv": "^6.10.2", diff --git a/packages/l7/package.json b/packages/l7/package.json index 652cd7b88d..50d3dd43b2 100644 --- a/packages/l7/package.json +++ b/packages/l7/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7", - "version": "2.0.13", + "version": "2.0.14", "description": "A Large-scale WebGL-powered Geospatial Data Visualization", "main": "lib/index.js", "module": "es/index.js", @@ -24,11 +24,11 @@ "author": "antv", "license": "MIT", "dependencies": { - "@antv/l7-component": "^2.0.13", - "@antv/l7-core": "^2.0.13", - "@antv/l7-layers": "^2.0.13", - "@antv/l7-maps": "^2.0.13", - "@antv/l7-scene": "^2.0.13", + "@antv/l7-component": "^2.0.14", + "@antv/l7-core": "^2.0.14", + "@antv/l7-layers": "^2.0.14", + "@antv/l7-maps": "^2.0.14", + "@antv/l7-scene": "^2.0.14", "@babel/runtime": "^7.7.7" }, "gitHead": "9fabb78790428d2025b89fb6146fc555cb1d987d", diff --git a/packages/layers/package.json b/packages/layers/package.json index 4028a93be4..fbba05bfe7 100644 --- a/packages/layers/package.json +++ b/packages/layers/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-layers", - "version": "2.0.13", + "version": "2.0.14", "description": "L7's collection of built-in layers", "main": "lib/index.js", "module": "es/index.js", @@ -22,9 +22,9 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@antv/l7-core": "^2.0.13", - "@antv/l7-source": "^2.0.13", - "@antv/l7-utils": "^2.0.13", + "@antv/l7-core": "^2.0.14", + "@antv/l7-source": "^2.0.14", + "@antv/l7-utils": "^2.0.14", "@babel/runtime": "^7.7.7", "@mapbox/martini": "^0.1.0", "@turf/meta": "^6.0.2", diff --git a/packages/maps/package.json b/packages/maps/package.json index c89d903dee..d900e5af27 100644 --- a/packages/maps/package.json +++ b/packages/maps/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-maps", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", @@ -23,8 +23,8 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@antv/l7-core": "^2.0.13", - "@antv/l7-utils": "^2.0.13", + "@antv/l7-core": "^2.0.14", + "@antv/l7-utils": "^2.0.14", "@babel/runtime": "^7.7.7", "gl-matrix": "^3.1.0", "inversify": "^5.0.1", diff --git a/packages/renderer/package.json b/packages/renderer/package.json index 6ad1e3859c..e7303957e3 100644 --- a/packages/renderer/package.json +++ b/packages/renderer/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-renderer", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", @@ -25,7 +25,7 @@ "gl": "^4.4.0" }, "dependencies": { - "@antv/l7-core": "^2.0.13", + "@antv/l7-core": "^2.0.14", "@babel/runtime": "^7.7.7", "inversify": "^5.0.1", "lodash": "^4.17.15", diff --git a/packages/scene/package.json b/packages/scene/package.json index 0345f89603..2e870bd85b 100644 --- a/packages/scene/package.json +++ b/packages/scene/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-scene", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", @@ -22,11 +22,11 @@ "author": "xiaoiver", "license": "ISC", "dependencies": { - "@antv/l7-component": "^2.0.13", - "@antv/l7-core": "^2.0.13", - "@antv/l7-maps": "^2.0.13", - "@antv/l7-renderer": "^2.0.13", - "@antv/l7-utils": "^2.0.13", + "@antv/l7-component": "^2.0.14", + "@antv/l7-core": "^2.0.14", + "@antv/l7-maps": "^2.0.14", + "@antv/l7-renderer": "^2.0.14", + "@antv/l7-utils": "^2.0.14", "@babel/runtime": "^7.7.7", "inversify": "^5.0.1", "mapbox-gl": "^1.2.1", diff --git a/packages/source/package.json b/packages/source/package.json index 8c022bd094..ff09c66641 100644 --- a/packages/source/package.json +++ b/packages/source/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-source", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", @@ -24,8 +24,8 @@ "author": "lzxue", "license": "ISC", "dependencies": { - "@antv/l7-core": "^2.0.13", - "@antv/l7-utils": "^2.0.13", + "@antv/l7-core": "^2.0.14", + "@antv/l7-utils": "^2.0.14", "@babel/runtime": "^7.7.7", "@mapbox/geojson-rewind": "^0.4.0", "@turf/helpers": "^6.1.4", diff --git a/packages/utils/package.json b/packages/utils/package.json index 39825d5b00..e89bb224eb 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@antv/l7-utils", - "version": "2.0.13", + "version": "2.0.14", "description": "", "main": "lib/index.js", "module": "es/index.js", From 62da56dc2286481588eec1deed283df712d59208 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Tue, 4 Feb 2020 22:37:11 +0800 Subject: [PATCH 2/6] =?UTF-8?q?improvement:=20=E5=A2=9E=E5=8A=A0L7=20logo?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E6=98=BE=E7=A4=BA=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/scene.zh.md | 16 + .../core/src/services/config/ConfigService.ts | 2 + .../src/services/config/IConfigService.ts | 4 +- .../core/src/services/scene/ISceneService.ts | 2 +- .../core/src/services/scene/SceneService.ts | 9 +- packages/maps/src/amap/Wrapper.ts | 11 - packages/maps/src/amap/index.ts | 398 +----------------- packages/maps/src/amap/map.ts | 393 +++++++++++++++++ packages/maps/src/index.ts | 4 +- packages/maps/src/mapbox/Wrapper.ts | 12 - packages/maps/src/mapbox/index.ts | 332 +-------------- packages/maps/src/mapbox/map.ts | 328 +++++++++++++++ packages/scene/src/index.ts | 10 +- stories/Layers/components/Point.tsx | 2 +- 14 files changed, 773 insertions(+), 750 deletions(-) delete mode 100644 packages/maps/src/amap/Wrapper.ts create mode 100644 packages/maps/src/amap/map.ts delete mode 100644 packages/maps/src/mapbox/Wrapper.ts create mode 100644 packages/maps/src/mapbox/map.ts diff --git a/docs/api/scene.zh.md b/docs/api/scene.zh.md index 4e8a03871a..30675b7f2e 100644 --- a/docs/api/scene.zh.md +++ b/docs/api/scene.zh.md @@ -76,6 +76,22 @@ const scene = new L7.Scene({ 需传入 dom 容器或者容器 id  {domObject || string} [必选] +### logoPosition + +L7 Logo的显示位置 默认左下角 + +- bottomright +- topright +- bottomleft, +- topleft` + + +### logoVisible + +是否显示L7的Logo {boolean} true + + + ### zoom 地图初始显示级别 {number} (0-22) diff --git a/packages/core/src/services/config/ConfigService.ts b/packages/core/src/services/config/ConfigService.ts index 7025ab6dd4..cdbffd5c0e 100644 --- a/packages/core/src/services/config/ConfigService.ts +++ b/packages/core/src/services/config/ConfigService.ts @@ -12,6 +12,8 @@ import WarnInfo, { IWarnInfo } from './warnInfo'; */ const defaultSceneConfig: Partial = { id: 'map', + logoPosition: 'bottomleft', + logoVisible: true, }; /** diff --git a/packages/core/src/services/config/IConfigService.ts b/packages/core/src/services/config/IConfigService.ts index 9b09e2a052..c7aa5ca400 100644 --- a/packages/core/src/services/config/IConfigService.ts +++ b/packages/core/src/services/config/IConfigService.ts @@ -1,11 +1,13 @@ import Ajv from 'ajv'; +import { PositionName } from '../component/IControlService'; import { ILayerConfig } from '../layer/ILayerService'; import { IMapWrapper } from '../map/IMapService'; import { IRenderConfig } from '../renderer/IRendererService'; - export interface ISceneConfig extends IRenderConfig { id: string | HTMLDivElement; map: IMapWrapper; + logoPosition?: PositionName; + logoVisible?: boolean; } interface IValidateResult { diff --git a/packages/core/src/services/scene/ISceneService.ts b/packages/core/src/services/scene/ISceneService.ts index e6a6cf95a9..50bc35ab01 100644 --- a/packages/core/src/services/scene/ISceneService.ts +++ b/packages/core/src/services/scene/ISceneService.ts @@ -10,7 +10,7 @@ export interface ISceneService { addLayer(layer: ILayer): void; render(): void; getSceneContainer(): HTMLDivElement; - ExportMap2Png(): string; + exportPng(): string; destroy(): void; } // scene 事件 diff --git a/packages/core/src/services/scene/SceneService.ts b/packages/core/src/services/scene/SceneService.ts index 45776b2e57..e60adb1692 100644 --- a/packages/core/src/services/scene/SceneService.ts +++ b/packages/core/src/services/scene/SceneService.ts @@ -226,10 +226,11 @@ export default class Scene extends EventEmitter implements ISceneService { return this.$container as HTMLDivElement; } - public ExportMap2Png(): string { + public exportPng(): string { const renderCanvas = this.$container?.getElementsByTagName('canvas')[0]; - this.render(); - const layersPng = renderCanvas?.toDataURL() as string; + // this.render(); + DOM.printCanvas(renderCanvas as HTMLCanvasElement); + const layersPng = renderCanvas?.toDataURL('image/png') as string; return layersPng; } @@ -237,11 +238,11 @@ export default class Scene extends EventEmitter implements ISceneService { this.emit('destroy'); this.inited = false; this.layerService.destroy(); + this.rendererService.destroy(); this.interactionService.destroy(); this.controlService.destroy(); this.markerService.destroy(); this.removeAllListeners(); - this.rendererService.destroy(); this.map.destroy(); unbind(this.$container as HTMLDivElement, this.handleWindowResized); window diff --git a/packages/maps/src/amap/Wrapper.ts b/packages/maps/src/amap/Wrapper.ts deleted file mode 100644 index 7114e93c1d..0000000000 --- a/packages/maps/src/amap/Wrapper.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IAMapInstance } from '../../typings/index'; -import BaseMapWrapper from '../BaseMapWrapper'; -import AMapService from './index'; - -export default class AMapWrapper extends BaseMapWrapper< - AMap.Map & IAMapInstance -> { - protected getServiceConstructor() { - return AMapService; - } -} diff --git a/packages/maps/src/amap/index.ts b/packages/maps/src/amap/index.ts index 59b2af238f..245811aad8 100644 --- a/packages/maps/src/amap/index.ts +++ b/packages/maps/src/amap/index.ts @@ -1,393 +1,11 @@ -/** - * AMapService - */ -import { - Bounds, - CoordinateSystem, - ICoordinateSystemService, - IGlobalConfigService, - ILngLat, - ILogService, - IMapConfig, - IMapService, - IPoint, - IViewport, - MapServiceEvent, - MapStyle, - TYPES, -} from '@antv/l7-core'; -import { DOM } from '@antv/l7-utils'; -import { inject, injectable } from 'inversify'; -import { IAMapEvent, IAMapInstance } from '../../typings/index'; -import { MapTheme } from './theme'; -import Viewport from './Viewport'; -let mapdivCount = 0; +import { IAMapInstance } from '../../typings/index'; +import BaseMapWrapper from '../BaseMapWrapper'; +import AMapService from './map'; -const AMAP_API_KEY: string = '15cd8a57710d40c9b7c0e3cc120f1200'; -const AMAP_VERSION: string = '1.4.15'; -/** - * 确保多个场景只引入一个高德地图脚本 - */ -const AMAP_SCRIPT_ID: string = 'amap-script'; -/** - * 高德地图脚本是否加载完毕 - */ -let amapLoaded = false; -/** - * 高德地图脚本加载成功等待队列,成功之后依次触发 - */ -let pendingResolveQueue: Array<() => void> = []; -const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; // 暂时关闭 fix 统一不同坐标系,不同底图的高度位置 - -/** - * AMapService - */ -@injectable() -export default class AMapService - implements IMapService { - /** - * 原始地图实例 - */ - public map: AMap.Map & IAMapInstance; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.ILogService) - private readonly logger: ILogService; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @inject(TYPES.ICoordinateSystemService) - private readonly coordinateSystemService: ICoordinateSystemService; - - @inject(TYPES.IEventEmitter) - private eventEmitter: any; - - private markerContainer: HTMLElement; - private $mapContainer: HTMLElement | null; - - private viewport: Viewport; - - private cameraChangedCallback: (viewport: IViewport) => void; - - public addMarkerContainer(): void { - const mapContainer = this.map.getContainer(); - if (mapContainer !== null) { - const amap = mapContainer.getElementsByClassName( - 'amap-maps', - )[0] as HTMLElement; - this.markerContainer = DOM.create('div', 'l7-marker-container', amap); - } - } - public getMarkerContainer(): HTMLElement { - return this.markerContainer; - } - - // map event - public on(type: string, handler: (...args: any[]) => void): void { - if (MapServiceEvent.indexOf(type) !== -1) { - this.eventEmitter.on(type, handler); - } else { - this.map.on(type, handler); - } - } - public off(type: string, handler: (...args: any[]) => void): void { - if (MapServiceEvent.indexOf(type) !== -1) { - this.eventEmitter.off(type, handler); - } else { - this.map.off(type, handler); - } - } - - public getContainer(): HTMLElement | null { - return this.map.getContainer(); - } - - public getSize(): [number, number] { - const size = this.map.getSize(); - return [size.getWidth(), size.getHeight()]; - } - - public getType() { - return 'amap'; - } - public getZoom(): number { - // 统一返回 Mapbox 缩放等级 - return this.map.getZoom() - 1; - } - - public setZoom(zoom: number): void { - return this.map.setZoom(zoom); - } - - public getCenter(): ILngLat { - const center = this.map.getCenter(); - return { - lng: center.getLng(), - lat: center.getLat(), - }; - } - - public getPitch(): number { - return this.map.getPitch(); - } - - public getRotation(): number { - // 统一返回逆时针旋转角度 - return 360 - this.map.getRotation(); - } - - public getBounds(): Bounds { - // @ts-ignore - const amapBound = this.map.getBounds().toBounds(); - const NE = amapBound.getNorthEast(); - const SW = amapBound.getSouthWest(); - const center = this.getCenter(); - const maxlng = - center.lng > NE.getLng() || center.lng < SW.getLng() - ? 180 - NE.getLng() - : NE.getLng(); - const minlng = center.lng < SW.getLng() ? SW.getLng() - 180 : SW.getLng(); - // 兼容 Mapbox,统一返回西南、东北 - return [ - [minlng, SW.getLat()], - [maxlng, NE.getLat()], - ]; - } - - public getMinZoom(): number { - const zooms = this.map.get('zooms') as [number, number]; - return zooms[0] - 1; - } - public getMaxZoom(): number { - const zooms = this.map.get('zooms') as [number, number]; - return zooms[1] - 1; - } - public setRotation(rotation: number): void { - return this.map.setRotation(rotation); - } - - public zoomIn(): void { - this.map.zoomIn(); - } - - public zoomOut(): void { - this.map.zoomOut(); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - public panBy(pixel: [number, number]): void { - this.map.panTo(pixel); - } - public fitBounds(extent: Bounds): void { - this.map.setBounds( - new AMap.Bounds([extent[0][0], extent[0][1], extent[1][0], extent[1][1]]), - ); - } - public setZoomAndCenter(zoom: number, center: [number, number]): void { - this.map.setZoomAndCenter(zoom, center); - } - public setMapStyle(style: string): void { - this.map.setMapStyle(this.getMapStyle(style)); - } - public pixelToLngLat(pixel: [number, number]): ILngLat { - const lngLat = this.map.pixelToLngLat(new AMap.Pixel(pixel[0], pixel[1])); - return { lng: lngLat.getLng(), lat: lngLat.getLat() }; - } - public lngLatToPixel(lnglat: [number, number]): IPoint { - const p = this.map.lnglatToPixel(new AMap.LngLat(lnglat[0], lnglat[1])); - return { - x: p.getX(), - y: p.getY(), - }; - } - public containerToLngLat(pixel: [number, number]): ILngLat { - const ll = new AMap.Pixel(pixel[0], pixel[1]); - const lngLat = this.map.containerToLngLat(ll); - return { - lng: lngLat?.getLng(), - lat: lngLat?.getLat(), - }; - } - public lngLatToContainer(lnglat: [number, number]): IPoint { - const ll = new AMap.LngLat(lnglat[0], lnglat[1]); - const pixel = this.map.lngLatToContainer(ll); - return { - x: pixel.getX(), - y: pixel.getY(), - }; - } - - public async init(): Promise { - const { - id, - style = 'light', - minZoom = 0, - maxZoom = 18, - token = AMAP_API_KEY, - mapInstance, - plugin = [], - ...rest - } = this.config; - // 高德地图创建独立的container; - // tslint:disable-next-line:typedef - await new Promise((resolve) => { - const resolveMap = () => { - if (mapInstance) { - this.map = mapInstance as AMap.Map & IAMapInstance; - this.$mapContainer = this.map.getContainer(); - setTimeout(() => { - this.map.on('camerachange', this.handleCameraChanged); - resolve(); - }, 30); - } else { - this.$mapContainer = this.creatAmapContainer( - id as string | HTMLDivElement, - ); - - const map = new AMap.Map(this.$mapContainer, { - mapStyle: this.getMapStyle(style as string), - zooms: [minZoom, maxZoom], - viewMode: '3D', - ...rest, - }); - // 监听地图相机事件 - map.on('camerachange', this.handleCameraChanged); - // @ts-ignore - this.map = map; - setTimeout(() => { - resolve(); - }, 10); - } - }; - if (!amapLoaded && !mapInstance) { - if (token === AMAP_API_KEY) { - this.logger.warn(this.configService.getSceneWarninfo('MapToken')); - } - amapLoaded = true; - plugin.push('Map3D'); - this.loadAMapScript( - `https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${token}&plugin=${plugin.join( - ',', - )}`, - ).then(() => { - resolveMap(); - if (pendingResolveQueue.length) { - pendingResolveQueue.forEach((r) => r()); - pendingResolveQueue = []; - } - }); - } else { - if ((amapLoaded && window.AMap) || mapInstance) { - resolveMap(); - } else { - pendingResolveQueue.push(resolveMap); - } - } - }); - - this.viewport = new Viewport(); - } - public emit(name: string, ...args: any[]) { - this.eventEmitter.emit(name, ...args); - } - - public once(name: string, ...args: any[]) { - this.eventEmitter.once(name, ...args); - } - - public destroy() { - this.map.destroy(); - // @ts-ignore - delete window.initAMap; - const $jsapi = document.getElementById(AMAP_SCRIPT_ID); - if ($jsapi) { - document.head.removeChild($jsapi); - } - } - - public getMapContainer() { - return this.$mapContainer; - } - - public onCameraChanged(callback: (viewport: IViewport) => void): void { - this.cameraChangedCallback = callback; - } - - private handleCameraChanged = (e: IAMapEvent): void => { - const { - fov, - near, - far, - height, - pitch, - rotation, - aspect, - position, - } = e.camera; - const { lng, lat } = this.getCenter(); - if (this.cameraChangedCallback) { - // resync viewport - this.viewport.syncWithMapCamera({ - aspect, - // AMap 定义 rotation 为顺时针方向,而 Mapbox 为逆时针 - // @see https://docs.mapbox.com/mapbox-gl-js/api/#map#getbearing - bearing: 360 - rotation, - far, - fov, - cameraHeight: height, - near, - pitch, - // AMap 定义的缩放等级 与 Mapbox 相差 1 - zoom: this.map.getZoom() - 1, - center: [lng, lat], - offsetOrigin: [position.x, position.y], - }); - - // set coordinate system - if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) { - this.coordinateSystemService.setCoordinateSystem( - CoordinateSystem.P20_OFFSET, - ); - } else { - this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20); - } - this.cameraChangedCallback(this.viewport); - } - }; - - private getMapStyle(name: string): string { - return MapTheme[name] ? MapTheme[name] : name; - } - private creatAmapContainer(id: string | HTMLDivElement) { - let $wrapper = id as HTMLDivElement; - if (typeof id === 'string') { - $wrapper = document.getElementById(id) as HTMLDivElement; - } - const $amapdiv = document.createElement('div'); - $amapdiv.style.cssText += ` - position: absolute; - top: 0; - height: 100%; - width: 100%; - `; - $amapdiv.id = 'l7_amap_div' + mapdivCount++; - $wrapper.appendChild($amapdiv); - return $amapdiv; - } - private loadAMapScript(src: string) { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.src = src; - script.onload = () => { - resolve(); - }; - script.onerror = reject; - document.head.appendChild(script); - }); +export default class AMapWrapper extends BaseMapWrapper< + AMap.Map & IAMapInstance +> { + protected getServiceConstructor() { + return AMapService; } } diff --git a/packages/maps/src/amap/map.ts b/packages/maps/src/amap/map.ts new file mode 100644 index 0000000000..59b2af238f --- /dev/null +++ b/packages/maps/src/amap/map.ts @@ -0,0 +1,393 @@ +/** + * AMapService + */ +import { + Bounds, + CoordinateSystem, + ICoordinateSystemService, + IGlobalConfigService, + ILngLat, + ILogService, + IMapConfig, + IMapService, + IPoint, + IViewport, + MapServiceEvent, + MapStyle, + TYPES, +} from '@antv/l7-core'; +import { DOM } from '@antv/l7-utils'; +import { inject, injectable } from 'inversify'; +import { IAMapEvent, IAMapInstance } from '../../typings/index'; +import { MapTheme } from './theme'; +import Viewport from './Viewport'; +let mapdivCount = 0; + +const AMAP_API_KEY: string = '15cd8a57710d40c9b7c0e3cc120f1200'; +const AMAP_VERSION: string = '1.4.15'; +/** + * 确保多个场景只引入一个高德地图脚本 + */ +const AMAP_SCRIPT_ID: string = 'amap-script'; +/** + * 高德地图脚本是否加载完毕 + */ +let amapLoaded = false; +/** + * 高德地图脚本加载成功等待队列,成功之后依次触发 + */ +let pendingResolveQueue: Array<() => void> = []; +const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; // 暂时关闭 fix 统一不同坐标系,不同底图的高度位置 + +/** + * AMapService + */ +@injectable() +export default class AMapService + implements IMapService { + /** + * 原始地图实例 + */ + public map: AMap.Map & IAMapInstance; + + @inject(TYPES.IGlobalConfigService) + private readonly configService: IGlobalConfigService; + + @inject(TYPES.ILogService) + private readonly logger: ILogService; + + @inject(TYPES.MapConfig) + private readonly config: Partial; + + @inject(TYPES.ICoordinateSystemService) + private readonly coordinateSystemService: ICoordinateSystemService; + + @inject(TYPES.IEventEmitter) + private eventEmitter: any; + + private markerContainer: HTMLElement; + private $mapContainer: HTMLElement | null; + + private viewport: Viewport; + + private cameraChangedCallback: (viewport: IViewport) => void; + + public addMarkerContainer(): void { + const mapContainer = this.map.getContainer(); + if (mapContainer !== null) { + const amap = mapContainer.getElementsByClassName( + 'amap-maps', + )[0] as HTMLElement; + this.markerContainer = DOM.create('div', 'l7-marker-container', amap); + } + } + public getMarkerContainer(): HTMLElement { + return this.markerContainer; + } + + // map event + public on(type: string, handler: (...args: any[]) => void): void { + if (MapServiceEvent.indexOf(type) !== -1) { + this.eventEmitter.on(type, handler); + } else { + this.map.on(type, handler); + } + } + public off(type: string, handler: (...args: any[]) => void): void { + if (MapServiceEvent.indexOf(type) !== -1) { + this.eventEmitter.off(type, handler); + } else { + this.map.off(type, handler); + } + } + + public getContainer(): HTMLElement | null { + return this.map.getContainer(); + } + + public getSize(): [number, number] { + const size = this.map.getSize(); + return [size.getWidth(), size.getHeight()]; + } + + public getType() { + return 'amap'; + } + public getZoom(): number { + // 统一返回 Mapbox 缩放等级 + return this.map.getZoom() - 1; + } + + public setZoom(zoom: number): void { + return this.map.setZoom(zoom); + } + + public getCenter(): ILngLat { + const center = this.map.getCenter(); + return { + lng: center.getLng(), + lat: center.getLat(), + }; + } + + public getPitch(): number { + return this.map.getPitch(); + } + + public getRotation(): number { + // 统一返回逆时针旋转角度 + return 360 - this.map.getRotation(); + } + + public getBounds(): Bounds { + // @ts-ignore + const amapBound = this.map.getBounds().toBounds(); + const NE = amapBound.getNorthEast(); + const SW = amapBound.getSouthWest(); + const center = this.getCenter(); + const maxlng = + center.lng > NE.getLng() || center.lng < SW.getLng() + ? 180 - NE.getLng() + : NE.getLng(); + const minlng = center.lng < SW.getLng() ? SW.getLng() - 180 : SW.getLng(); + // 兼容 Mapbox,统一返回西南、东北 + return [ + [minlng, SW.getLat()], + [maxlng, NE.getLat()], + ]; + } + + public getMinZoom(): number { + const zooms = this.map.get('zooms') as [number, number]; + return zooms[0] - 1; + } + public getMaxZoom(): number { + const zooms = this.map.get('zooms') as [number, number]; + return zooms[1] - 1; + } + public setRotation(rotation: number): void { + return this.map.setRotation(rotation); + } + + public zoomIn(): void { + this.map.zoomIn(); + } + + public zoomOut(): void { + this.map.zoomOut(); + } + + public panTo(p: [number, number]): void { + this.map.panTo(p); + } + public panBy(pixel: [number, number]): void { + this.map.panTo(pixel); + } + public fitBounds(extent: Bounds): void { + this.map.setBounds( + new AMap.Bounds([extent[0][0], extent[0][1], extent[1][0], extent[1][1]]), + ); + } + public setZoomAndCenter(zoom: number, center: [number, number]): void { + this.map.setZoomAndCenter(zoom, center); + } + public setMapStyle(style: string): void { + this.map.setMapStyle(this.getMapStyle(style)); + } + public pixelToLngLat(pixel: [number, number]): ILngLat { + const lngLat = this.map.pixelToLngLat(new AMap.Pixel(pixel[0], pixel[1])); + return { lng: lngLat.getLng(), lat: lngLat.getLat() }; + } + public lngLatToPixel(lnglat: [number, number]): IPoint { + const p = this.map.lnglatToPixel(new AMap.LngLat(lnglat[0], lnglat[1])); + return { + x: p.getX(), + y: p.getY(), + }; + } + public containerToLngLat(pixel: [number, number]): ILngLat { + const ll = new AMap.Pixel(pixel[0], pixel[1]); + const lngLat = this.map.containerToLngLat(ll); + return { + lng: lngLat?.getLng(), + lat: lngLat?.getLat(), + }; + } + public lngLatToContainer(lnglat: [number, number]): IPoint { + const ll = new AMap.LngLat(lnglat[0], lnglat[1]); + const pixel = this.map.lngLatToContainer(ll); + return { + x: pixel.getX(), + y: pixel.getY(), + }; + } + + public async init(): Promise { + const { + id, + style = 'light', + minZoom = 0, + maxZoom = 18, + token = AMAP_API_KEY, + mapInstance, + plugin = [], + ...rest + } = this.config; + // 高德地图创建独立的container; + // tslint:disable-next-line:typedef + await new Promise((resolve) => { + const resolveMap = () => { + if (mapInstance) { + this.map = mapInstance as AMap.Map & IAMapInstance; + this.$mapContainer = this.map.getContainer(); + setTimeout(() => { + this.map.on('camerachange', this.handleCameraChanged); + resolve(); + }, 30); + } else { + this.$mapContainer = this.creatAmapContainer( + id as string | HTMLDivElement, + ); + + const map = new AMap.Map(this.$mapContainer, { + mapStyle: this.getMapStyle(style as string), + zooms: [minZoom, maxZoom], + viewMode: '3D', + ...rest, + }); + // 监听地图相机事件 + map.on('camerachange', this.handleCameraChanged); + // @ts-ignore + this.map = map; + setTimeout(() => { + resolve(); + }, 10); + } + }; + if (!amapLoaded && !mapInstance) { + if (token === AMAP_API_KEY) { + this.logger.warn(this.configService.getSceneWarninfo('MapToken')); + } + amapLoaded = true; + plugin.push('Map3D'); + this.loadAMapScript( + `https://webapi.amap.com/maps?v=${AMAP_VERSION}&key=${token}&plugin=${plugin.join( + ',', + )}`, + ).then(() => { + resolveMap(); + if (pendingResolveQueue.length) { + pendingResolveQueue.forEach((r) => r()); + pendingResolveQueue = []; + } + }); + } else { + if ((amapLoaded && window.AMap) || mapInstance) { + resolveMap(); + } else { + pendingResolveQueue.push(resolveMap); + } + } + }); + + this.viewport = new Viewport(); + } + public emit(name: string, ...args: any[]) { + this.eventEmitter.emit(name, ...args); + } + + public once(name: string, ...args: any[]) { + this.eventEmitter.once(name, ...args); + } + + public destroy() { + this.map.destroy(); + // @ts-ignore + delete window.initAMap; + const $jsapi = document.getElementById(AMAP_SCRIPT_ID); + if ($jsapi) { + document.head.removeChild($jsapi); + } + } + + public getMapContainer() { + return this.$mapContainer; + } + + public onCameraChanged(callback: (viewport: IViewport) => void): void { + this.cameraChangedCallback = callback; + } + + private handleCameraChanged = (e: IAMapEvent): void => { + const { + fov, + near, + far, + height, + pitch, + rotation, + aspect, + position, + } = e.camera; + const { lng, lat } = this.getCenter(); + if (this.cameraChangedCallback) { + // resync viewport + this.viewport.syncWithMapCamera({ + aspect, + // AMap 定义 rotation 为顺时针方向,而 Mapbox 为逆时针 + // @see https://docs.mapbox.com/mapbox-gl-js/api/#map#getbearing + bearing: 360 - rotation, + far, + fov, + cameraHeight: height, + near, + pitch, + // AMap 定义的缩放等级 与 Mapbox 相差 1 + zoom: this.map.getZoom() - 1, + center: [lng, lat], + offsetOrigin: [position.x, position.y], + }); + + // set coordinate system + if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) { + this.coordinateSystemService.setCoordinateSystem( + CoordinateSystem.P20_OFFSET, + ); + } else { + this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20); + } + this.cameraChangedCallback(this.viewport); + } + }; + + private getMapStyle(name: string): string { + return MapTheme[name] ? MapTheme[name] : name; + } + private creatAmapContainer(id: string | HTMLDivElement) { + let $wrapper = id as HTMLDivElement; + if (typeof id === 'string') { + $wrapper = document.getElementById(id) as HTMLDivElement; + } + const $amapdiv = document.createElement('div'); + $amapdiv.style.cssText += ` + position: absolute; + top: 0; + height: 100%; + width: 100%; + `; + $amapdiv.id = 'l7_amap_div' + mapdivCount++; + $wrapper.appendChild($amapdiv); + return $amapdiv; + } + private loadAMapScript(src: string) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.src = src; + script.onload = () => { + resolve(); + }; + script.onerror = reject; + document.head.appendChild(script); + }); + } +} diff --git a/packages/maps/src/index.ts b/packages/maps/src/index.ts index 980d870217..920d74d4a4 100644 --- a/packages/maps/src/index.ts +++ b/packages/maps/src/index.ts @@ -1,4 +1,4 @@ -import GaodeMap from './amap/Wrapper'; -import Mapbox from './mapbox/Wrapper'; +import GaodeMap from './amap/'; +import Mapbox from './mapbox/'; export { GaodeMap, Mapbox }; diff --git a/packages/maps/src/mapbox/Wrapper.ts b/packages/maps/src/mapbox/Wrapper.ts deleted file mode 100644 index b3fe8faec1..0000000000 --- a/packages/maps/src/mapbox/Wrapper.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Map } from 'mapbox-gl'; -import { IMapboxInstance } from '../../typings/index'; -import BaseMapWrapper from '../BaseMapWrapper'; -import MapboxService from './index'; - -export default class MapboxWrapper extends BaseMapWrapper< - Map & IMapboxInstance -> { - protected getServiceConstructor() { - return MapboxService; - } -} diff --git a/packages/maps/src/mapbox/index.ts b/packages/maps/src/mapbox/index.ts index b8659c1716..dc6e5f2259 100644 --- a/packages/maps/src/mapbox/index.ts +++ b/packages/maps/src/mapbox/index.ts @@ -1,328 +1,12 @@ -/** - * MapboxService - */ -import { - Bounds, - CoordinateSystem, - ICoordinateSystemService, - IGlobalConfigService, - ILngLat, - ILogService, - IMapConfig, - IMapService, - IPoint, - IViewport, - MapServiceEvent, - MapStyle, - TYPES, -} from '@antv/l7-core'; -import { DOM } from '@antv/l7-utils'; -import { inject, injectable } from 'inversify'; -import mapboxgl, { IControl, Map } from 'mapbox-gl'; +import { Map } from 'mapbox-gl'; import { IMapboxInstance } from '../../typings/index'; -import Viewport from './Viewport'; -const EventMap: { - [key: string]: any; -} = { - mapmove: 'move', - camerachange: 'move', -}; -import { MapTheme } from './theme'; +import BaseMapWrapper from '../BaseMapWrapper'; +import MapboxService from './map'; -const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; -const MAPBOX_API_KEY = - 'pk.eyJ1IjoibHp4dWUiLCJhIjoiYnhfTURyRSJ9.Ugm314vAKPHBzcPmY1p4KQ'; -/** - * AMapService - */ -@injectable() -export default class MapboxService - implements IMapService { - public map: Map & IMapboxInstance; - - @inject(TYPES.MapConfig) - private readonly config: Partial; - - @inject(TYPES.IGlobalConfigService) - private readonly configService: IGlobalConfigService; - - @inject(TYPES.ILogService) - private readonly logger: ILogService; - @inject(TYPES.ICoordinateSystemService) - private readonly coordinateSystemService: ICoordinateSystemService; - - @inject(TYPES.IEventEmitter) - private eventEmitter: any; - private viewport: Viewport; - private markerContainer: HTMLElement; - private cameraChangedCallback: (viewport: IViewport) => void; - private $mapContainer: HTMLElement | null; - - // init - public addMarkerContainer(): void { - const container = this.map.getCanvasContainer(); - this.markerContainer = DOM.create('div', 'l7-marker-container', container); - } - - public getMarkerContainer(): HTMLElement { - return this.markerContainer; - } - - // map event - public on(type: string, handle: (...args: any[]) => void): void { - if (MapServiceEvent.indexOf(type) !== -1) { - this.eventEmitter.on(type, handle); - } else { - // 统一事件名称 - this.map.on(EventMap[type] || type, handle); - } - } - public off(type: string, handle: (...args: any[]) => void): void { - this.map.off(EventMap[type] || type, handle); - } - - public getContainer(): HTMLElement | null { - return this.map.getContainer(); - } - - public getSize(): [number, number] { - const size = this.map.transform; - return [size.width, size.height]; - } - // get mapStatus method - - public getType() { - return 'mapbox'; - } - - public getZoom(): number { - return this.map.getZoom(); - } - - public setZoom(zoom: number) { - return this.map.setZoom(zoom); - } - - public getCenter(): ILngLat { - return this.map.getCenter(); - } - - public getPitch(): number { - return this.map.getPitch(); - } - - public getRotation(): number { - return this.map.getBearing(); - } - - public getBounds(): Bounds { - return this.map.getBounds().toArray() as Bounds; - } - - public getMinZoom(): number { - return this.map.getMinZoom(); - } - - public getMaxZoom(): number { - return this.map.getMaxZoom(); - } - - public setRotation(rotation: number): void { - this.map.setBearing(rotation); - } - - public zoomIn(): void { - this.map.zoomIn(); - } - - public zoomOut(): void { - this.map.zoomOut(); - } - - public panTo(p: [number, number]): void { - this.map.panTo(p); - } - - public panBy(pixel: [number, number]): void { - this.panTo(pixel); - } - - public fitBounds(bound: Bounds): void { - this.map.fitBounds(bound); - } - - public setMaxZoom(max: number): void { - this.map.setMaxZoom(max); - } - - public setMinZoom(min: number): void { - this.map.setMinZoom(min); - } - - public setZoomAndCenter(zoom: number, center: [number, number]): void { - this.map.flyTo({ - zoom, - center, - }); - } - - public setMapStyle(style: string): void { - this.map.setStyle(this.getMapStyle(style)); - } - // TODO: 计算像素坐标 - public pixelToLngLat(pixel: [number, number]): ILngLat { - return this.map.unproject(pixel); - } - - public lngLatToPixel(lnglat: [number, number]): IPoint { - return this.map.project(lnglat); - } - - public containerToLngLat(pixel: [number, number]): ILngLat { - return this.map.unproject(pixel); - } - - public lngLatToContainer(lnglat: [number, number]): IPoint { - return this.map.project(lnglat); - } - - public async init(): Promise { - const { - id = 'map', - attributionControl = false, - style = 'light', - token = MAPBOX_API_KEY, - rotation = 0, - mapInstance, - ...rest - } = this.config; - - this.viewport = new Viewport(); - - /** - * TODO: 使用 mapbox v0.53.x 版本 custom layer,需要共享 gl context - * @see https://github.com/mapbox/mapbox-gl-js/blob/master/debug/threejs.html#L61-L64 - */ - - // 判断全局 mapboxgl 对象的加载 - if (!mapInstance && !mapboxgl) { - // 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。 - this.logger.error(this.configService.getSceneWarninfo('SDK')); - } - - if ( - token === MAPBOX_API_KEY && - style !== 'blank' && - !mapboxgl.accessToken && - !mapInstance // 如果用户传递了 mapInstance,应该不去干预实例的 accessToken。 - ) { - this.logger.warn(this.configService.getSceneWarninfo('MapToken')); - } - - // 判断是否设置了 accessToken - if (!mapInstance && !mapboxgl.accessToken) { - // 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。 - mapboxgl.accessToken = token; - } - - if (mapInstance) { - // @ts-ignore - this.map = mapInstance; - this.$mapContainer = this.map.getContainer(); - } else { - this.$mapContainer = this.creatAmapContainer(id); - // @ts-ignore - this.map = new mapboxgl.Map({ - container: id, - style: this.getMapStyle(style), - attributionControl, - bearing: rotation, - ...rest, - }); - } - this.map.on('load', this.handleCameraChanged); - this.map.on('move', this.handleCameraChanged); - - // 不同于高德地图,需要手动触发首次渲染 - this.handleCameraChanged(); - } - - public destroy() { - this.eventEmitter.removeAllListeners(); - if (this.map) { - this.map.remove(); - this.$mapContainer = null; - this.removeLogoControl(); - } - } - public emit(name: string, ...args: any[]) { - this.eventEmitter.emit(name, ...args); - } - public once(name: string, ...args: any[]) { - this.eventEmitter.once(name, ...args); - } - - public getMapContainer() { - return this.$mapContainer; - } - - public onCameraChanged(callback: (viewport: IViewport) => void): void { - this.cameraChangedCallback = callback; - } - - private handleCameraChanged = () => { - // @see https://github.com/mapbox/mapbox-gl-js/issues/2572 - const { lat, lng } = this.map.getCenter().wrap(); - - // resync - this.viewport.syncWithMapCamera({ - bearing: this.map.getBearing(), - center: [lng, lat], - viewportHeight: this.map.transform.height, - pitch: this.map.getPitch(), - viewportWidth: this.map.transform.width, - zoom: this.map.getZoom(), - // mapbox 中固定相机高度为 viewport 高度的 1.5 倍 - cameraHeight: 0, - }); - - // set coordinate system - if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) { - this.coordinateSystemService.setCoordinateSystem( - CoordinateSystem.LNGLAT_OFFSET, - ); - } else { - this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.LNGLAT); - } - - this.cameraChangedCallback(this.viewport); - }; - - private creatAmapContainer(id: string | HTMLDivElement) { - let $wrapper = id as HTMLDivElement; - if (typeof id === 'string') { - $wrapper = document.getElementById(id) as HTMLDivElement; - } - return $wrapper; - } - - private removeLogoControl(): void { - // @ts-ignore - const controls = this.map._controls as IControl[]; - const logoCtr = controls.find((ctr: IControl) => { - if (ctr.hasOwnProperty('_updateLogo')) { - return true; - } - }); - if (logoCtr) { - this.map.removeControl(logoCtr); - } - } - - private getMapStyle(name: MapStyle) { - if (typeof name !== 'string') { - return name; - } - return MapTheme[name] ? MapTheme[name] : name; +export default class MapboxWrapper extends BaseMapWrapper< + Map & IMapboxInstance +> { + protected getServiceConstructor() { + return MapboxService; } } diff --git a/packages/maps/src/mapbox/map.ts b/packages/maps/src/mapbox/map.ts new file mode 100644 index 0000000000..b8659c1716 --- /dev/null +++ b/packages/maps/src/mapbox/map.ts @@ -0,0 +1,328 @@ +/** + * MapboxService + */ +import { + Bounds, + CoordinateSystem, + ICoordinateSystemService, + IGlobalConfigService, + ILngLat, + ILogService, + IMapConfig, + IMapService, + IPoint, + IViewport, + MapServiceEvent, + MapStyle, + TYPES, +} from '@antv/l7-core'; +import { DOM } from '@antv/l7-utils'; +import { inject, injectable } from 'inversify'; +import mapboxgl, { IControl, Map } from 'mapbox-gl'; +import { IMapboxInstance } from '../../typings/index'; +import Viewport from './Viewport'; +const EventMap: { + [key: string]: any; +} = { + mapmove: 'move', + camerachange: 'move', +}; +import { MapTheme } from './theme'; + +const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; +const MAPBOX_API_KEY = + 'pk.eyJ1IjoibHp4dWUiLCJhIjoiYnhfTURyRSJ9.Ugm314vAKPHBzcPmY1p4KQ'; +/** + * AMapService + */ +@injectable() +export default class MapboxService + implements IMapService { + public map: Map & IMapboxInstance; + + @inject(TYPES.MapConfig) + private readonly config: Partial; + + @inject(TYPES.IGlobalConfigService) + private readonly configService: IGlobalConfigService; + + @inject(TYPES.ILogService) + private readonly logger: ILogService; + @inject(TYPES.ICoordinateSystemService) + private readonly coordinateSystemService: ICoordinateSystemService; + + @inject(TYPES.IEventEmitter) + private eventEmitter: any; + private viewport: Viewport; + private markerContainer: HTMLElement; + private cameraChangedCallback: (viewport: IViewport) => void; + private $mapContainer: HTMLElement | null; + + // init + public addMarkerContainer(): void { + const container = this.map.getCanvasContainer(); + this.markerContainer = DOM.create('div', 'l7-marker-container', container); + } + + public getMarkerContainer(): HTMLElement { + return this.markerContainer; + } + + // map event + public on(type: string, handle: (...args: any[]) => void): void { + if (MapServiceEvent.indexOf(type) !== -1) { + this.eventEmitter.on(type, handle); + } else { + // 统一事件名称 + this.map.on(EventMap[type] || type, handle); + } + } + public off(type: string, handle: (...args: any[]) => void): void { + this.map.off(EventMap[type] || type, handle); + } + + public getContainer(): HTMLElement | null { + return this.map.getContainer(); + } + + public getSize(): [number, number] { + const size = this.map.transform; + return [size.width, size.height]; + } + // get mapStatus method + + public getType() { + return 'mapbox'; + } + + public getZoom(): number { + return this.map.getZoom(); + } + + public setZoom(zoom: number) { + return this.map.setZoom(zoom); + } + + public getCenter(): ILngLat { + return this.map.getCenter(); + } + + public getPitch(): number { + return this.map.getPitch(); + } + + public getRotation(): number { + return this.map.getBearing(); + } + + public getBounds(): Bounds { + return this.map.getBounds().toArray() as Bounds; + } + + public getMinZoom(): number { + return this.map.getMinZoom(); + } + + public getMaxZoom(): number { + return this.map.getMaxZoom(); + } + + public setRotation(rotation: number): void { + this.map.setBearing(rotation); + } + + public zoomIn(): void { + this.map.zoomIn(); + } + + public zoomOut(): void { + this.map.zoomOut(); + } + + public panTo(p: [number, number]): void { + this.map.panTo(p); + } + + public panBy(pixel: [number, number]): void { + this.panTo(pixel); + } + + public fitBounds(bound: Bounds): void { + this.map.fitBounds(bound); + } + + public setMaxZoom(max: number): void { + this.map.setMaxZoom(max); + } + + public setMinZoom(min: number): void { + this.map.setMinZoom(min); + } + + public setZoomAndCenter(zoom: number, center: [number, number]): void { + this.map.flyTo({ + zoom, + center, + }); + } + + public setMapStyle(style: string): void { + this.map.setStyle(this.getMapStyle(style)); + } + // TODO: 计算像素坐标 + public pixelToLngLat(pixel: [number, number]): ILngLat { + return this.map.unproject(pixel); + } + + public lngLatToPixel(lnglat: [number, number]): IPoint { + return this.map.project(lnglat); + } + + public containerToLngLat(pixel: [number, number]): ILngLat { + return this.map.unproject(pixel); + } + + public lngLatToContainer(lnglat: [number, number]): IPoint { + return this.map.project(lnglat); + } + + public async init(): Promise { + const { + id = 'map', + attributionControl = false, + style = 'light', + token = MAPBOX_API_KEY, + rotation = 0, + mapInstance, + ...rest + } = this.config; + + this.viewport = new Viewport(); + + /** + * TODO: 使用 mapbox v0.53.x 版本 custom layer,需要共享 gl context + * @see https://github.com/mapbox/mapbox-gl-js/blob/master/debug/threejs.html#L61-L64 + */ + + // 判断全局 mapboxgl 对象的加载 + if (!mapInstance && !mapboxgl) { + // 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。 + this.logger.error(this.configService.getSceneWarninfo('SDK')); + } + + if ( + token === MAPBOX_API_KEY && + style !== 'blank' && + !mapboxgl.accessToken && + !mapInstance // 如果用户传递了 mapInstance,应该不去干预实例的 accessToken。 + ) { + this.logger.warn(this.configService.getSceneWarninfo('MapToken')); + } + + // 判断是否设置了 accessToken + if (!mapInstance && !mapboxgl.accessToken) { + // 用户有时传递进来的实例是继承于 mapbox 实例化的,不一定是 mapboxgl 对象。 + mapboxgl.accessToken = token; + } + + if (mapInstance) { + // @ts-ignore + this.map = mapInstance; + this.$mapContainer = this.map.getContainer(); + } else { + this.$mapContainer = this.creatAmapContainer(id); + // @ts-ignore + this.map = new mapboxgl.Map({ + container: id, + style: this.getMapStyle(style), + attributionControl, + bearing: rotation, + ...rest, + }); + } + this.map.on('load', this.handleCameraChanged); + this.map.on('move', this.handleCameraChanged); + + // 不同于高德地图,需要手动触发首次渲染 + this.handleCameraChanged(); + } + + public destroy() { + this.eventEmitter.removeAllListeners(); + if (this.map) { + this.map.remove(); + this.$mapContainer = null; + this.removeLogoControl(); + } + } + public emit(name: string, ...args: any[]) { + this.eventEmitter.emit(name, ...args); + } + public once(name: string, ...args: any[]) { + this.eventEmitter.once(name, ...args); + } + + public getMapContainer() { + return this.$mapContainer; + } + + public onCameraChanged(callback: (viewport: IViewport) => void): void { + this.cameraChangedCallback = callback; + } + + private handleCameraChanged = () => { + // @see https://github.com/mapbox/mapbox-gl-js/issues/2572 + const { lat, lng } = this.map.getCenter().wrap(); + + // resync + this.viewport.syncWithMapCamera({ + bearing: this.map.getBearing(), + center: [lng, lat], + viewportHeight: this.map.transform.height, + pitch: this.map.getPitch(), + viewportWidth: this.map.transform.width, + zoom: this.map.getZoom(), + // mapbox 中固定相机高度为 viewport 高度的 1.5 倍 + cameraHeight: 0, + }); + + // set coordinate system + if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) { + this.coordinateSystemService.setCoordinateSystem( + CoordinateSystem.LNGLAT_OFFSET, + ); + } else { + this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.LNGLAT); + } + + this.cameraChangedCallback(this.viewport); + }; + + private creatAmapContainer(id: string | HTMLDivElement) { + let $wrapper = id as HTMLDivElement; + if (typeof id === 'string') { + $wrapper = document.getElementById(id) as HTMLDivElement; + } + return $wrapper; + } + + private removeLogoControl(): void { + // @ts-ignore + const controls = this.map._controls as IControl[]; + const logoCtr = controls.find((ctr: IControl) => { + if (ctr.hasOwnProperty('_updateLogo')) { + return true; + } + }); + if (logoCtr) { + this.map.removeControl(logoCtr); + } + } + + private getMapStyle(name: MapStyle) { + if (typeof name !== 'string') { + return name; + } + return MapTheme[name] ? MapTheme[name] : name; + } +} diff --git a/packages/scene/src/index.ts b/packages/scene/src/index.ts index a6f7ec64ac..d066ba0739 100644 --- a/packages/scene/src/index.ts +++ b/packages/scene/src/index.ts @@ -57,7 +57,7 @@ class Scene private container: Container; public constructor(config: ISceneConfig) { - const { id, map } = config; + const { id, map, logoPosition, logoVisible } = config; // 创建场景容器 const sceneContainer = createSceneContainer(); @@ -93,14 +93,16 @@ class Scene // 初始化 scene this.sceneService.init(config); // TODO: 初始化组件 - this.addControl(new Logo()); + if (logoVisible) { + this.addControl(new Logo({ position: logoPosition })); + } } public getMapService(): IMapService { return this.mapService; } - public ExportMap2Png(): string { - return this.sceneService.ExportMap2Png(); + public exportPng(): string { + return this.sceneService.exportPng(); } public get map() { diff --git a/stories/Layers/components/Point.tsx b/stories/Layers/components/Point.tsx index 6cdba83922..75d2949200 100644 --- a/stories/Layers/components/Point.tsx +++ b/stories/Layers/components/Point.tsx @@ -23,7 +23,7 @@ export default class Point3D extends React.Component { center: [120.19382669582967, 30.258134], pitch: 0, style: 'dark', - zoom: 3, + zoom: 0, }), }); // scene.on('loaded', () => { From 3062ac794070eeff3e3779f9e75b24c43a867137 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 5 Feb 2020 10:13:59 +0800 Subject: [PATCH 3/6] fix: utils package.json --- docs/api/scene.en.md | 13 +++++++++++++ docs/api/scene.zh.md | 9 +++------ packages/layers/package.json | 4 ++-- packages/utils/package.json | 6 +++++- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/api/scene.en.md b/docs/api/scene.en.md index b4f55bd491..5bfd741c2a 100644 --- a/docs/api/scene.en.md +++ b/docs/api/scene.en.md @@ -76,6 +76,19 @@ const scene = new L7.Scene({ 需传入 dom 容器或者容器 id  {domObject || string} [必选] +### logoPosition + +L7 Logo 的显示位置 默认左下角 + +- bottomright +- topright +- bottomleft, +- topleft` + +### logoVisible + +是否显示 L7 的 Logo {boolean} true + ### zoom 地图初始显示级别 {number} (0-22) diff --git a/docs/api/scene.zh.md b/docs/api/scene.zh.md index 30675b7f2e..39dc8fea5b 100644 --- a/docs/api/scene.zh.md +++ b/docs/api/scene.zh.md @@ -78,19 +78,16 @@ const scene = new L7.Scene({ ### logoPosition -L7 Logo的显示位置 默认左下角 +L7 Logo 的显示位置 默认左下角 - bottomright - topright - bottomleft, - topleft` +### logoVisible -### logoVisible - -是否显示L7的Logo {boolean} true - - +是否显示 L7 的 Logo {boolean} true ### zoom diff --git a/packages/layers/package.json b/packages/layers/package.json index fbba05bfe7..9bf7f141ba 100644 --- a/packages/layers/package.json +++ b/packages/layers/package.json @@ -28,7 +28,6 @@ "@babel/runtime": "^7.7.7", "@mapbox/martini": "^0.1.0", "@turf/meta": "^6.0.2", - "@types/d3-color": "^1.2.2", "d3-array": "^2.3.1", "d3-color": "^1.4.0", "d3-scale": "^3.1.0", @@ -47,7 +46,8 @@ "@types/d3-scale": "^2.1.1", "@types/earcut": "^2.1.0", "@types/gl-matrix": "^2.4.5", - "@types/lodash": "^4.14.138" + "@types/lodash": "^4.14.138", + "@types/d3-color": "^1.2.2" }, "gitHead": "9fabb78790428d2025b89fb6146fc555cb1d987d", "publishConfig": { diff --git a/packages/utils/package.json b/packages/utils/package.json index e89bb224eb..0d7dd7b196 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,7 +23,11 @@ "license": "ISC", "dependencies": { "@babel/runtime": "^7.7.7", - "@turf/helpers": "^6.1.4" + "@turf/helpers": "^6.1.4", + "d3-color": "^1.4.0" + }, + "devDependencies": { + "@types/d3-color": "^1.2.2" }, "gitHead": "00d23ef70d9ec76eec26833fc50ac18fe584cf26", "publishConfig": { From 5b943721e3360a635b94eb232f8208f0d3b0059e Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 5 Feb 2020 10:35:22 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20point=20layer=20=E5=9C=B0=E5=9B=BE?= =?UTF-8?q?=E9=AB=98=E4=BA=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/shaders/picking.frag.glsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/shaders/picking.frag.glsl b/packages/core/src/shaders/picking.frag.glsl index 5b1864759e..0e58d7eb15 100644 --- a/packages/core/src/shaders/picking.frag.glsl +++ b/packages/core/src/shaders/picking.frag.glsl @@ -16,11 +16,11 @@ vec4 filterHighlightColor(vec4 color) { if (selected) { vec4 highLightColor = u_HighlightColor * COLOR_SCALE; - // float highLightAlpha = highLightColor.a; - // float highLightRatio = highLightAlpha / (highLightAlpha + color.a * (1.0 - highLightAlpha)); + float highLightAlpha = highLightColor.a; + float highLightRatio = highLightAlpha / (highLightAlpha + color.a * (1.0 - highLightAlpha)); - // vec3 resultRGB = mix(color.rgb, highLightColor.rgb, highLightRatio); - return highLightColor; + vec3 resultRGB = mix(color.rgb, highLightColor.rgb, highLightRatio); + return vec4(resultRGB, color.a); } else { return color; } From 4a9f0c466d23403d8a033c392a7deeba94270906 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 5 Feb 2020 14:56:20 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(control):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E7=BC=A9=E6=94=BE=E6=97=B6,=E5=9B=BE?= =?UTF-8?q?=E5=B1=82=E6=8E=A7=E4=BB=B6=E9=80=89=E4=B8=AD=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/component/src/control/layer.ts | 8 ++++++-- .../core/src/services/layer/ILayerService.ts | 3 +++ packages/layers/src/core/BaseLayer.ts | 16 ++++++++++++++++ stories/Components/components/Scale.tsx | 11 ++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/component/src/control/layer.ts b/packages/component/src/control/layer.ts index f3762a1ea1..2fdd7d8245 100644 --- a/packages/component/src/control/layer.ts +++ b/packages/component/src/control/layer.ts @@ -175,8 +175,12 @@ export default class Layers extends Control { for (let i = inputs.length - 1; i >= 0; i--) { input = inputs[i]; layer = this.layerService.getLayer(input.layerId); - if (layer) { - input.disabled = !layer.inited || !layer.isVisible(); + + if (layer && layer.inited) { + const minZoom = layer.getMinZoom(); + const maxZoom = layer.getMaxZoom(); + + input.disabled = zoom < minZoom || zoom > maxZoom; } } } diff --git a/packages/core/src/services/layer/ILayerService.ts b/packages/core/src/services/layer/ILayerService.ts index e9fda0a895..d1c29ce8cb 100644 --- a/packages/core/src/services/layer/ILayerService.ts +++ b/packages/core/src/services/layer/ILayerService.ts @@ -141,6 +141,9 @@ export interface ILayer { isVisible(): boolean; setMaxZoom(min: number): ILayer; setMinZoom(max: number): ILayer; + getMinZoom(): number; + getMaxZoom(): number; + get(name: string): number; setBlend(type: keyof typeof BlendType): void; // animate(field: string, option: any): ILayer; render(): ILayer; diff --git a/packages/layers/src/core/BaseLayer.ts b/packages/layers/src/core/BaseLayer.ts index 68b2c08588..bae6d16c98 100644 --- a/packages/layers/src/core/BaseLayer.ts +++ b/packages/layers/src/core/BaseLayer.ts @@ -629,6 +629,22 @@ export default class BaseLayer extends EventEmitter return this; } + public getMinZoom(): number { + const { minZoom } = this.getLayerConfig(); + return minZoom as number; + } + + public getMaxZoom(): number { + const { maxZoom } = this.getLayerConfig(); + return maxZoom as number; + } + + public get(name: string) { + const cfg = this.getLayerConfig(); + // @ts-ignore + return cfg[name]; + } + public setMaxZoom(maxZoom: number): ILayer { this.updateLayerConfig({ maxZoom, diff --git a/stories/Components/components/Scale.tsx b/stories/Components/components/Scale.tsx index 0ff5e67a15..7b3b5845f9 100644 --- a/stories/Components/components/Scale.tsx +++ b/stories/Components/components/Scale.tsx @@ -1,5 +1,5 @@ // @ts-ignore -import { PointLayer, PolygonLayer, Scale, Scene } from '@antv/l7'; +import { Layers, PointLayer, PolygonLayer, Scale, Scene } from '@antv/l7'; import { Mapbox } from '@antv/l7-maps'; import * as React from 'react'; @@ -70,7 +70,16 @@ export default class ScaleComponent extends React.Component { layer.setSelect(e.featureId); }); const scaleControl = new Scale(); + const layers = { + 点图层: pointLayer, + 面图层: layer, + }; + const layerControl = new Layers({ + overlayers: layers, + }); + scene.addControl(scaleControl); + scene.addControl(layerControl); } public render() { From d52fe42f2f2b316a135c05de87128e5ac205e6fa Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Wed, 5 Feb 2020 15:17:46 +0800 Subject: [PATCH 6/6] fix(stories): colorscales --- stories/Layers/lib/colorscales.ts | 183 ++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 stories/Layers/lib/colorscales.ts diff --git a/stories/Layers/lib/colorscales.ts b/stories/Layers/lib/colorscales.ts new file mode 100644 index 0000000000..0c62c15a96 --- /dev/null +++ b/stories/Layers/lib/colorscales.ts @@ -0,0 +1,183 @@ +// code from https://github.com/santilland/plotty/blob/master/src/colorscales.js +export const colorScales: { + [key: string]: any; +} = { + rainbow: { + colors: [ + '#96005A', + '#0000C8', + '#0019FF', + '#0098FF', + '#2CFF96', + '#97FF00', + '#FFEA00', + '#FF6F00', + '#FF0000', + ], + positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + }, + jet: { + colors: ['#000083', '#003CAA', '#05FFFF', '#FFFF00', '#FA0000', '#800000'], + positions: [0, 0.125, 0.375, 0.625, 0.875, 1], + }, + hsv: { + colors: [ + '#ff0000', + '#fdff02', + '#f7ff02', + '#00fc04', + '#00fc0a', + '#01f9ff', + '#0200fd', + '#0800fd', + '#ff00fb', + '#ff00f5', + '#ff0006', + ], + positions: [ + 0, + 0.169, + 0.173, + 0.337, + 0.341, + 0.506, + 0.671, + 0.675, + 0.839, + 0.843, + 1, + ], + }, + hot: { + colors: ['#000000', '#e60000', '#ffd200', '#ffffff'], + positions: [0, 0.3, 0.6, 1], + }, + cool: { + colors: ['#00ffff', '#ff00ff'], + positions: [0, 1], + }, + spring: { + colors: ['#ff00ff', '#ffff00'], + positions: [0, 1], + }, + summer: { + colors: ['#008066', '#ffff66'], + positions: [0, 1], + }, + autumn: { + colors: ['#ff0000', '#ffff00'], + positions: [0, 1], + }, + winter: { + colors: ['#0000ff', '#00ff80'], + positions: [0, 1], + }, + bone: { + colors: ['#000000', '#545474', '#a9c8c8', '#ffffff'], + positions: [0, 0.376, 0.753, 1], + }, + copper: { + colors: ['#000000', '#ffa066', '#ffc77f'], + positions: [0, 0.804, 1], + }, + greys: { + colors: ['#000000', '#ffffff'], + positions: [0, 1], + }, + yignbu: { + colors: [ + '#081d58', + '#253494', + '#225ea8', + '#1d91c0', + '#41b6c4', + '#7fcdbb', + '#c7e9b4', + '#edf8d9', + '#ffffd9', + ], + positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + }, + greens: { + colors: [ + '#00441b', + '#006d2c', + '#238b45', + '#41ab5d', + '#74c476', + '#a1d99b', + '#c7e9c0', + '#e5f5e0', + '#f7fcf5', + ], + positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + }, + wind: { + colors: [ + '#3288bd', + '#66c2a5', + '#abdda4', + '#e6f598', + '#fee08b', + '#fdae61', + '#f46d43', + '#d53e4f', + ], + positions: [0, 0.1, 0.2, 0.6, 0.7, 0.8, 0.9, 1], + }, + yiorrd: { + colors: [ + '#800026', + '#bd0026', + '#e31a1c', + '#fc4e2a', + '#fd8d3c', + '#feb24c', + '#fed976', + '#ffeda0', + '#ffffcc', + ], + positions: [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1], + }, + bluered: { + colors: ['#0000ff', '#ff0000'], + positions: [0, 1], + }, + rdbu: { + colors: ['#050aac', '#6a89f7', '#bebebe', '#dcaa84', '#e6915a', '#b20a1c'], + positions: [0, 0.35, 0.5, 0.6, 0.7, 1], + }, + picnic: { + colors: [ + '#0000ff', + '#3399ff', + '#66ccff', + '#99ccff', + '#ccccff', + '#ffffff', + '#ffccff', + '#ff99ff', + '#ff66cc', + '#ff6666', + '#ff0000', + ], + positions: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1], + }, + portland: { + colors: ['#0c3383', '#0a88ba', '#f2d338', '#f28f38', '#d91e1e'], + positions: [0, 0.25, 0.5, 0.75, 1], + }, + blackbody: { + colors: ['#000000', '#e60000', '#e6d200', '#ffffff', '#a0c8ff'], + positions: [0, 0.2, 0.4, 0.7, 1], + }, + earth: { + colors: ['#000082', '#00b4b4', '#28d228', '#e6e632', '#784614', '#ffffff'], + positions: [0, 0.1, 0.2, 0.4, 0.6, 1], + }, + electric: { + colors: ['#000000', '#1e0064', '#780064', '#a05a00', '#e6c800', '#fffadc'], + positions: [0, 0.15, 0.4, 0.6, 0.8, 1], + }, +}; +// export default colorScales;