diff --git a/examples/multilinestring/app.tsx b/examples/multilinestring/app.tsx index 8ad3849..ef8ab6c 100644 --- a/examples/multilinestring/app.tsx +++ b/examples/multilinestring/app.tsx @@ -24,9 +24,6 @@ const NAV_CONTROL_STYLE = { left: 10, }; -console.log("multipoint") -console.log(arrow); - function Root() { const onClick = (info) => { if (info.object) { diff --git a/examples/multipoint/app.tsx b/examples/multipoint/app.tsx index 8d7cd81..0c0cf70 100644 --- a/examples/multipoint/app.tsx +++ b/examples/multipoint/app.tsx @@ -24,9 +24,6 @@ const NAV_CONTROL_STYLE = { left: 10, }; -console.log("multipoint") -console.log(arrow); - function Root() { const onClick = (info) => { if (info.object) { diff --git a/examples/multipolygon/app.tsx b/examples/multipolygon/app.tsx index b640ce8..a22326c 100644 --- a/examples/multipolygon/app.tsx +++ b/examples/multipolygon/app.tsx @@ -54,9 +54,6 @@ function Root() { new GeoArrowSolidPolygonLayer({ id: "geoarrow-polygons", data: table, - filled: true, - wireframe: true, - extruded: true, getPolygon: table.getChild("geometry")!, getFillColor: table.getChild("pop_colors")!, pickable: true, diff --git a/examples/multipolygon/generate_data.py b/examples/multipolygon/generate_data.py index 4eb5e5d..459fd1a 100644 --- a/examples/multipolygon/generate_data.py +++ b/examples/multipolygon/generate_data.py @@ -27,10 +27,11 @@ def main(): min_pop = np.min(log_pop_est) max_pop = np.max(log_pop_est) normalized = (log_pop_est - min_pop) / (max_pop - min_pop) - colors = apply_continuous_cmap(normalized, PRGn_11) + colors = apply_continuous_cmap(normalized, PRGn_11, alpha=0.5) table = table.append_column( - "pop_colors", pa.FixedSizeListArray.from_arrays(colors.flatten("C"), 3) + "pop_colors", + pa.FixedSizeListArray.from_arrays(colors.flatten("C"), colors.shape[-1]), ) feather.write_feather( diff --git a/package-lock.json b/package-lock.json index e63b5a2..b1b13a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "devDependencies": { "@deck.gl/core": "^8.9.23", "@deck.gl/layers": "^8.9.23", + "@math.gl/polygon": "^3.6.2", "@rollup/plugin-terser": "^0.4.3", "@rollup/plugin-typescript": "^11.1.2", "apache-arrow": "^13.0.0", @@ -27,6 +28,7 @@ "peerDependencies": { "@deck.gl/core": "^8.9.23", "@deck.gl/layers": "^8.9.23", + "@math.gl/polygon": "^3.6.2", "apache-arrow": "^13.0.0" } }, @@ -82,6 +84,23 @@ "vite": "^4.0.0" } }, + "examples/multipolygon": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@geoarrow/deck.gl-layers": "../../", + "apache-arrow": "^13.0.0", + "deck.gl": "^8.9.23", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-map-gl": "^5.3.0" + }, + "devDependencies": { + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "vite": "^4.0.0" + } + }, "examples/point": { "name": "deckgl-example-geoarrow-point-layer", "version": "0.0.0", @@ -2198,6 +2217,10 @@ "resolved": "examples/multipoint", "link": true }, + "node_modules/deckgl-example-geoarrow-multipolygon-layer": { + "resolved": "examples/multipolygon", + "link": true + }, "node_modules/deckgl-example-geoarrow-point-layer": { "resolved": "examples/point", "link": true diff --git a/package.json b/package.json index 3181d1c..e944dc0 100644 --- a/package.json +++ b/package.json @@ -39,11 +39,13 @@ "peerDependencies": { "@deck.gl/core": "^8.9.23", "@deck.gl/layers": "^8.9.23", + "@math.gl/polygon": "^3.6.2", "apache-arrow": "^13.0.0" }, "devDependencies": { "@deck.gl/core": "^8.9.23", "@deck.gl/layers": "^8.9.23", + "@math.gl/polygon": "^3.6.2", "@rollup/plugin-terser": "^0.4.3", "@rollup/plugin-typescript": "^11.1.2", "apache-arrow": "^13.0.0", diff --git a/src/earcut.ts b/src/earcut.ts new file mode 100644 index 0000000..e4674e4 --- /dev/null +++ b/src/earcut.ts @@ -0,0 +1,60 @@ +import { earcut } from "@math.gl/polygon"; +import { PolygonData } from "./types"; +import { getLineStringChild, getPointChild, getPolygonChild } from "./utils"; + +export function earcutPolygonArray(data: PolygonData): Uint32Array { + const trianglesResults: number[][] = []; + let outputSize = 0; + for (let geomIndex = 0; geomIndex < data.length; geomIndex++) { + const triangles = earcutSinglePolygon(data, geomIndex); + trianglesResults.push(triangles); + outputSize += triangles.length; + } + + const outputArray = new Uint32Array(outputSize); + let idx = 0; + for (const triangles of trianglesResults) { + for (const value of triangles) { + outputArray[idx] = value; + idx += 1; + } + } + + return outputArray; +} + +function earcutSinglePolygon(data: PolygonData, geomIndex: number): number[] { + const geomOffsets = data.valueOffsets; + const rings = getPolygonChild(data); + const ringOffsets = rings.valueOffsets; + + const coords = getLineStringChild(rings); + const dim = coords.type.listSize; + const flatCoords = getPointChild(coords); + + const ringBegin = geomOffsets[geomIndex]; + const ringEnd = geomOffsets[geomIndex + 1]; + + const coordsBegin = ringOffsets[ringBegin]; + const coordsEnd = ringOffsets[ringEnd]; + + const slicedFlatCoords = flatCoords.values.subarray( + coordsBegin * dim, + coordsEnd * dim + ); + + const initialCoordIndex = ringOffsets[ringBegin]; + const holeIndices = []; + for (let holeRingIdx = ringBegin + 1; holeRingIdx < ringEnd; holeRingIdx++) { + holeIndices.push(ringOffsets[holeRingIdx] - initialCoordIndex); + } + // @ts-expect-error earcut typing is off. Should allow TypedArray but here + // only says number[] + const triangles = earcut(slicedFlatCoords, holeIndices, dim); + + for (let i = 0; i < triangles.length; i++) { + triangles[i] += initialCoordIndex; + } + + return triangles; +} diff --git a/src/path-layer.ts b/src/path-layer.ts index fc64504..9cab8fe 100644 --- a/src/path-layer.ts +++ b/src/path-layer.ts @@ -7,7 +7,6 @@ import { GetPickingInfoParams, Layer, LayersList, - PickingInfo, Unit, } from "@deck.gl/core/typed"; import { PathLayer } from "@deck.gl/layers/typed"; @@ -28,7 +27,11 @@ import { validateMultiLineStringType, validateVectorAccessors, } from "./utils.js"; -import { LineStringVector, MultiLineStringVector } from "./types.js"; +import { + GeoArrowPickingInfo, + LineStringVector, + MultiLineStringVector, +} from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255]; @@ -141,7 +144,10 @@ export class GeoArrowPathLayer< static defaultProps = defaultProps; static layerName = "GeoArrowPathLayer"; - getPickingInfo({ info, sourceLayer }: GetPickingInfoParams): PickingInfo { + getPickingInfo({ + info, + sourceLayer, + }: GetPickingInfoParams): GeoArrowPickingInfo { const { data: table } = this.props; // Geometry index as rendered diff --git a/src/scatterplot-layer.ts b/src/scatterplot-layer.ts index 8817327..f5c5569 100644 --- a/src/scatterplot-layer.ts +++ b/src/scatterplot-layer.ts @@ -7,7 +7,6 @@ import { GetPickingInfoParams, Layer, LayersList, - PickingInfo, Unit, } from "@deck.gl/core/typed"; import { ScatterplotLayer } from "@deck.gl/layers/typed"; diff --git a/src/solid-polygon-layer.ts b/src/solid-polygon-layer.ts index 1066789..a5718c2 100644 --- a/src/solid-polygon-layer.ts +++ b/src/solid-polygon-layer.ts @@ -8,7 +8,6 @@ import { Layer, LayersList, GetPickingInfoParams, - PickingInfo, } from "@deck.gl/core/typed"; import { SolidPolygonLayer } from "@deck.gl/layers/typed"; import type { SolidPolygonLayerProps } from "@deck.gl/layers/typed"; @@ -36,6 +35,7 @@ import { PolygonVector, } from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; +import { earcutPolygonArray } from "./earcut.js"; const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255]; @@ -257,6 +257,8 @@ export class GeoArrowSolidPolygonLayer< const resolvedRingOffsets = getPolygonResolvedOffsets(polygonData); + const earcutTriangles = earcutPolygonArray(polygonData); + const props: SolidPolygonLayerProps = { // used for picking purposes recordBatchIdx, @@ -278,6 +280,7 @@ export class GeoArrowSolidPolygonLayer< startIndices: resolvedRingOffsets, attributes: { getPolygon: { value: flatCoordinateArray, size: nDim }, + indices: { value: earcutTriangles, size: 1 }, }, }, }; @@ -358,6 +361,8 @@ export class GeoArrowSolidPolygonLayer< // const ringOffsets = ringData.valueOffsets; const flatCoordinateArray = coordData.values; + const earcutTriangles = earcutPolygonArray(polygonData); + // NOTE: we have two different uses of offsets. One is for _rendering_ // each polygon. The other is for mapping _accessor attributes_ from one // value per feature to one value per vertex. And for that we need to use @@ -399,19 +404,18 @@ export class GeoArrowSolidPolygonLayer< startIndices: resolvedPolygonToCoordOffsets, attributes: { getPolygon: { value: flatCoordinateArray, size: nDim }, - instancePickingColors: { - value: encodePickingColors( - resolvedMultiPolygonToCoordOffsets, - this.encodePickingColor - ), - size: 3, - }, + indices: { value: earcutTriangles, size: 1 }, + // instancePickingColors: { + // value: encodePickingColors( + // resolvedMultiPolygonToCoordOffsets, + // this.encodePickingColor + // ), + // size: 3, + // }, }, }, }; - console.log(resolvedMultiPolygonToCoordOffsets); - assignAccessor({ props, propName: "getElevation", @@ -464,6 +468,5 @@ function encodePickingColors( } } - console.log(pickingColors); return pickingColors; } diff --git a/src/types.ts b/src/types.ts index 112cabb..553d11e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,8 +3,8 @@ import * as arrow from "apache-arrow"; export type InterleavedCoord = arrow.FixedSizeList; export type SeparatedCoord = arrow.Struct<{ - x: arrow.Float; - y: arrow.Float; + x: arrow.Float64; + y: arrow.Float64; }>; // TODO: support separated coords export type Coord = InterleavedCoord; // | SeparatedCoord;