From 63b5bc625a60546b59d6f1184fb7f338eb7218d2 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 11:42:08 -0500 Subject: [PATCH 1/9] ColumnLayer --- src/column-layer.ts | 385 ++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 2 + 2 files changed, 387 insertions(+) create mode 100644 src/column-layer.ts diff --git a/src/column-layer.ts b/src/column-layer.ts new file mode 100644 index 0000000..38ceb0f --- /dev/null +++ b/src/column-layer.ts @@ -0,0 +1,385 @@ +import { + CompositeLayer, + CompositeLayerProps, + DefaultProps, + GetPickingInfoParams, + Layer, + LayersList, + Material, + Position, + Unit, +} from "@deck.gl/core/typed"; +import { ColumnLayer } from "@deck.gl/layers/typed"; +import type { ColumnLayerProps } from "@deck.gl/layers/typed"; +import * as arrow from "apache-arrow"; +import { + assignAccessor, + getGeometryVector, + getMultiPointChild, + getPointChild, + invertOffsets, + isMultiPointVector, + isPointVector, + validateColorVector, + validateMultiPointType, + validatePointType, + validateVectorAccessors, +} from "./utils.js"; +import { + ColorAccessor, + FloatAccessor, + GeoArrowPickingInfo, + MultiPointVector, + PointVector, +} from "./types.js"; +import { EXTENSION_NAME } from "./constants.js"; + +const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255]; + +/** All properties supported by GeoArrowColumnLayer */ +export type GeoArrowColumnLayerProps = _GeoArrowColumnLayerProps & + CompositeLayerProps; + +/** Properties added by GeoArrowColumnLayer */ +type _GeoArrowColumnLayerProps = { + data: arrow.Table; + + /** + * The number of sides to render the disk as. + * @default 20 + */ + diskResolution?: number; + + /** + * isk size in units specified by `radiusUnits`. + * @default 1000 + */ + radius?: number; + + /** + * Disk rotation, counter-clockwise in degrees. + * @default 0 + */ + angle?: number; + + /** + * Replace the default geometry (regular polygon that fits inside the unit circle) with a custom one. + * @default null + */ + vertices: Position[] | null; + + /** + * Disk offset from the position, relative to the radius. + * @default [0,0] + */ + offset?: [number, number]; + + /** + * Radius multiplier, between 0 - 1 + * @default 1 + */ + coverage?: number; + + /** + * Column elevation multiplier. + * @default 1 + */ + elevationScale?: number; + + /** + * Whether to draw a filled column (solid fill). + * @default true + */ + filled?: boolean; + + /** + * Whether to draw an outline around the disks. + * @default false + */ + stroked?: boolean; + + /** + * Whether to extrude the columns. If set to `false`, all columns will be rendered as flat polygons. + * @default true + */ + extruded?: boolean; + + /** + * Whether to generate a line wireframe of the column. + * @default false + */ + wireframe?: boolean; + + /** + * If `true`, the vertical surfaces of the columns use [flat shading](https://en.wikipedia.org/wiki/Shading#Flat_vs._smooth_shading). + * @default false + */ + flatShading?: boolean; + + /** + * The units of the radius. + * @default 'meters' + */ + radiusUnits?: Unit; + + /** + * The units of the line width. + * @default 'meters' + */ + lineWidthUnits?: Unit; + + /** + * The line width multiplier that multiplied to all outlines. + * @default 1 + */ + lineWidthScale?: number; + + /** + * The minimum outline width in pixels. + * @default 0 + */ + lineWidthMinPixels?: number; + + /** + * The maximum outline width in pixels. + * @default Number.MAX_SAFE_INTEGER + */ + lineWidthMaxPixels?: number; + + /** + * Material settings for lighting effect. Applies if `extruded: true`. + * + * @default true + * @see https://deck.gl/docs/developer-guide/using-lighting + */ + material?: Material; + + /** + * Method called to retrieve the position of each column. + * @default object => object.position + */ + getPosition?: PointVector; + + /** + * Fill color value or accessor. + * @default [0, 0, 0, 255] + */ + getFillColor?: ColorAccessor; + + /** + * Line color value or accessor. + * + * @default [0, 0, 0, 255] + */ + getLineColor?: ColorAccessor; + + /** + * The elevation of each cell in meters. + * @default 1000 + */ + getElevation?: FloatAccessor; + + /** + * The width of the outline of the column, in units specified by `lineWidthUnits`. + * + * @default 1 + */ + getLineWidth?: FloatAccessor; + + /** + * If `true`, validate the arrays provided (e.g. chunk lengths) + * @default true + */ + _validate?: boolean; +}; + +const defaultProps: DefaultProps = { + diskResolution: { type: "number", min: 4, value: 20 }, + vertices: null, + radius: { type: "number", min: 0, value: 1000 }, + angle: { type: "number", value: 0 }, + offset: { type: "array", value: [0, 0] }, + coverage: { type: "number", min: 0, max: 1, value: 1 }, + elevationScale: { type: "number", min: 0, value: 1 }, + radiusUnits: "meters", + lineWidthUnits: "meters", + lineWidthScale: 1, + lineWidthMinPixels: 0, + lineWidthMaxPixels: Number.MAX_SAFE_INTEGER, + + extruded: true, + wireframe: false, + filled: true, + stroked: false, + flatShading: false, + + getFillColor: { type: "accessor", value: DEFAULT_COLOR }, + getLineColor: { type: "accessor", value: DEFAULT_COLOR }, + getLineWidth: { type: "accessor", value: 1 }, + getElevation: { type: "accessor", value: 1000 }, + material: true, + + _validate: true, +}; + +/** + * Render extruded cylinders (tessellated regular polygons) at given + * coordinates. + */ +export class GeoArrowColumnLayer< + ExtraProps extends {} = {} +> extends CompositeLayer & ExtraProps> { + static defaultProps = defaultProps; + static layerName = "GeoArrowColumnLayer"; + + getPickingInfo({ + info, + sourceLayer, + }: GetPickingInfoParams): GeoArrowPickingInfo { + const { data: table } = this.props; + + // Geometry index as rendered + let index = info.index; + + // @ts-expect-error `recordBatchIdx` is manually set on layer props + const recordBatchIdx: number = sourceLayer.props.recordBatchIdx; + const batch = table.batches[recordBatchIdx]; + const row = batch.get(index); + + // @ts-expect-error hack: using private method to avoid recomputing via + // batch lengths on each iteration + const offsets: number[] = table._offsets; + const currentBatchOffset = offsets[recordBatchIdx]; + + // Update index to be _global_ index, not within the specific record batch + index += currentBatchOffset; + return { + ...info, + index, + object: row, + }; + } + + renderLayers(): Layer<{}> | LayersList | null { + const { data: table } = this.props; + + const pointVector = getGeometryVector(table, EXTENSION_NAME.POINT); + if (pointVector !== null) { + return this._renderLayersPoint(pointVector); + } + + const geometryColumn = this.props.getPosition; + if (isPointVector(geometryColumn)) { + return this._renderLayersPoint(geometryColumn); + } + + throw new Error("geometryColumn not point"); + } + + _renderLayersPoint( + geometryColumn: PointVector + ): Layer<{}> | LayersList | null { + const { data: table } = this.props; + + if (this.props._validate) { + const vectorAccessors: arrow.Vector[] = [geometryColumn]; + for (const accessor of [ + this.props.getFillColor, + this.props.getLineColor, + this.props.getLineWidth, + this.props.getElevation, + ]) { + if (accessor instanceof arrow.Vector) { + vectorAccessors.push(accessor); + } + } + + validatePointType(geometryColumn.type); + validateVectorAccessors(table, vectorAccessors); + + if (this.props.getFillColor instanceof arrow.Vector) { + validateColorVector(this.props.getFillColor); + } + if (this.props.getLineColor instanceof arrow.Vector) { + validateColorVector(this.props.getLineColor); + } + } + + const layers: ColumnLayer[] = []; + for ( + let recordBatchIdx = 0; + recordBatchIdx < table.batches.length; + recordBatchIdx++ + ) { + const geometryData = geometryColumn.data[recordBatchIdx]; + const flatCoordsData = getPointChild(geometryData); + const flatCoordinateArray = flatCoordsData.values; + + const props: ColumnLayerProps = { + // @ts-expect-error used for picking purposes + recordBatchIdx, + + id: `${this.props.id}-geoarrow-column-${recordBatchIdx}`, + + diskResolution: this.props.diskResolution, + radius: this.props.radius, + angle: this.props.angle, + vertices: this.props.vertices, + offset: this.props.offset, + coverage: this.props.coverage, + elevationScale: this.props.elevationScale, + filled: this.props.filled, + stroked: this.props.stroked, + extruded: this.props.extruded, + wireframe: this.props.wireframe, + flatShading: this.props.flatShading, + radiusUnits: this.props.radiusUnits, + lineWidthUnits: this.props.lineWidthUnits, + lineWidthScale: this.props.lineWidthScale, + lineWidthMinPixels: this.props.lineWidthMinPixels, + lineWidthMaxPixels: this.props.lineWidthMaxPixels, + material: this.props.material, + + data: { + length: geometryData.length, + attributes: { + getPosition: { + value: flatCoordinateArray, + size: geometryData.type.listSize, + }, + }, + }, + }; + + assignAccessor({ + props, + propName: "getFillColor", + propInput: this.props.getFillColor, + chunkIdx: recordBatchIdx, + }); + assignAccessor({ + props, + propName: "getLineColor", + propInput: this.props.getLineColor, + chunkIdx: recordBatchIdx, + }); + assignAccessor({ + props, + propName: "getElevation", + propInput: this.props.getElevation, + chunkIdx: recordBatchIdx, + }); + assignAccessor({ + props, + propName: "getLineWidth", + propInput: this.props.getLineWidth, + chunkIdx: recordBatchIdx, + }); + + const layer = new ColumnLayer(this.getSubLayerProps(props)); + layers.push(layer); + } + + return layers; + } +} diff --git a/src/index.ts b/src/index.ts index 349f7e7..aa2400e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,12 @@ export { GeoArrowArcLayer } from "./arc-layer.js"; +export { GeoArrowColumnLayer } from "./column-layer.js"; export { GeoArrowHeatmapLayer } from "./heatmap-layer.js"; export { GeoArrowPathLayer } from "./path-layer.js"; export { GeoArrowScatterplotLayer } from "./scatterplot-layer.js"; export { GeoArrowSolidPolygonLayer } from "./solid-polygon-layer.js"; export type { GeoArrowArcLayerProps } from "./arc-layer.js"; +export type { GeoArrowColumnLayerProps } from "./column-layer.js"; export type { GeoArrowHeatmapLayerProps } from "./heatmap-layer.js"; export type { GeoArrowPathLayerProps } from "./path-layer.js"; export type { GeoArrowScatterplotLayerProps } from "./scatterplot-layer.js"; From 5531ff826c22aa5458a6d93a5c5de6759eddc8ac Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 11:42:30 -0500 Subject: [PATCH 2/9] remove unused imports --- src/column-layer.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/column-layer.ts b/src/column-layer.ts index 38ceb0f..c861606 100644 --- a/src/column-layer.ts +++ b/src/column-layer.ts @@ -15,13 +15,9 @@ import * as arrow from "apache-arrow"; import { assignAccessor, getGeometryVector, - getMultiPointChild, getPointChild, - invertOffsets, - isMultiPointVector, isPointVector, validateColorVector, - validateMultiPointType, validatePointType, validateVectorAccessors, } from "./utils.js"; @@ -29,7 +25,6 @@ import { ColorAccessor, FloatAccessor, GeoArrowPickingInfo, - MultiPointVector, PointVector, } from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; From 0f146b32132a97a41543bf4e2a0cfd8d8cb9b958 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 11:51:37 -0500 Subject: [PATCH 3/9] Add geo-layers --- package-lock.json | 2 ++ package.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/package-lock.json b/package-lock.json index 27fef15..445e8c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "devDependencies": { "@deck.gl/aggregation-layers": "^8.9.23", "@deck.gl/core": "^8.9.23", + "@deck.gl/geo-layers": "^8.9.23", "@deck.gl/layers": "^8.9.23", "@math.gl/polygon": "^3.6.2", "@rollup/plugin-terser": "^0.4.3", @@ -29,6 +30,7 @@ "peerDependencies": { "@deck.gl/aggregation-layers": "^8.9.23", "@deck.gl/core": "^8.9.23", + "@deck.gl/geo-layers": "^8.9.23", "@deck.gl/layers": "^8.9.23", "@math.gl/polygon": "^3.6.2", "apache-arrow": "^13.0.0" diff --git a/package.json b/package.json index 57573b5..3980926 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "peerDependencies": { "@deck.gl/aggregation-layers": "^8.9.23", "@deck.gl/core": "^8.9.23", + "@deck.gl/geo-layers": "^8.9.23", "@deck.gl/layers": "^8.9.23", "@math.gl/polygon": "^3.6.2", "apache-arrow": "^13.0.0" @@ -46,6 +47,7 @@ "devDependencies": { "@deck.gl/aggregation-layers": "^8.9.23", "@deck.gl/core": "^8.9.23", + "@deck.gl/geo-layers": "^8.9.23", "@deck.gl/layers": "^8.9.23", "@math.gl/polygon": "^3.6.2", "@rollup/plugin-terser": "^0.4.3", From 153818a24bea6320d32be7e7a064a9f915e82ae8 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 11:53:08 -0500 Subject: [PATCH 4/9] wip --- src/h3-hexagon-layer.ts | 91 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/h3-hexagon-layer.ts diff --git a/src/h3-hexagon-layer.ts b/src/h3-hexagon-layer.ts new file mode 100644 index 0000000..4f82845 --- /dev/null +++ b/src/h3-hexagon-layer.ts @@ -0,0 +1,91 @@ +import { + CompositeLayer, + CompositeLayerProps, + DefaultProps, + GetPickingInfoParams, + Layer, + LayersList, + Unit, +} from "@deck.gl/core/typed"; +import { PolygonLayer } from "@deck.gl/layers/typed"; +import { ScatterplotLayer } from "@deck.gl/layers/typed"; +import { H3HexagonLayer } from "@deck.gl/geo-layers/typed"; +import type { H3HexagonLayerProps } from "@deck.gl/geo-layers/typed"; +import type { + PolygonLayerProps, + ScatterplotLayerProps, +} from "@deck.gl/layers/typed"; +import * as arrow from "apache-arrow"; +import { + assignAccessor, + getGeometryVector, + getMultiPointChild, + getPointChild, + invertOffsets, + isMultiPointVector, + isPointVector, + validateColorVector, + validateMultiPointType, + validatePointType, + validateVectorAccessors, +} from "./utils.js"; +import { + ColorAccessor, + FloatAccessor, + GeoArrowPickingInfo, + MultiPointVector, + PointVector, +} from "./types.js"; +import { EXTENSION_NAME } from "./constants.js"; + +/** All properties supported by GeoArrowH3HexagonLayer */ +export type GeoArrowH3HexagonLayerProps = + _GeoArrowH3HexagonLayerProps & PolygonLayerProps; + +/** Props added by the GeoArrowH3HexagonLayer */ +type _GeoArrowH3HexagonLayerProps = Omit & { + getHexagon: arrow.Vector; + +} + +let x: _GeoArrowH3HexagonLayerProps; +function tmp(x: _GeoArrowH3HexagonLayerProps) { + x.gethe +} + + +{ + /** + * Whether or not draw hexagons with high precision. + * @default 'auto' + */ + highPrecision?: boolean | "auto"; + /** + * Coverage of hexagon in cell. + * @default 1 + */ + coverage?: number; + /** + * Center hexagon that best represents the shape of the set. If not specified, the hexagon closest to the center of the viewport is used. + */ + centerHexagon?: string | null; // H3Index | null; + /** + * Called for each data object to retrieve the quadkey string identifier. + * + * By default, it reads `hexagon` property of data object. + */ + getHexagon: arrow.Vector; + /** + * Whether to extrude polygons. + * @default true + */ + extruded?: boolean; +}; + +const defaultProps: DefaultProps = { + ...PolygonLayer.defaultProps, + highPrecision: "auto", + coverage: { type: "number", min: 0, max: 1, value: 1 }, + centerHexagon: null, + extruded: true, +}; From 944516d9045d2e13c9dbceb9d8e07bc2e2781d3b Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 12:25:45 -0500 Subject: [PATCH 5/9] simplify column layer props --- src/column-layer.ts | 158 +++++--------------------------------------- 1 file changed, 17 insertions(+), 141 deletions(-) diff --git a/src/column-layer.ts b/src/column-layer.ts index c861606..3fcfb77 100644 --- a/src/column-layer.ts +++ b/src/column-layer.ts @@ -5,9 +5,6 @@ import { GetPickingInfoParams, Layer, LayersList, - Material, - Position, - Unit, } from "@deck.gl/core/typed"; import { ColumnLayer } from "@deck.gl/layers/typed"; import type { ColumnLayerProps } from "@deck.gl/layers/typed"; @@ -29,126 +26,22 @@ import { } from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; -const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255]; - /** All properties supported by GeoArrowColumnLayer */ -export type GeoArrowColumnLayerProps = _GeoArrowColumnLayerProps & +export type GeoArrowColumnLayerProps = Omit< + ColumnLayerProps, + | "getPosition" + | "getFillColor" + | "getLineColor" + | "getElevation" + | "getLineWidth" +> & + _GeoArrowColumnLayerProps & CompositeLayerProps; /** Properties added by GeoArrowColumnLayer */ type _GeoArrowColumnLayerProps = { data: arrow.Table; - /** - * The number of sides to render the disk as. - * @default 20 - */ - diskResolution?: number; - - /** - * isk size in units specified by `radiusUnits`. - * @default 1000 - */ - radius?: number; - - /** - * Disk rotation, counter-clockwise in degrees. - * @default 0 - */ - angle?: number; - - /** - * Replace the default geometry (regular polygon that fits inside the unit circle) with a custom one. - * @default null - */ - vertices: Position[] | null; - - /** - * Disk offset from the position, relative to the radius. - * @default [0,0] - */ - offset?: [number, number]; - - /** - * Radius multiplier, between 0 - 1 - * @default 1 - */ - coverage?: number; - - /** - * Column elevation multiplier. - * @default 1 - */ - elevationScale?: number; - - /** - * Whether to draw a filled column (solid fill). - * @default true - */ - filled?: boolean; - - /** - * Whether to draw an outline around the disks. - * @default false - */ - stroked?: boolean; - - /** - * Whether to extrude the columns. If set to `false`, all columns will be rendered as flat polygons. - * @default true - */ - extruded?: boolean; - - /** - * Whether to generate a line wireframe of the column. - * @default false - */ - wireframe?: boolean; - - /** - * If `true`, the vertical surfaces of the columns use [flat shading](https://en.wikipedia.org/wiki/Shading#Flat_vs._smooth_shading). - * @default false - */ - flatShading?: boolean; - - /** - * The units of the radius. - * @default 'meters' - */ - radiusUnits?: Unit; - - /** - * The units of the line width. - * @default 'meters' - */ - lineWidthUnits?: Unit; - - /** - * The line width multiplier that multiplied to all outlines. - * @default 1 - */ - lineWidthScale?: number; - - /** - * The minimum outline width in pixels. - * @default 0 - */ - lineWidthMinPixels?: number; - - /** - * The maximum outline width in pixels. - * @default Number.MAX_SAFE_INTEGER - */ - lineWidthMaxPixels?: number; - - /** - * Material settings for lighting effect. Applies if `extruded: true`. - * - * @default true - * @see https://deck.gl/docs/developer-guide/using-lighting - */ - material?: Material; - /** * Method called to retrieve the position of each column. * @default object => object.position @@ -188,32 +81,15 @@ type _GeoArrowColumnLayerProps = { _validate?: boolean; }; -const defaultProps: DefaultProps = { - diskResolution: { type: "number", min: 4, value: 20 }, - vertices: null, - radius: { type: "number", min: 0, value: 1000 }, - angle: { type: "number", value: 0 }, - offset: { type: "array", value: [0, 0] }, - coverage: { type: "number", min: 0, max: 1, value: 1 }, - elevationScale: { type: "number", min: 0, value: 1 }, - radiusUnits: "meters", - lineWidthUnits: "meters", - lineWidthScale: 1, - lineWidthMinPixels: 0, - lineWidthMaxPixels: Number.MAX_SAFE_INTEGER, - - extruded: true, - wireframe: false, - filled: true, - stroked: false, - flatShading: false, - - getFillColor: { type: "accessor", value: DEFAULT_COLOR }, - getLineColor: { type: "accessor", value: DEFAULT_COLOR }, - getLineWidth: { type: "accessor", value: 1 }, - getElevation: { type: "accessor", value: 1000 }, - material: true, +// Remove data and getPosition from the upstream default props +const { + data: _data, + getPosition: _getPosition, + ..._defaultProps +} = ColumnLayer.defaultProps; +const defaultProps: DefaultProps = { + ..._defaultProps, _validate: true, }; From 21cd0a42cea924a86112a43e1dfdfa395eed2cbe Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 12:32:31 -0500 Subject: [PATCH 6/9] wip --- src/h3-hexagon-layer.ts | 213 +++++++++++++++++++++++++++++++++------- 1 file changed, 177 insertions(+), 36 deletions(-) diff --git a/src/h3-hexagon-layer.ts b/src/h3-hexagon-layer.ts index 4f82845..7e7848d 100644 --- a/src/h3-hexagon-layer.ts +++ b/src/h3-hexagon-layer.ts @@ -39,53 +39,194 @@ import { import { EXTENSION_NAME } from "./constants.js"; /** All properties supported by GeoArrowH3HexagonLayer */ -export type GeoArrowH3HexagonLayerProps = - _GeoArrowH3HexagonLayerProps & PolygonLayerProps; +export type GeoArrowH3HexagonLayerProps = Omit< + H3HexagonLayerProps, + "data" | "getHexagon" +> & + _GeoArrowH3HexagonLayerProps & + CompositeLayerProps; /** Props added by the GeoArrowH3HexagonLayer */ -type _GeoArrowH3HexagonLayerProps = Omit & { - getHexagon: arrow.Vector; - -} - -let x: _GeoArrowH3HexagonLayerProps; -function tmp(x: _GeoArrowH3HexagonLayerProps) { - x.gethe -} - +type _GeoArrowH3HexagonLayerProps = { + data?: arrow.Table; -{ - /** - * Whether or not draw hexagons with high precision. - * @default 'auto' - */ - highPrecision?: boolean | "auto"; - /** - * Coverage of hexagon in cell. - * @default 1 - */ - coverage?: number; - /** - * Center hexagon that best represents the shape of the set. If not specified, the hexagon closest to the center of the viewport is used. - */ - centerHexagon?: string | null; // H3Index | null; /** * Called for each data object to retrieve the quadkey string identifier. - * - * By default, it reads `hexagon` property of data object. */ getHexagon: arrow.Vector; + /** - * Whether to extrude polygons. + * If `true`, validate the arrays provided (e.g. chunk lengths) * @default true */ - extruded?: boolean; + _validate?: boolean; }; +// Remove data from the upstream default props +const { + data: _data, + getHexagon: _getHexagon, + ..._defaultProps +} = H3HexagonLayer.defaultProps; + const defaultProps: DefaultProps = { - ...PolygonLayer.defaultProps, - highPrecision: "auto", - coverage: { type: "number", min: 0, max: 1, value: 1 }, - centerHexagon: null, - extruded: true, + ..._defaultProps, + _validate: true, }; + +// export class GeoArrowArcLayer< +// ExtraProps extends {} = {} +// > extends CompositeLayer & ExtraProps> { +// static defaultProps = defaultProps; +// static layerName = "GeoArrowArcLayer"; + +// getPickingInfo({ +// info, +// sourceLayer, +// }: GetPickingInfoParams): GeoArrowPickingInfo { +// const { data: table } = this.props; + +// // Geometry index as rendered +// let index = info.index; + +// // @ts-expect-error `recordBatchIdx` is manually set on layer props +// const recordBatchIdx: number = sourceLayer.props.recordBatchIdx; +// const batch = table.batches[recordBatchIdx]; +// const row = batch.get(index); + +// // @ts-expect-error hack: using private method to avoid recomputing via +// // batch lengths on each iteration +// const offsets: number[] = table._offsets; +// const currentBatchOffset = offsets[recordBatchIdx]; + +// // Update index to be _global_ index, not within the specific record batch +// index += currentBatchOffset; +// return { +// ...info, +// index, +// object: row, +// }; +// } + +// renderLayers(): Layer<{}> | LayersList | null { +// return this._renderLayersPoint(); +// } + +// _renderLayersPoint(): Layer<{}> | LayersList | null { +// const { +// data: table, +// getSourcePosition: sourcePosition, +// getTargetPosition: targetPosition, +// } = this.props; + +// if (this.props._validate) { +// const vectorAccessors: arrow.Vector[] = [sourcePosition, targetPosition]; +// for (const accessor of [ +// this.props.getSourceColor, +// this.props.getTargetColor, +// this.props.getWidth, +// this.props.getHeight, +// this.props.getTilt, +// ]) { +// if (accessor instanceof arrow.Vector) { +// vectorAccessors.push(accessor); +// } +// } + +// validatePointType(sourcePosition.type); +// validatePointType(targetPosition.type); +// if (table) { +// validateVectorAccessors(table, vectorAccessors); +// } else { +// const validationTable = new arrow.Table({ +// source: sourcePosition, +// target: targetPosition, +// }); +// validateVectorAccessors(validationTable, vectorAccessors); +// } + +// if (this.props.getSourceColor instanceof arrow.Vector) { +// validateColorVector(this.props.getSourceColor); +// } +// if (this.props.getTargetColor instanceof arrow.Vector) { +// validateColorVector(this.props.getTargetColor); +// } +// } + +// const layers: H3HexagonLayer[] = []; +// for ( +// let recordBatchIdx = 0; +// recordBatchIdx < table.batches.length; +// recordBatchIdx++ +// ) { +// const sourceData = sourcePosition.data[recordBatchIdx]; +// const sourceValues = getPointChild(sourceData).values; +// const targetData = targetPosition.data[recordBatchIdx]; +// const targetValues = getPointChild(targetData).values; + +// const props: H3HexagonLayerProps = { +// // @ts-expect-error used for picking purposes +// recordBatchIdx, + +// id: `${this.props.id}-geoarrow-arc-${recordBatchIdx}`, + +// greatCircle: this.props.greatCircle, +// numSegments: this.props.numSegments, +// widthUnits: this.props.widthUnits, +// widthScale: this.props.widthScale, +// widthMinPixels: this.props.widthMinPixels, +// widthMaxPixels: this.props.widthMaxPixels, + +// data: { +// length: sourceData.length, +// attributes: { +// getSourcePosition: { +// value: sourceValues, +// size: sourceData.type.listSize, +// }, +// getTargetPosition: { +// value: targetValues, +// size: targetData.type.listSize, +// }, +// }, +// }, +// }; + +// assignAccessor({ +// props, +// propName: "getSourceColor", +// propInput: this.props.getSourceColor, +// chunkIdx: recordBatchIdx, +// }); +// assignAccessor({ +// props, +// propName: "getTargetColor", +// propInput: this.props.getTargetColor, +// chunkIdx: recordBatchIdx, +// }); +// assignAccessor({ +// props, +// propName: "getWidth", +// propInput: this.props.getWidth, +// chunkIdx: recordBatchIdx, +// }); +// assignAccessor({ +// props, +// propName: "getHeight", +// propInput: this.props.getHeight, +// chunkIdx: recordBatchIdx, +// }); +// assignAccessor({ +// props, +// propName: "getTilt", +// propInput: this.props.getTilt, +// chunkIdx: recordBatchIdx, +// }); + +// const layer = new H3HexagonLayer(this.getSubLayerProps(props)); +// layers.push(layer); +// } + +// return layers; +// } +// } From 7e8dec7cef7e50fbc2a37214a90261a0b93b9ddd Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 12:34:36 -0500 Subject: [PATCH 7/9] wip --- src/column-layer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/column-layer.ts b/src/column-layer.ts index 3fcfb77..32b5cf7 100644 --- a/src/column-layer.ts +++ b/src/column-layer.ts @@ -29,6 +29,7 @@ import { EXTENSION_NAME } from "./constants.js"; /** All properties supported by GeoArrowColumnLayer */ export type GeoArrowColumnLayerProps = Omit< ColumnLayerProps, + | "data" | "getPosition" | "getFillColor" | "getLineColor" From 4c502449e2851323d986a499690748f43da1395f Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 13:29:01 -0500 Subject: [PATCH 8/9] Add h3 hexagon layer --- src/arc-layer.ts | 4 +- src/column-layer.ts | 29 +---- src/h3-hexagon-layer.ts | 275 ++++++++++++++-------------------------- src/heatmap-layer.ts | 2 +- src/index.ts | 2 + 5 files changed, 104 insertions(+), 208 deletions(-) diff --git a/src/arc-layer.ts b/src/arc-layer.ts index d4b4273..399119d 100644 --- a/src/arc-layer.ts +++ b/src/arc-layer.ts @@ -42,7 +42,7 @@ export type GeoArrowArcLayerProps = Omit< /** Properties added by GeoArrowArcLayer */ type _GeoArrowArcLayerProps = { - data?: arrow.Table; + data: arrow.Table; /** * Method called to retrieve the source position of each object. @@ -139,6 +139,8 @@ export class GeoArrowArcLayer< } } + // Note: below we iterate over table batches anyways, so this layer won't + // work as-is if data/table is null validatePointType(sourcePosition.type); validatePointType(targetPosition.type); if (table) { diff --git a/src/column-layer.ts b/src/column-layer.ts index 32b5cf7..ff048bc 100644 --- a/src/column-layer.ts +++ b/src/column-layer.ts @@ -25,6 +25,7 @@ import { PointVector, } from "./types.js"; import { EXTENSION_NAME } from "./constants.js"; +import { getPickingInfo } from "./picking.js"; /** All properties supported by GeoArrowColumnLayer */ export type GeoArrowColumnLayerProps = Omit< @@ -104,32 +105,8 @@ export class GeoArrowColumnLayer< static defaultProps = defaultProps; static layerName = "GeoArrowColumnLayer"; - getPickingInfo({ - info, - sourceLayer, - }: GetPickingInfoParams): GeoArrowPickingInfo { - const { data: table } = this.props; - - // Geometry index as rendered - let index = info.index; - - // @ts-expect-error `recordBatchIdx` is manually set on layer props - const recordBatchIdx: number = sourceLayer.props.recordBatchIdx; - const batch = table.batches[recordBatchIdx]; - const row = batch.get(index); - - // @ts-expect-error hack: using private method to avoid recomputing via - // batch lengths on each iteration - const offsets: number[] = table._offsets; - const currentBatchOffset = offsets[recordBatchIdx]; - - // Update index to be _global_ index, not within the specific record batch - index += currentBatchOffset; - return { - ...info, - index, - object: row, - }; + getPickingInfo(params: GetPickingInfoParams): GeoArrowPickingInfo { + return getPickingInfo(params, this.props.data); } renderLayers(): Layer<{}> | LayersList | null { diff --git a/src/h3-hexagon-layer.ts b/src/h3-hexagon-layer.ts index 7e7848d..f417047 100644 --- a/src/h3-hexagon-layer.ts +++ b/src/h3-hexagon-layer.ts @@ -5,38 +5,18 @@ import { GetPickingInfoParams, Layer, LayersList, - Unit, } from "@deck.gl/core/typed"; -import { PolygonLayer } from "@deck.gl/layers/typed"; -import { ScatterplotLayer } from "@deck.gl/layers/typed"; import { H3HexagonLayer } from "@deck.gl/geo-layers/typed"; import type { H3HexagonLayerProps } from "@deck.gl/geo-layers/typed"; -import type { - PolygonLayerProps, - ScatterplotLayerProps, -} from "@deck.gl/layers/typed"; import * as arrow from "apache-arrow"; import { assignAccessor, - getGeometryVector, - getMultiPointChild, - getPointChild, - invertOffsets, - isMultiPointVector, - isPointVector, + extractAccessorsFromProps, validateColorVector, - validateMultiPointType, - validatePointType, validateVectorAccessors, } from "./utils.js"; -import { - ColorAccessor, - FloatAccessor, - GeoArrowPickingInfo, - MultiPointVector, - PointVector, -} from "./types.js"; -import { EXTENSION_NAME } from "./constants.js"; +import { GeoArrowPickingInfo } from "./types.js"; +import { getPickingInfo } from "./picking.js"; /** All properties supported by GeoArrowH3HexagonLayer */ export type GeoArrowH3HexagonLayerProps = Omit< @@ -48,7 +28,7 @@ export type GeoArrowH3HexagonLayerProps = Omit< /** Props added by the GeoArrowH3HexagonLayer */ type _GeoArrowH3HexagonLayerProps = { - data?: arrow.Table; + data: arrow.Table; /** * Called for each data object to retrieve the quadkey string identifier. @@ -74,159 +54,94 @@ const defaultProps: DefaultProps = { _validate: true, }; -// export class GeoArrowArcLayer< -// ExtraProps extends {} = {} -// > extends CompositeLayer & ExtraProps> { -// static defaultProps = defaultProps; -// static layerName = "GeoArrowArcLayer"; - -// getPickingInfo({ -// info, -// sourceLayer, -// }: GetPickingInfoParams): GeoArrowPickingInfo { -// const { data: table } = this.props; - -// // Geometry index as rendered -// let index = info.index; - -// // @ts-expect-error `recordBatchIdx` is manually set on layer props -// const recordBatchIdx: number = sourceLayer.props.recordBatchIdx; -// const batch = table.batches[recordBatchIdx]; -// const row = batch.get(index); - -// // @ts-expect-error hack: using private method to avoid recomputing via -// // batch lengths on each iteration -// const offsets: number[] = table._offsets; -// const currentBatchOffset = offsets[recordBatchIdx]; - -// // Update index to be _global_ index, not within the specific record batch -// index += currentBatchOffset; -// return { -// ...info, -// index, -// object: row, -// }; -// } - -// renderLayers(): Layer<{}> | LayersList | null { -// return this._renderLayersPoint(); -// } - -// _renderLayersPoint(): Layer<{}> | LayersList | null { -// const { -// data: table, -// getSourcePosition: sourcePosition, -// getTargetPosition: targetPosition, -// } = this.props; - -// if (this.props._validate) { -// const vectorAccessors: arrow.Vector[] = [sourcePosition, targetPosition]; -// for (const accessor of [ -// this.props.getSourceColor, -// this.props.getTargetColor, -// this.props.getWidth, -// this.props.getHeight, -// this.props.getTilt, -// ]) { -// if (accessor instanceof arrow.Vector) { -// vectorAccessors.push(accessor); -// } -// } - -// validatePointType(sourcePosition.type); -// validatePointType(targetPosition.type); -// if (table) { -// validateVectorAccessors(table, vectorAccessors); -// } else { -// const validationTable = new arrow.Table({ -// source: sourcePosition, -// target: targetPosition, -// }); -// validateVectorAccessors(validationTable, vectorAccessors); -// } - -// if (this.props.getSourceColor instanceof arrow.Vector) { -// validateColorVector(this.props.getSourceColor); -// } -// if (this.props.getTargetColor instanceof arrow.Vector) { -// validateColorVector(this.props.getTargetColor); -// } -// } - -// const layers: H3HexagonLayer[] = []; -// for ( -// let recordBatchIdx = 0; -// recordBatchIdx < table.batches.length; -// recordBatchIdx++ -// ) { -// const sourceData = sourcePosition.data[recordBatchIdx]; -// const sourceValues = getPointChild(sourceData).values; -// const targetData = targetPosition.data[recordBatchIdx]; -// const targetValues = getPointChild(targetData).values; - -// const props: H3HexagonLayerProps = { -// // @ts-expect-error used for picking purposes -// recordBatchIdx, - -// id: `${this.props.id}-geoarrow-arc-${recordBatchIdx}`, - -// greatCircle: this.props.greatCircle, -// numSegments: this.props.numSegments, -// widthUnits: this.props.widthUnits, -// widthScale: this.props.widthScale, -// widthMinPixels: this.props.widthMinPixels, -// widthMaxPixels: this.props.widthMaxPixels, - -// data: { -// length: sourceData.length, -// attributes: { -// getSourcePosition: { -// value: sourceValues, -// size: sourceData.type.listSize, -// }, -// getTargetPosition: { -// value: targetValues, -// size: targetData.type.listSize, -// }, -// }, -// }, -// }; - -// assignAccessor({ -// props, -// propName: "getSourceColor", -// propInput: this.props.getSourceColor, -// chunkIdx: recordBatchIdx, -// }); -// assignAccessor({ -// props, -// propName: "getTargetColor", -// propInput: this.props.getTargetColor, -// chunkIdx: recordBatchIdx, -// }); -// assignAccessor({ -// props, -// propName: "getWidth", -// propInput: this.props.getWidth, -// chunkIdx: recordBatchIdx, -// }); -// assignAccessor({ -// props, -// propName: "getHeight", -// propInput: this.props.getHeight, -// chunkIdx: recordBatchIdx, -// }); -// assignAccessor({ -// props, -// propName: "getTilt", -// propInput: this.props.getTilt, -// chunkIdx: recordBatchIdx, -// }); - -// const layer = new H3HexagonLayer(this.getSubLayerProps(props)); -// layers.push(layer); -// } - -// return layers; -// } -// } +export class GeoArrowH3HexagonLayer< + ExtraProps extends {} = {} +> extends CompositeLayer & ExtraProps> { + static defaultProps = defaultProps; + static layerName = "GeoArrowH3HexagonLayer"; + + getPickingInfo(params: GetPickingInfoParams): GeoArrowPickingInfo { + return getPickingInfo(params, this.props.data); + } + + renderLayers(): Layer<{}> | LayersList | null { + return this._renderLayersPoint(); + } + + _renderLayersPoint(): Layer<{}> | LayersList | null { + const { data: table, getHexagon: hexagonColumn } = this.props; + + if (this.props._validate) { + const vectorAccessors: arrow.Vector[] = []; + const colorVectorAccessors: arrow.Vector[] = []; + for (const [accessorName, accessorValue] of Object.entries(this.props)) { + // Is it an accessor + if (accessorName.startsWith("get")) { + // Is it a vector accessor + if (accessorValue instanceof arrow.Vector) { + vectorAccessors.push(accessorValue); + + // Is it a color vector accessor + if (accessorName.endsWith("Color")) { + colorVectorAccessors.push(accessorValue); + } + } + } + } + + validateVectorAccessors(table, vectorAccessors); + for (const colorVectorAccessor of colorVectorAccessors) { + validateColorVector(colorVectorAccessor); + } + } + + const layers: H3HexagonLayer[] = []; + for ( + let recordBatchIdx = 0; + recordBatchIdx < table.batches.length; + recordBatchIdx++ + ) { + const hexData = hexagonColumn.data[recordBatchIdx]; + const hexValues = hexData.values; + + // Exclude manually-set accessors + const [accessors, otherProps] = extractAccessorsFromProps(this.props, [ + "getHexagon", + ]); + + const props: H3HexagonLayerProps = { + ...otherProps, + + // @ts-expect-error used for picking purposes + recordBatchIdx, + + id: `${this.props.id}-geoarrow-arc-${recordBatchIdx}`, + + data: { + length: hexData.length, + attributes: { + getHexagon: { + value: hexValues, + // h3 cells should always be 15 characters...? + size: 15, + }, + }, + }, + }; + + for (const [propName, propInput] of Object.entries(accessors)) { + assignAccessor({ + props, + propName, + propInput, + chunkIdx: recordBatchIdx, + }); + } + + const layer = new H3HexagonLayer(this.getSubLayerProps(props)); + layers.push(layer); + } + + return layers; + } +} diff --git a/src/heatmap-layer.ts b/src/heatmap-layer.ts index b64eccc..8a4e908 100644 --- a/src/heatmap-layer.ts +++ b/src/heatmap-layer.ts @@ -30,7 +30,7 @@ export type GeoArrowHeatmapLayerProps = Omit< /** Properties added by GeoArrowHeatmapLayer */ type _GeoArrowHeatmapLayerProps = { - data?: arrow.Table; + data: arrow.Table; /** * Method called to retrieve the position of each object. diff --git a/src/index.ts b/src/index.ts index 1d6e5e9..9e2b5c2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ export { GeoArrowArcLayer } from "./arc-layer.js"; export { GeoArrowColumnLayer } from "./column-layer.js"; +export { GeoArrowH3HexagonLayer } from "./h3-hexagon-layer.js"; export { GeoArrowHeatmapLayer } from "./heatmap-layer.js"; export { GeoArrowPathLayer } from "./path-layer.js"; export { GeoArrowScatterplotLayer } from "./scatterplot-layer.js"; @@ -7,6 +8,7 @@ export { GeoArrowSolidPolygonLayer } from "./solid-polygon-layer.js"; export type { GeoArrowArcLayerProps } from "./arc-layer.js"; export type { GeoArrowColumnLayerProps } from "./column-layer.js"; +export type { GeoArrowH3HexagonLayerProps } from "./h3-hexagon-layer.js"; export type { GeoArrowHeatmapLayerProps } from "./heatmap-layer.js"; export type { GeoArrowPathLayerProps } from "./path-layer.js"; export type { GeoArrowScatterplotLayerProps } from "./scatterplot-layer.js"; From df3da43ee9e9d1fbf8d2af6db76b23d4c1467393 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Wed, 8 Nov 2023 13:32:28 -0500 Subject: [PATCH 9/9] export hex layer with underscore --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9e2b5c2..0e56c0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ export { GeoArrowArcLayer } from "./arc-layer.js"; export { GeoArrowColumnLayer } from "./column-layer.js"; -export { GeoArrowH3HexagonLayer } from "./h3-hexagon-layer.js"; +export { GeoArrowH3HexagonLayer as _GeoArrowH3HexagonLayer } from "./h3-hexagon-layer.js"; export { GeoArrowHeatmapLayer } from "./heatmap-layer.js"; export { GeoArrowPathLayer } from "./path-layer.js"; export { GeoArrowScatterplotLayer } from "./scatterplot-layer.js"; @@ -8,7 +8,7 @@ export { GeoArrowSolidPolygonLayer } from "./solid-polygon-layer.js"; export type { GeoArrowArcLayerProps } from "./arc-layer.js"; export type { GeoArrowColumnLayerProps } from "./column-layer.js"; -export type { GeoArrowH3HexagonLayerProps } from "./h3-hexagon-layer.js"; +export type { GeoArrowH3HexagonLayerProps as _GeoArrowH3HexagonLayerProps } from "./h3-hexagon-layer.js"; export type { GeoArrowHeatmapLayerProps } from "./heatmap-layer.js"; export type { GeoArrowPathLayerProps } from "./path-layer.js"; export type { GeoArrowScatterplotLayerProps } from "./scatterplot-layer.js";