From 6f3d043a7b4822478d48dc14ed72f83227548557 Mon Sep 17 00:00:00 2001 From: "@thinkinggis" Date: Tue, 29 Nov 2022 23:35:27 +0800 Subject: [PATCH] Feat map (#1515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 多地图模式 * fix: getcustomdata 数据为空 * fix: map 支持 threejs 图层 * fix: lint error * chore: add vercel-build * chore: add vercel.json * chore: add vercel.json --- dev-demos/bugs/point/demos/image.tsx | 87 +++++++++++ dev-demos/bugs/point/image.md | 2 + dev-demos/bugs/scene/demos/multiMap.tsx | 66 +++++++++ dev-demos/bugs/scene/multiMap.md | 3 + dev-demos/features/threejs/amap2.md | 29 +++- dev-demos/features/threejs/map.md | 139 ++++++++++++++++++ dev-demos/features/tile/raster/satellite.tsx | 2 +- package.json | 1 + packages/layers/src/point/index.ts | 3 + packages/layers/src/point/models/image.ts | 45 +++--- packages/layers/src/point/models/text.ts | 5 +- packages/map/src/geo/mercator.ts | 2 + packages/map/src/index.ts | 1 + packages/maps/src/amap/map.ts | 3 +- packages/maps/src/amap2/map.ts | 3 +- packages/maps/src/map/map.ts | 63 +++++++- .../source/src/utils/tile/getCustomData.ts | 4 +- packages/three/src/core/threeRenderService.ts | 2 + vercel.json | 16 ++ 19 files changed, 437 insertions(+), 39 deletions(-) create mode 100644 dev-demos/bugs/point/demos/image.tsx create mode 100644 dev-demos/bugs/point/image.md create mode 100644 dev-demos/bugs/scene/demos/multiMap.tsx create mode 100644 dev-demos/bugs/scene/multiMap.md create mode 100644 dev-demos/features/threejs/map.md create mode 100644 vercel.json diff --git a/dev-demos/bugs/point/demos/image.tsx b/dev-demos/bugs/point/demos/image.tsx new file mode 100644 index 0000000000..3c574613fe --- /dev/null +++ b/dev-demos/bugs/point/demos/image.tsx @@ -0,0 +1,87 @@ +// @ts-ignore +import { PointLayer, Scene,Popup } from '@antv/l7'; +// @ts-ignore +import { GaodeMap, Mapbox } from '@antv/l7-maps'; +import React, { useEffect } from 'react'; + +export default () => { + // @ts-ignore + useEffect( async () => { + const response = await fetch( + 'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json', + ); + const scene = new Scene({ + id: 'map', + map: new GaodeMap({ + center: [121.4, 31.258134], + zoom: 12, + pitch: 0, + style: 'normal', + doubleClickZoom:false, + }), + }); + scene.addImage( + '00', + 'https://gw.alipayobjects.com/mdn/rms_fcd5b3/afts/img/A*g8cUQ7pPT9YAAAAAAAAAAAAAARQnAQ', + ); + scene.addImage( + '01', + 'https://gw.alipayobjects.com/mdn/rms_fcd5b3/afts/img/A*LTcXTLBM7kYAAAAAAAAAAAAAARQnAQ', + ); + scene.addImage( + '02', + 'https://gw.alipayobjects.com/zos/bmw-prod/904d047a-16a5-461b-a921-98fa537fc04a.svg', + ); + const data = await response.json(); + const newData = data.map((item: any) => { + item.type = ['00', '01', '02'][Math.floor(Math.random() * 3)]; + return item; + }); + const imageLayer = new PointLayer({ + autoFit:false + }) + .source(newData, { + parser: { + type: 'json', + x: 'longitude', + y: 'latitude', + }, + }) + .shape('type', (v: any) => { + return v; + }) + .active(false) + .size(20); + scene.addLayer(imageLayer); + setInterval(()=>{ + scene.addImage( + '00', + 'https://gw.alipayobjects.com/mdn/rms_fcd5b3/afts/img/A*g8cUQ7pPT9YAAAAAAAAAAAAAARQnAQ', + ); + scene.addImage( + '01', + 'https://gw.alipayobjects.com/mdn/rms_fcd5b3/afts/img/A*LTcXTLBM7kYAAAAAAAAAAAAAARQnAQ', + ); + scene.addImage( + '02', + 'https://gw.alipayobjects.com/zos/bmw-prod/904d047a-16a5-461b-a921-98fa537fc04a.svg', + ); + const data = newData.slice(0,5+ Math.round(Math.random()*10)); + imageLayer.setData(data) + console.log(imageLayer) + console.log('更新') + },3000) + + + }, []); + return ( +
+ ); + }; + \ No newline at end of file diff --git a/dev-demos/bugs/point/image.md b/dev-demos/bugs/point/image.md new file mode 100644 index 0000000000..1ea5c3d48b --- /dev/null +++ b/dev-demos/bugs/point/image.md @@ -0,0 +1,2 @@ +### Point - image + \ No newline at end of file diff --git a/dev-demos/bugs/scene/demos/multiMap.tsx b/dev-demos/bugs/scene/demos/multiMap.tsx new file mode 100644 index 0000000000..2751099718 --- /dev/null +++ b/dev-demos/bugs/scene/demos/multiMap.tsx @@ -0,0 +1,66 @@ +// @ts-ignore +import { + LineLayer, + Scene, + Source, + lineAtOffset, + lineAtOffsetAsyc, + PointLayer, + // @ts-ignore + } from '@antv/l7'; + // @ts-ignore + import { GaodeMapV1 } from '@antv/l7-maps'; + import React, { useEffect } from 'react'; + + export default () => { + useEffect(() => { + const scene = new Scene({ + id: 'map', + map: new GaodeMapV1({ + style: 'light', + center: [-96, 37.8], + zoom: 3, + }), + }); + scene.on('loaded', () => { + + }); + + const scene2 = new Scene({ + id: 'map2', + map: new GaodeMapV1({ + style: 'dark', + center: [-96, 37.8], + zoom: 3, + }), + }); + scene2.on('loaded', () => { + + }); + + + }, []); + return ( + <> +
+
+ + ); + }; + \ No newline at end of file diff --git a/dev-demos/bugs/scene/multiMap.md b/dev-demos/bugs/scene/multiMap.md new file mode 100644 index 0000000000..1ebfa3a9b2 --- /dev/null +++ b/dev-demos/bugs/scene/multiMap.md @@ -0,0 +1,3 @@ +### multiMap 地图 + + diff --git a/dev-demos/features/threejs/amap2.md b/dev-demos/features/threejs/amap2.md index 2636b9a1ad..3573a94a71 100644 --- a/dev-demos/features/threejs/amap2.md +++ b/dev-demos/features/threejs/amap2.md @@ -1,7 +1,7 @@ ### threejs - amap2 ```tsx -import { Scene } from '@antv/l7'; -import { GaodeMap } from '@antv/l7-maps'; +import { Scene,RasterLayer } from '@antv/l7'; +import { GaodeMap, } from '@antv/l7-maps'; import React, { useEffect } from 'react'; import { ThreeLayer, ThreeRender } from '@antv/l7-three'; import * as THREE from 'three'; @@ -20,6 +20,31 @@ export default () => { }), }); scene.registerRenderService(ThreeRender); + const url1 = + 'https://tiles{1-3}.geovisearth.com/base/v1/ter/{z}/{x}/{y}?format=webp&tmsIds=w&token=b2a0cfc132cd60b61391b9dd63c15711eadb9b38a9943e3f98160d5710aef788'; + const url2 = + 'https://tiles{1-3}.geovisearth.com/base/v1/cat/{z}/{x}/{y}?format=png&tmsIds=w&token=b2a0cfc132cd60b61391b9dd63c15711eadb9b38a9943e3f98160d5710aef788'; + const layer1 = new RasterLayer({ + zIndex: 1, + }).source(url1, { + parser: { + type: 'rasterTile', + tileSize: 256, + zoomOffset: 0, + }, + }); + + const layer2 = new RasterLayer({ + zIndex: 1, + }).source(url2, { + parser: { + type: 'rasterTile', + tileSize: 256, + zoomOffset: 0, + }, + }); + scene.addLayer(layer1); + scene.addLayer(layer2); scene.on('loaded', () => { const threeJSLayer = new ThreeLayer({ enableMultiPassRenderer: false, diff --git a/dev-demos/features/threejs/map.md b/dev-demos/features/threejs/map.md new file mode 100644 index 0000000000..3912909ecb --- /dev/null +++ b/dev-demos/features/threejs/map.md @@ -0,0 +1,139 @@ +### threejs - Map + +```tsx +import { Scene, RasterLayer } from '@antv/l7'; +import { Map } from '@antv/l7-maps'; +import React, { useEffect } from 'react'; +import { ThreeLayer, ThreeRender } from '@antv/l7-three'; +import * as THREE from 'three'; +import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { animate, easeInOut } from 'popmotion'; + +export default () => { + useEffect(() => { + const scene = new Scene({ + id: 'map', + map: new Map({ + center: [111.4453125, 32.84267363195431], + pitch: 45, + rotation: 30, + zoom: 12, + token: + 'pk.eyJ1IjoiMTg5Njk5NDg2MTkiLCJhIjoiY2w3dHk3dnN4MDYzaDNycDkyMDl2bzh6NiJ9.YIrG9kwUpayLj01f6W23Gw', + }), + }); + + scene.on('loaded', () => { + scene.registerRenderService(ThreeRender); + const url1 = + 'https://tiles{1-3}.geovisearth.com/base/v1/ter/{z}/{x}/{y}?format=webp&tmsIds=w&token=b2a0cfc132cd60b61391b9dd63c15711eadb9b38a9943e3f98160d5710aef788'; + const url2 = + 'https://tiles{1-3}.geovisearth.com/base/v1/cat/{z}/{x}/{y}?format=png&tmsIds=w&token=b2a0cfc132cd60b61391b9dd63c15711eadb9b38a9943e3f98160d5710aef788'; + const layer1 = new RasterLayer({ + zIndex: 1, + }).source(url1, { + parser: { + type: 'rasterTile', + tileSize: 256, + zoomOffset: 0, + }, + }); + + const layer2 = new RasterLayer({ + zIndex: 1, + }).source(url2, { + parser: { + type: 'rasterTile', + tileSize: 256, + zoomOffset: 0, + }, + }); + scene.addLayer(layer1); + scene.addLayer(layer2); + const threeJSLayer = new ThreeLayer({ + enableMultiPassRenderer: false, + onAddMeshes: (threeScene, layer) => { + threeScene.add(new THREE.AmbientLight(0xffffff)); + const sunlight = new THREE.DirectionalLight(0xffffff, 0.25); + sunlight.position.set(0, 80000000, 100000000); + sunlight.matrixWorldNeedsUpdate = true; + threeScene.add(sunlight); + + const center = scene.getCenter(); + + const cubeGeometry = new THREE.BoxBufferGeometry(10000, 10000, 10000); + const cubeMaterial = new THREE.MeshNormalMaterial({ + side: THREE.DoubleSide, + }); + const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); + layer.setObjectLngLat(cube, [center.lng + 0.05, center.lat], 0); + threeScene.add(cube); + + // 使用 Three.js glTFLoader 加载模型 + const loader = new GLTFLoader(); + loader.load( + 'https://gw.alipayobjects.com/os/bmw-prod/3ca0a546-92d8-4ba0-a89c-017c218d5bea.gltf', + (gltf) => { + const gltfScene = gltf.scene; + setDouble(gltfScene); + layer.adjustMeshToMap(gltfScene); + // gltfScene.scale.set(1000, 1000, 1000) + layer.setMeshScale(gltfScene, 100, 100, 100); + + layer.setObjectLngLat(gltfScene, [center.lng, center.lat], 0); + + const animations = gltf.animations; + if (animations && animations.length) { + const mixer = new THREE.AnimationMixer(gltfScene); + + const animation = animations[2]; + + const action = mixer.clipAction(animation); + + action.play(); + layer.addAnimateMixer(mixer); + } + let t = 0; + setInterval(() => { + t += 0.01; + layer.setObjectLngLat( + gltfScene, + [center.lng, center.lat + Math.sin(t) * 0.1], + 0, + ); + }, 16); + + // 向场景中添加模型 + threeScene.add(gltfScene); + // 重绘图层 + layer.render(); + }, + ); + }, + }).animate(true); + scene.addLayer(threeJSLayer); + }); + + function setDouble(object) { + if ( + object.children && + object.children.length && + object.children.length > 0 + ) { + object.children.map((child) => setDouble(child)); + } else if (object.material) { + object.material.side = THREE.DoubleSide; + } + } + }, []); + return ( +
+ ); +}; +``` diff --git a/dev-demos/features/tile/raster/satellite.tsx b/dev-demos/features/tile/raster/satellite.tsx index c0b63f6c78..47a0aa766c 100644 --- a/dev-demos/features/tile/raster/satellite.tsx +++ b/dev-demos/features/tile/raster/satellite.tsx @@ -16,7 +16,7 @@ export default () => { center: [127.471855, 46.509622], // 绥化市-北林区 pitch: 0, style: 'blank', - zoom: 10, + zoom: 7, }), }); diff --git a/package.json b/package.json index 82c41f199d..bd9ba7647d 100644 --- a/package.json +++ b/package.json @@ -179,6 +179,7 @@ "lint:css": "stylelint 'packages/**/src/**/*.js{,x}'", "lint": "run-p -c lint:*", "commit": "git-cz", + "vercel-build": "yarn global add node-gyp", "version": "lerna version --force-publish --conventional-commits --exact --no-changelog", "version:prerelease": "lerna version --force-publish --exact --conventional-prerelease", "prerelease": "yarn build && yarn bundle", diff --git a/packages/layers/src/point/index.ts b/packages/layers/src/point/index.ts index b9b737cb98..7daa8d7e84 100644 --- a/packages/layers/src/point/index.ts +++ b/packages/layers/src/point/index.ts @@ -18,6 +18,9 @@ export default class PointLayer extends BaseLayer { public async buildModels() { const modelType = this.getModelType(); + if (this.layerModel) { + this.layerModel.clearModels(); + } this.layerModel = new PointModels[modelType](this); await this.initLayerModels(); } diff --git a/packages/layers/src/point/models/image.ts b/packages/layers/src/point/models/image.ts index dbf5d1b878..572a084976 100644 --- a/packages/layers/src/point/models/image.ts +++ b/packages/layers/src/point/models/image.ts @@ -15,7 +15,6 @@ import pointImageFrag from '../shaders/image_frag.glsl'; import pointImageVert from '../shaders/image_vert.glsl'; export default class ImageModel extends BaseModel { private texture: ITexture2D; - public getUninforms(): IModelUniform { const { opacity = 1, @@ -49,21 +48,21 @@ export default class ImageModel extends BaseModel { this.dataTexture = this.cellLength > 0 && data.length > 0 ? this.createTexture2D({ - flipY: true, - data, - format: gl.LUMINANCE, - type: gl.FLOAT, - width, - height, - }) + flipY: true, + data, + format: gl.LUMINANCE, + type: gl.FLOAT, + width, + height, + }) : this.createTexture2D({ - flipY: true, - data: [1], - format: gl.LUMINANCE, - type: gl.FLOAT, - width: 1, - height: 1, - }); + flipY: true, + data: [1], + format: gl.LUMINANCE, + type: gl.FLOAT, + width: 1, + height: 1, + }); } return { u_raisingHeight: Number(raisingHeight), @@ -81,10 +80,8 @@ export default class ImageModel extends BaseModel { }; } - public async initModels():Promise { - this.iconService.off('imageUpdate', this.updateTexture); + public async initModels(): Promise { this.iconService.on('imageUpdate', this.updateTexture); - // this.registerBuiltinAttributes(); this.updateTexture(); return await this.buildModels(); @@ -96,13 +93,13 @@ export default class ImageModel extends BaseModel { this.iconService.off('imageUpdate', this.updateTexture); } - public async buildModels():Promise { + public async buildModels(): Promise { const { mask = false, maskInside = true, } = this.layer.getLayerConfig() as IPointLayerStyleOptions; - const model = await this.layer + const model = await this.layer .buildLayerModel({ moduleName: 'pointImage', vertexShader: pointImageVert, @@ -114,8 +111,8 @@ export default class ImageModel extends BaseModel { stencil: getMask(mask, maskInside), }); - return [model] - + return [model] + } protected registerBuiltinAttributes() { // point layer size; @@ -176,10 +173,10 @@ export default class ImageModel extends BaseModel { }); // 更新完纹理后在更新的图层的时候需要更新所有的图层 // this.layer.layerModelNeedUpdate = true; - setTimeout(()=>{ // 延迟渲染 + setTimeout(() => { // 延迟渲染 this.layerService.throttleRenderLayers(); }) - + return; } this.texture = createTexture2D({ diff --git a/packages/layers/src/point/models/text.ts b/packages/layers/src/point/models/text.ts index d68ff27e86..bf8c44dc7b 100644 --- a/packages/layers/src/point/models/text.ts +++ b/packages/layers/src/point/models/text.ts @@ -180,10 +180,7 @@ export default class TextModel extends BaseModel { public async initModels():Promise { // 绑定事件 - if(!this.layer.inited) { - this.bindEvent(); - } - + this.bindEvent(); this.extent = this.textExtent(); const { textAnchor = 'center', diff --git a/packages/map/src/geo/mercator.ts b/packages/map/src/geo/mercator.ts index 52dff815b5..4504e4c9e0 100644 --- a/packages/map/src/geo/mercator.ts +++ b/packages/map/src/geo/mercator.ts @@ -89,3 +89,5 @@ export default class MercatorCoordinate { return (1 / earthCircumfrence) * mercatorScale(latFromMercatorY(this.y)); } } + +export { MercatorCoordinate }; diff --git a/packages/map/src/index.ts b/packages/map/src/index.ts index 72d44e2709..353b688df2 100644 --- a/packages/map/src/index.ts +++ b/packages/map/src/index.ts @@ -1,3 +1,4 @@ export * from './map'; export * from './earthmap'; +export * from './geo/mercator'; export * from './interface'; \ No newline at end of file diff --git a/packages/maps/src/amap/map.ts b/packages/maps/src/amap/map.ts index 24f71ef595..d88a915f92 100644 --- a/packages/maps/src/amap/map.ts +++ b/packages/maps/src/amap/map.ts @@ -12,10 +12,9 @@ import { mat4, vec3 } from 'gl-matrix'; import { injectable } from 'inversify'; import 'reflect-metadata'; import { IAMapEvent, IAMapInstance } from '../../typings/index'; +import AMapBaseService from '../utils/amap/AMapBaseService'; import AMapLoader from '../utils/amaploader'; import { Version } from '../version'; - -import AMapBaseService from '../utils/amap/AMapBaseService'; import Viewport from './Viewport'; // @ts-ignore window.forceWebGL = true; diff --git a/packages/maps/src/amap2/map.ts b/packages/maps/src/amap2/map.ts index 3d434beee4..a3bfcf7538 100644 --- a/packages/maps/src/amap2/map.ts +++ b/packages/maps/src/amap2/map.ts @@ -2,7 +2,7 @@ /** * AMapService */ -import AMapLoader from '../utils/amaploader'; +import AMapLoader from '@amap/amap-jsapi-loader'; import { Bounds, @@ -246,6 +246,7 @@ export default class AMapService extends AMapBaseService { this.viewport = new Viewport(); if (!(window.AMap || mapInstance)) { plugin.push('Map3D'); + // if (AMapLoader.status.AMap === 'notload') { await AMapLoader.load({ key: token, // 申请好的Web端开发者Key,首次调用 load 时必填 version: AMAP_VERSION, // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15 diff --git a/packages/maps/src/map/map.ts b/packages/maps/src/map/map.ts index dbb4731888..f76853751e 100644 --- a/packages/maps/src/map/map.ts +++ b/packages/maps/src/map/map.ts @@ -3,8 +3,9 @@ * MapboxService */ import { CoordinateSystem, IMercator } from '@antv/l7-core'; -import { Map } from '@antv/l7-map'; +import { Map, MercatorCoordinate } from '@antv/l7-map'; import { $window } from '@antv/l7-utils'; +import { mat4, vec3 } from 'gl-matrix'; import { injectable } from 'inversify'; import 'reflect-metadata'; import BaseMapService from '../utils/BaseMapService'; @@ -18,15 +19,69 @@ const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; @injectable() export default class DefaultMapService extends BaseMapService { public version: string = Version.DEFUALT; + /** + * 将经纬度转成墨卡托坐标 + * @param lnglat + * @returns + */ + public lngLatToCoord( + lnglat: [number, number], + origin: IMercator = { x: 0, y: 0, z: 0 }, + ) { + // @ts-ignore + const { x, y } = this.lngLatToMercator(lnglat, 0); + return [x - origin.x, y - origin.y] as [number, number]; + } + public lngLatToMercator( lnglat: [number, number], altitude: number, ): IMercator { - throw new Error('Method not implemented.'); + const { + x = 0, + y = 0, + z = 0, + } = MercatorCoordinate.fromLngLat(lnglat, altitude); + return { x, y, z }; } - public getModelMatrix(): number[] { - 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[] { + const modelAsMercatorCoordinate = MercatorCoordinate.fromLngLat( + lnglat, + altitude, + ); + // @ts-ignore + const meters = modelAsMercatorCoordinate.meterInMercatorCoordinateUnits(); + const modelMatrix = mat4.create(); + + mat4.translate( + modelMatrix, + modelMatrix, + vec3.fromValues( + modelAsMercatorCoordinate.x - origin.x, + modelAsMercatorCoordinate.y - origin.y, + modelAsMercatorCoordinate.z || 0 - origin.z, + ), + ); + + mat4.scale( + modelMatrix, + modelMatrix, + vec3.fromValues(meters * scale[0], -meters * scale[1], meters * 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 viewport: Viewport; public async init(): Promise { diff --git a/packages/source/src/utils/tile/getCustomData.ts b/packages/source/src/utils/tile/getCustomData.ts index f09f78ed12..16987a72b0 100644 --- a/packages/source/src/utils/tile/getCustomData.ts +++ b/packages/source/src/utils/tile/getCustomData.ts @@ -17,8 +17,10 @@ export const getCustomData = async ( y: tile.y, z: tile.z }, (err, data) => { - if(err){ + + if(err || data.length ===0){ reject(err) + return; } if (data) { processRasterData([{data,bands:[0]}], rasterFormat, operation, (err: any, img: any) => { diff --git a/packages/three/src/core/threeRenderService.ts b/packages/three/src/core/threeRenderService.ts index 33e8b6ca93..ac86ba7fac 100644 --- a/packages/three/src/core/threeRenderService.ts +++ b/packages/three/src/core/threeRenderService.ts @@ -82,6 +82,8 @@ export class ThreeRenderService implements IThreeRenderService { return this.AMap2Camera(); case 'MAPBOX': return this.mapboxCamera(); + case 'DEFAULTMAP': + return this.mapboxCamera(); default: return this.AMapCamera(); } diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000000..d42c38512c --- /dev/null +++ b/vercel.json @@ -0,0 +1,16 @@ +{ + "version": 2, + "builds": [ + { + "src": "package.json", + "use": "@vercel/node" + }, + { + "src": "nuxt.config.js", + "use": "@nuxtjs/vercel-builder", + "config": { + "serverFiles": ["server-middleware/**"] + } + } + ] + } \ No newline at end of file