From f69791f434fd5457489ca0db5a9515cd95ddc634 Mon Sep 17 00:00:00 2001 From: yunji Date: Mon, 13 May 2024 20:17:16 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20mapbox=20=E4=B8=8B=E9=83=A8=E5=88=86?= =?UTF-8?q?=E9=9D=A2=E6=95=B0=E6=8D=AE=E5=9B=BE=E5=B1=82=E7=BB=98=E5=88=B6?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/demos/bugfix/index.ts | 1 + examples/demos/bugfix/muti-polygon.ts | 40 +++++++++++++++++++++++ packages/layers/src/core/triangulation.ts | 26 +++++++++++---- packages/utils/src/geo.ts | 5 +++ 4 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 examples/demos/bugfix/muti-polygon.ts diff --git a/examples/demos/bugfix/index.ts b/examples/demos/bugfix/index.ts index ebab728f40..a679410950 100644 --- a/examples/demos/bugfix/index.ts +++ b/examples/demos/bugfix/index.ts @@ -1,6 +1,7 @@ export { MapRender as color } from './color'; export { MapRender as data_shake } from './data-shake'; export { MapRender as event_legend } from './event_legend'; +export { MapRender as mutiPolygon } from './muti-polygon'; export { MapRender as polygon } from './polygon'; export { MapRender as remove_muti_layer } from './remove-muti-layer'; export { MapRender as size } from './size'; diff --git a/examples/demos/bugfix/muti-polygon.ts b/examples/demos/bugfix/muti-polygon.ts new file mode 100644 index 0000000000..f65c9b45cf --- /dev/null +++ b/examples/demos/bugfix/muti-polygon.ts @@ -0,0 +1,40 @@ +import { PolygonLayer, Scene } from '@antv/l7'; +import * as allMap from '@antv/l7-maps'; +import type { RenderDemoOptions } from '../../types'; + +export function MapRender(options: RenderDemoOptions) { + const scene = new Scene({ + id: 'map', + renderer: options.renderer, + map: new allMap[options.map]({ + style: 'dark', + center: [-96, 37.8], + zoom: 3, + }), + }); + + fetch( + 'https://npm.elemecdn.com/static-geo-atlas/geo-data/choropleth-data/country/100000_country_province.json', + ) + .then((res) => res.json()) + .then((data) => data.features.slice(4, 5)) + .then((geoData) => { + const layer = new PolygonLayer({ + autoFit: true, + }) + .source(geoData, { + parser: { + type: 'json', + geometry: 'geometry', + }, + }) + .color('rgb(22,199,255)') + .shape('fill') + .active(true) + .style({ + opacity: 0.5, + }); + + scene.addLayer(layer); + }); +} diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index 9720d271cc..06bad79e78 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -334,8 +334,24 @@ export function polygonTriangulation(feature: IEncodeFeature) { const { coordinates } = feature; const flattengeo = earcut.flatten(coordinates as number[][][]); const { vertices, dimensions, holes } = flattengeo; + + const positions = vertices.slice(); + const p: number[] = []; + for (let i = 0; i < vertices.length; i += 2) { + p[0] = vertices[i]; + p[1] = vertices[i + 1]; + + // earcut is a 2D triangulation algorithm, and handles 3D data as if it was projected onto the XY plane + const xy = lngLatToMeters(p, true, { enable: false, decimal: 1 }); + + positions[i] = xy[0]; + positions[i + 1] = xy[1]; + } + + const triangles = earcut(positions, holes, dimensions); + return { - indices: earcut(vertices, holes, dimensions), + indices: triangles, vertices, size: dimensions, }; @@ -343,14 +359,12 @@ export function polygonTriangulation(feature: IEncodeFeature) { // 构建几何图形(带有中心点和大小) export function polygonTriangulationWithCenter(feature: IEncodeFeature) { - const { coordinates } = feature; - const flattengeo = earcut.flatten(coordinates as number[][][]); - const { vertices, dimensions, holes } = flattengeo; + const { indices, vertices, size } = polygonTriangulation(feature); return { - indices: earcut(vertices, holes, dimensions), + indices: indices, vertices: getVerticesWithCenter(vertices), - size: dimensions + 4, + size: size + 4, }; } diff --git a/packages/utils/src/geo.ts b/packages/utils/src/geo.ts index eac2b8ec57..50d8d00010 100644 --- a/packages/utils/src/geo.ts +++ b/packages/utils/src/geo.ts @@ -65,6 +65,11 @@ function transform(item: any[], cb: (item: any[]) => any): any { return cb(item); } export function lngLatToMeters(lnglat: Point): Point; +export function lngLatToMeters( + lnglat: Point, + validate?: boolean, + accuracy?: { enable: boolean; decimal: number }, +): Point; export function lngLatToMeters( lnglat: Point, validate: boolean = true, From 570fcc9f1b1275acce70524475452dca4fb531e9 Mon Sep 17 00:00:00 2001 From: yunji Date: Mon, 13 May 2024 20:29:01 +0800 Subject: [PATCH 2/3] chore: add changeset --- .changeset/tiny-seas-camp.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tiny-seas-camp.md diff --git a/.changeset/tiny-seas-camp.md b/.changeset/tiny-seas-camp.md new file mode 100644 index 0000000000..c8ab41a673 --- /dev/null +++ b/.changeset/tiny-seas-camp.md @@ -0,0 +1,5 @@ +--- +'@antv/l7-layers': patch +--- + +fix: mapbox 下部分面数据图层绘制异常 From dd29d0191a9b389323fad9dd89726b5cb729a2ad Mon Sep 17 00:00:00 2001 From: yunji Date: Tue, 14 May 2024 20:34:49 +0800 Subject: [PATCH 3/3] fix: 3d extrude polygon layer --- examples/demos/bugfix/muti-polygon.ts | 2 ++ packages/layers/src/core/shape/extrude.ts | 12 +++++-- packages/layers/src/core/triangulation.ts | 20 +++-------- packages/layers/src/core/utils.ts | 42 +++++++++++++++++++++++ 4 files changed, 57 insertions(+), 19 deletions(-) diff --git a/examples/demos/bugfix/muti-polygon.ts b/examples/demos/bugfix/muti-polygon.ts index f65c9b45cf..6d2f0b9366 100644 --- a/examples/demos/bugfix/muti-polygon.ts +++ b/examples/demos/bugfix/muti-polygon.ts @@ -30,6 +30,8 @@ export function MapRender(options: RenderDemoOptions) { }) .color('rgb(22,199,255)') .shape('fill') + // .shape('extrude') + // .size(1200 * 100) .active(true) .style({ opacity: 0.5, diff --git a/packages/layers/src/core/shape/extrude.ts b/packages/layers/src/core/shape/extrude.ts index d5edd94a07..bd15dad95c 100644 --- a/packages/layers/src/core/shape/extrude.ts +++ b/packages/layers/src/core/shape/extrude.ts @@ -1,7 +1,9 @@ import { lngLatToMeters } from '@antv/l7-utils'; import earcut from 'earcut'; import { vec3 } from 'gl-matrix'; +import { getPolygonSurfaceIndices } from '../utils'; import type { IPath } from './Path'; + export interface IExtrudeGeomety { positions: number[]; index: number[]; @@ -62,6 +64,7 @@ export default function extrudePolygon(path: IPath[]): IExtrudeGeomety { index: indexArray, }; } + export function fillPolygon(points: IPath[]) { const flattengeo = earcut.flatten(points); const triangles = earcut(flattengeo.vertices, flattengeo.holes, flattengeo.dimensions); @@ -82,7 +85,7 @@ export function extrude_PolygonNormal( } const n = path[0].length; const flattengeo = earcut.flatten(path); - const { vertices, dimensions } = flattengeo; + const { vertices, dimensions, holes } = flattengeo; const positions = []; const indexArray = []; const normals = []; @@ -97,8 +100,10 @@ export function extrude_PolygonNormal( ); normals.push(0, 0, 1); } - const triangles = earcut(flattengeo.vertices, flattengeo.holes, flattengeo.dimensions); - indexArray.push(...triangles); + + const indices = getPolygonSurfaceIndices(vertices, holes, dimensions, needFlat); + indexArray.push(...indices); + // 设置侧面 for (let i = 0; i < n; i++) { const prePoint = flattengeo.vertices.slice(i * dimensions, (i + 1) * dimensions); @@ -145,6 +150,7 @@ export function extrude_PolygonNormal( normals, }; } + function computeVertexNormals( p1: [number, number, number], p2: [number, number, number], diff --git a/packages/layers/src/core/triangulation.ts b/packages/layers/src/core/triangulation.ts index 06bad79e78..3204f88f19 100644 --- a/packages/layers/src/core/triangulation.ts +++ b/packages/layers/src/core/triangulation.ts @@ -17,10 +17,11 @@ import { } from '../earth/utils'; import ExtrudePolyline from '../utils/extrude_polyline'; import type { IPosition, ShapeType2D, ShapeType3D } from './shape/Path'; +import { geometryShape } from './shape/Path'; import type { IExtrudeGeomety } from './shape/extrude'; import extrudePolygon, { extrude_PolygonNormal, fillPolygon } from './shape/extrude'; +import { getPolygonSurfaceIndices } from './utils'; -import { geometryShape } from './shape/Path'; type IShape = ShapeType2D & ShapeType3D; interface IGeometryCache { [key: string]: IExtrudeGeomety; @@ -335,23 +336,10 @@ export function polygonTriangulation(feature: IEncodeFeature) { const flattengeo = earcut.flatten(coordinates as number[][][]); const { vertices, dimensions, holes } = flattengeo; - const positions = vertices.slice(); - const p: number[] = []; - for (let i = 0; i < vertices.length; i += 2) { - p[0] = vertices[i]; - p[1] = vertices[i + 1]; - - // earcut is a 2D triangulation algorithm, and handles 3D data as if it was projected onto the XY plane - const xy = lngLatToMeters(p, true, { enable: false, decimal: 1 }); - - positions[i] = xy[0]; - positions[i + 1] = xy[1]; - } - - const triangles = earcut(positions, holes, dimensions); + const indices = getPolygonSurfaceIndices(vertices, holes, dimensions); return { - indices: triangles, + indices, vertices, size: dimensions, }; diff --git a/packages/layers/src/core/utils.ts b/packages/layers/src/core/utils.ts index 3c9e8edf8c..099e03e2a7 100644 --- a/packages/layers/src/core/utils.ts +++ b/packages/layers/src/core/utils.ts @@ -1,3 +1,45 @@ +import { lngLatToMeters } from '@antv/l7-utils'; +import earcut from 'earcut'; + export function MultipleOfFourNumber(num: number) { return Math.max(Math.ceil(num / 4) * 4, 4); } + +/** + * Get vertex indices for drawing polygon mesh (triangulation) + */ +export function getPolygonSurfaceIndices( + positions: number[], + holeIndices: number[], + positionSize: number, + preproject = true, +) { + const is3d = positionSize === 3; + + if (preproject) { + positions = positions.slice(); + const p: number[] = []; + for (let i = 0; i < positions.length; i += positionSize) { + p[0] = positions[i]; + p[1] = positions[i + 1]; + + if (is3d) { + p[2] = positions[i + 2]; + } + + // earcut is a 2D triangulation algorithm, and handles 3D data as if it was projected onto the XY plane + const xy = lngLatToMeters(p, true, { enable: false, decimal: 1 }); + + positions[i] = xy[0]; + positions[i + 1] = xy[1]; + + if (is3d) { + positions[i + 2] = xy[2]; + } + } + } + + const indices = earcut(positions, holeIndices, positionSize); + + return indices; +}