Skip to content

Commit

Permalink
Start implementation of point cloud layer (#96)
Browse files Browse the repository at this point in the history
* DRAFT: initial commit for pointcloud layer

* test push

* base pointcloud layer. TODO: verify accessors in PointCloudLayerProps, TODO: verify accessor methods in PointCloudLayer's render methods

* update accessors, error handling, remove multipoint support

* allows custom normalization function taking vector of fixed size lists

* run prettier and typecheck

* correct merge and pass typecheck

* remove unused imports

* omit PointCloudLayerProps overrides, remove TODO comments

* remove comment, add data attr to PointCloudLayerProps props

---------

Co-authored-by: Kyle Barron <[email protected]>
  • Loading branch information
naomatheus and kylebarron authored Feb 2, 2024
1 parent 4887ce8 commit bfde800
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ docs_build/
*.zip
*.feather
*.parquet
*.code-workspace

*.yarn

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

189 changes: 189 additions & 0 deletions src/point-cloud-layer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import {
CompositeLayer,
CompositeLayerProps,
DefaultProps,
GetPickingInfoParams,
Layer,
LayersList,
assert,
Unit,
Material,
} from "@deck.gl/core/typed";
import { PointCloudLayer } from "@deck.gl/layers/typed";
import type { PointCloudLayerProps } from "@deck.gl/layers/typed";
import * as arrow from "apache-arrow";
import * as ga from "@geoarrow/geoarrow-js";
import {
assignAccessor,
extractAccessorsFromProps,
getGeometryVector,
} from "./utils.js";
import {
GeoArrowExtraPickingProps,
computeChunkOffsets,
getPickingInfo,
} from "./picking.js";
import { ColorAccessor, GeoArrowPickingInfo, NormalAccessor } from "./types.js";
import { EXTENSION_NAME } from "./constants.js";
import { validateAccessors } from "./validate.js";

/* All properties supported by GeoArrowPointCloudLayer */
export type GeoArrowPointCloudLayerProps = Omit<
PointCloudLayerProps<arrow.Table>,
"data" | "getPosition" | "getNormal" | "getColor"
> &
_GeoArrowPointCloudLayerProps &
CompositeLayerProps;

/* All properties added by GeoArrowPointCloudLayer */
type _GeoArrowPointCloudLayerProps = {
// data
data: arrow.Table;

/**
* If `true`, validate the arrays provided (e.g. chunk lengths)
* @default true
*/
_validate?: boolean;

/**
* Center position accessor.
* If not provided, will be inferred by finding a column with extension type
* `"geoarrow.point"`
*/
getPosition?: ga.vector.PointVector;

/**
* The normal of each object, in `[nx, ny, nz]`.
* @default [0,0,1]
*/
getNormal?: NormalAccessor;

/**
* The rgba color is in the format of `[r, g, b, [a]]`
* @default [0,0,0,225]
*/
getColor?: ColorAccessor;
};

// Remove data nd get Position from the upstream default props
const {
data: _data,
getPosition: _getPosition,
..._upstreamDefaultProps
} = PointCloudLayer.defaultProps;

// Default props added by us
const ourDefaultProps = {
_validate: true,
};

// @ts-expect-error Type error in merging default props with ours
const defaultProps: DefaultProps<GeoArrowPointCloudLayerProps> = {
..._upstreamDefaultProps,
...ourDefaultProps,
};

export class GeoArrowPointCloudLayer<
ExtraProps extends {} = {},
> extends CompositeLayer<GeoArrowPointCloudLayerProps & ExtraProps> {
static defaultProps = defaultProps;
static layerName = "GeoArrowPointCloudLayer";

getPickingInfo(
params: GetPickingInfoParams & {
sourceLayer: { props: GeoArrowExtraPickingProps };
},
): GeoArrowPickingInfo {
return getPickingInfo(params, this.props.data);
}

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 (
geometryColumn !== undefined &&
ga.vector.isPointVector(geometryColumn)
) {
return this._renderLayersPoint(geometryColumn);
}

throw new Error("geometryColumn not GeoArrow point");
}

_renderLayersPoint(
geometryColumn: ga.vector.PointVector,
): Layer<{}> | LayersList | null {
const { data: table } = this.props;

if (this.props._validate) {
assert(
ga.vector.isPointVector(geometryColumn),
"The geometry column is not a valid PointVector.",
);
assert(
geometryColumn.type.listSize === 3,
"Points of a PointCloudLayer in the geometry column must be three-dimensional.",
);
validateAccessors(this.props, table);
}

// Exclude manually-set accessors
const [accessors, otherProps] = extractAccessorsFromProps(this.props, [
"getPosition",
]);
const tableOffsets = computeChunkOffsets(table.data);

const layers: PointCloudLayer[] = [];
for (
let recordBatchIdx = 0;
recordBatchIdx < table.batches.length;
recordBatchIdx++
) {
const geometryData = geometryColumn.data[recordBatchIdx];
const flatCoordsData = ga.child.getPointChild(geometryData);
const flatCoordinateArray = flatCoordsData.values;

const props: PointCloudLayerProps = {
// Note: because this is a composite layer and not doing the rendering
// itself, we still have to pass in our defaultProps
...ourDefaultProps,
...otherProps,

// used for picking purposes
recordBatchIdx,
tableOffsets,

id: `${this.props.id}-geoarrow-pointcloud-${recordBatchIdx}`,
data: {
// @ts-expect-error passed through to enable use by function accessors
data: table.batches[recordBatchIdx],
length: geometryData.length,
attributes: {
getPosition: {
value: flatCoordinateArray,
size: geometryData.type.listSize,
},
},
},
};
for (const [propName, propInput] of Object.entries(accessors)) {
assignAccessor({
props,
propName,
propInput,
chunkIdx: recordBatchIdx,
});
}
const layer = new PointCloudLayer(this.getSubLayerProps(props));
layers.push(layer);
}
return layers;
}
}
3 changes: 3 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,6 @@ export type TimestampAccessor = arrow.Vector<arrow.List<arrow.Float>>;
export type ColorAccessor =
| arrow.Vector<arrow.FixedSizeList<arrow.Uint8>>
| Accessor<arrow.RecordBatch, Color | Color[]>;
export type NormalAccessor =
| arrow.Vector<arrow.FixedSizeList<arrow.Float32>>
| Accessor<arrow.Table, arrow.Vector<arrow.FixedSizeList<arrow.Float32>>>;

0 comments on commit bfde800

Please sign in to comment.