-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start implementation of point cloud layer (#96)
* 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
1 parent
4887ce8
commit bfde800
Showing
4 changed files
with
195 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ docs_build/ | |
*.zip | ||
*.feather | ||
*.parquet | ||
*.code-workspace | ||
|
||
*.yarn | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters