Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix polygon layer #105

Merged
merged 2 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions examples/polygon/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import DeckGL, { Layer, PickingInfo } from "deck.gl/typed";
import { GeoArrowPolygonLayer } from "@geoarrow/deck.gl-layers";
import * as arrow from "apache-arrow";

const GEOARROW_POLYGON_DATA = "http://localhost:8080/small.feather";
// const GEOARROW_POLYGON_DATA = "http://localhost:8080/small.feather";

const GEOARROW_POLYGON_DATA = "http://localhost:8080/nybb.feather";

const INITIAL_VIEW_STATE = {
latitude: 40.63403641639511,
longitude: -111.91530172951025,
latitude: 40.71,
// longitude: -111.9,
longitude: -74.0,
zoom: 9,
bearing: 0,
pitch: 0,
Expand Down Expand Up @@ -59,7 +62,7 @@ function Root() {
data: table,
getFillColor: [0, 100, 60, 160],
getLineColor: [255, 0, 0],
lineWidthMinPixels: 0.1,
lineWidthMinPixels: 1,
extruded: false,
wireframe: true,
// getElevation: 0,
Expand Down
10 changes: 7 additions & 3 deletions examples/polygon/generate_data.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import geopandas as gpd
import pyarrow.feather as feather
from lonboard.geoarrow.geopandas_interop import geopandas_to_geoarrow
from lonboard import SolidPolygonLayer


def main():
gdf = gpd.read_file("Utah.geojson.zip", engine="pyogrio")
table = geopandas_to_geoarrow(gdf)
feather.write_feather(table, "utah.feather", compression="uncompressed")
layer = SolidPolygonLayer.from_geopandas(gdf)
feather.write_feather(layer.table, "utah.feather", compression="uncompressed")

gdf = gpd.read_file(gpd.datasets.get_path("nybb"))
layer = SolidPolygonLayer.from_geopandas(gdf)
feather.write_feather(layer.table, "nybb.feather", compression="uncompressed")


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"examples/*"
],
"name": "@geoarrow/deck.gl-layers",
"version": "0.3.0-beta.11",
"version": "0.3.0-beta.12",
"type": "module",
"description": "",
"source": "src/index.ts",
Expand Down
96 changes: 84 additions & 12 deletions src/polygon-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,82 @@ import { getGeometryVector } from "./utils.js";
import { GeoArrowExtraPickingProps, getPickingInfo } from "./picking.js";
import { ColorAccessor, FloatAccessor, GeoArrowPickingInfo } from "./types.js";
import { EXTENSION_NAME } from "./constants.js";
import { validateAccessors } from "./validate.js";
import { GeoArrowSolidPolygonLayer } from "./solid-polygon-layer.js";
import { GeoArrowPathLayer } from "./path-layer.js";

/**
* Get the exterior of a PolygonVector or PolygonData as a MultiLineString
*
* Note that casting to a MultiLineString is a no-op of the underlying data
* structure. For the purposes of the PolygonLayer we don't want to cast to a
* LineString because that would change the number of rows in the table.
*/
export function getPolygonExterior(
input: ga.vector.PolygonVector,
): ga.vector.MultiLineStringVector;
export function getPolygonExterior(
input: ga.data.PolygonData,
): ga.data.MultiLineStringData;

export function getPolygonExterior(
input: ga.vector.PolygonVector | ga.data.PolygonData,
): ga.vector.MultiLineStringVector | ga.data.MultiLineStringData {
if ("data" in input) {
return new arrow.Vector(input.data.map((data) => getPolygonExterior(data)));
}

return input;
}

/**
* Get the exterior of a MultiPolygonVector or MultiPolygonData
*
* Note that for the purposes of the PolygonLayer, we don't want to change the
* number of rows in the table. Instead, we convert each MultiPolygon to a
* single MultiLineString, combining all exteriors of each contained Polygon
* into a single united MultiLineString.
*
* This means that we need to condense both two offset buffers from the
* MultiPolygonVector/Data (geomOffsets and polygonOffsets) into a single
* `geomOffsets` for the new MultiLineStringVector/Data.
*/
export function getMultiPolygonExterior(
input: ga.vector.MultiPolygonVector,
): ga.vector.MultiLineStringVector;
export function getMultiPolygonExterior(
input: ga.data.MultiPolygonData,
): ga.data.MultiLineStringData;

export function getMultiPolygonExterior(
input: ga.vector.MultiPolygonVector | ga.data.MultiPolygonData,
): ga.vector.MultiLineStringVector | ga.data.MultiLineStringData {
if ("data" in input) {
return new arrow.Vector(
input.data.map((data) => getMultiPolygonExterior(data)),
);
}

const geomOffsets: Int32Array = input.valueOffsets;
const polygonData = ga.child.getMultiPolygonChild(input);
const polygonOffsets: Int32Array = polygonData.valueOffsets;
const lineStringData = ga.child.getPolygonChild(polygonData);

const resolvedOffsets = new Int32Array(geomOffsets.length);
for (let i = 0; i < resolvedOffsets.length; ++i) {
// Perform the lookup
resolvedOffsets[i] = polygonOffsets[geomOffsets[i]];
}

return arrow.makeData({
type: new arrow.List(polygonData.type.children[0]),
length: input.length,
nullCount: input.nullCount,
nullBitmap: input.nullBitmap,
child: lineStringData,
valueOffsets: resolvedOffsets,
});
}

/** All properties supported by GeoArrowPolygonLayer */
export type GeoArrowPolygonLayerProps = Omit<
PolygonLayerProps,
Expand Down Expand Up @@ -97,6 +169,11 @@ const defaultProps: DefaultProps<GeoArrowPolygonLayerProps> = {
const defaultLineColor: [number, number, number, number] = [0, 0, 0, 255];
const defaultFillColor: [number, number, number, number] = [0, 0, 0, 255];

/** The `GeoArrowPolygonLayer` renders filled, stroked and/or extruded polygons.
*
* GeoArrowPolygonLayer is a CompositeLayer that wraps the
* GeoArrowSolidPolygonLayer and the GeoArrowPathLayer.
*/
export class GeoArrowPolygonLayer<
ExtraProps extends {} = {},
> extends CompositeLayer<Required<GeoArrowPolygonLayerProps> & ExtraProps> {
Expand All @@ -119,12 +196,12 @@ export class GeoArrowPolygonLayer<
return this._renderLayers(polygonVector);
}

const MultiPolygonVector = getGeometryVector(
const multiPolygonVector = getGeometryVector(
table,
EXTENSION_NAME.MULTIPOLYGON,
);
if (MultiPolygonVector !== null) {
return this._renderLayers(MultiPolygonVector);
if (multiPolygonVector !== null) {
return this._renderLayers(multiPolygonVector);
}

const geometryColumn = this.props.getPolygon;
Expand All @@ -147,16 +224,11 @@ export class GeoArrowPolygonLayer<
): Layer<{}> | LayersList | null {
const { data: table } = this.props;

if (this.props._validate) {
assert(ga.vector.isPolygonVector(geometryColumn));
validateAccessors(this.props, table);
}

let getPath: ga.vector.LineStringVector | ga.vector.MultiLineStringVector;
let getPath: ga.vector.MultiLineStringVector;
if (ga.vector.isPolygonVector(geometryColumn)) {
getPath = ga.algorithm.getPolygonExterior(geometryColumn);
getPath = getPolygonExterior(geometryColumn);
} else if (ga.vector.isMultiPolygonVector(geometryColumn)) {
getPath = ga.algorithm.getMultiPolygonExterior(geometryColumn);
getPath = getMultiPolygonExterior(geometryColumn);
} else {
assert(false);
}
Expand Down
Loading