Skip to content

Commit a45b49e

Browse files
authored
GeoArrow-based Trips Layer (#34)
* wip trips layer * update lockfile * wip * updates * lint * fix package json * Working trips layer * lint * bump version
1 parent 5cc954d commit a45b49e

File tree

13 files changed

+1808
-4
lines changed

13 files changed

+1808
-4
lines changed

examples/trips/README.md

Whitespace-only changes.

examples/trips/app.tsx

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import React, { useState, useEffect } from "react";
2+
import { createRoot } from "react-dom/client";
3+
import { StaticMap, MapContext, NavigationControl } from "react-map-gl";
4+
import DeckGL, { Layer, PickingInfo } from "deck.gl/typed";
5+
import { GeoArrowTripsLayer } from "@geoarrow/deck.gl-layers";
6+
import * as arrow from "apache-arrow";
7+
8+
const GEOARROW_POINT_DATA = "http://localhost:8080/trips.feather";
9+
10+
const INITIAL_VIEW_STATE = {
11+
longitude: -74,
12+
latitude: 40.72,
13+
zoom: 13,
14+
pitch: 45,
15+
bearing: 0,
16+
};
17+
18+
const MAP_STYLE =
19+
"https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json";
20+
const NAV_CONTROL_STYLE = {
21+
position: "absolute",
22+
top: 10,
23+
left: 10,
24+
};
25+
26+
function Root() {
27+
const trailLength = 180;
28+
// unit corresponds to the timestamp in source data
29+
const loopLength = 1800;
30+
const animationSpeed = 1;
31+
32+
const [time, setTime] = useState(0);
33+
const [animation] = useState<{ id: number }>({ id: 0 });
34+
35+
const animate = () => {
36+
setTime((t) => (t + animationSpeed) % loopLength);
37+
animation.id = window.requestAnimationFrame(animate);
38+
};
39+
40+
useEffect(() => {
41+
animation.id = window.requestAnimationFrame(animate);
42+
return () => window.cancelAnimationFrame(animation.id);
43+
}, [animation]);
44+
45+
const onClick = (info: PickingInfo) => {
46+
if (info.object) {
47+
console.log(JSON.stringify(info.object.toJSON()));
48+
}
49+
};
50+
51+
const [table, setTable] = useState<arrow.Table | null>(null);
52+
53+
useEffect(() => {
54+
// declare the data fetching function
55+
const fetchData = async () => {
56+
const data = await fetch(GEOARROW_POINT_DATA);
57+
const buffer = await data.arrayBuffer();
58+
const table = arrow.tableFromIPC(buffer);
59+
setTable(table);
60+
};
61+
62+
if (!table) {
63+
fetchData().catch(console.error);
64+
}
65+
});
66+
67+
const layers: Layer[] = [];
68+
69+
table &&
70+
layers.push(
71+
new GeoArrowTripsLayer({
72+
id: "geoarrow-linestring",
73+
data: table,
74+
getColor: [255, 0, 0],
75+
getPath: table.getChild("geometry")!,
76+
getTimestamps: table.getChild("timestamps")!,
77+
widthMinPixels: 2,
78+
pickable: true,
79+
trailLength,
80+
currentTime: time,
81+
}),
82+
);
83+
84+
return (
85+
<DeckGL
86+
initialViewState={INITIAL_VIEW_STATE}
87+
controller={true}
88+
layers={layers}
89+
ContextProvider={MapContext.Provider}
90+
onClick={onClick}
91+
glOptions={{
92+
powerPreference: "high-performance",
93+
}}
94+
>
95+
<StaticMap mapStyle={MAP_STYLE} />
96+
<NavigationControl style={NAV_CONTROL_STYLE} />
97+
</DeckGL>
98+
);
99+
}
100+
101+
/* global document */
102+
const container = document.body.appendChild(document.createElement("div"));
103+
createRoot(container).render(<Root />);

examples/trips/generate_data.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import requests
2+
import numpy as np
3+
import pyarrow as pa
4+
import pyarrow.feather as feather
5+
6+
url = "https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/trips/trips-v7.json"
7+
r = requests.get(url)
8+
data = r.json()
9+
10+
coord_num = 0
11+
geoms_num = len(data)
12+
offsets = np.zeros(geoms_num + 1, dtype=np.int32)
13+
14+
for i, item in enumerate(data):
15+
assert len(item["path"]) == len(item["timestamps"])
16+
coord_num += len(item["path"])
17+
offsets[i + 1] = coord_num
18+
19+
vendor = np.zeros(geoms_num, dtype=np.uint8)
20+
coords = np.zeros((coord_num, 2), dtype=np.float32)
21+
timestamps = np.zeros(coord_num, dtype=np.float32)
22+
23+
for i, item in enumerate(data):
24+
start_offset = offsets[i]
25+
end_offset = offsets[i + 1]
26+
path = np.array(item["path"])
27+
assert end_offset - start_offset == path.shape[0]
28+
coords[start_offset:end_offset, :] = path
29+
timestamps[start_offset:end_offset] = item["timestamps"]
30+
vendor[i] = item["vendor"]
31+
32+
coords_fixed_size_list = pa.FixedSizeListArray.from_arrays(
33+
pa.array(coords.flatten("C")), 2
34+
)
35+
linestrings_arr = pa.ListArray.from_arrays(pa.array(offsets), coords_fixed_size_list)
36+
timestamp_arr = pa.ListArray.from_arrays(pa.array(offsets), timestamps)
37+
38+
table = pa.table(
39+
{"geometry": linestrings_arr, "timestamps": timestamp_arr, "vendor": vendor}
40+
)
41+
42+
feather.write_feather(table, "trips.feather", compression="uncompressed")

examples/trips/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>deck.gl GeoArrowTripsLayer Example</title>
6+
<style>
7+
body {margin: 0; width: 100vw; height: 100vh; overflow: hidden;}
8+
</style>
9+
</head>
10+
<body>
11+
</body>
12+
<script type="module" src="app.tsx"></script>
13+
</html>

examples/trips/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "deckgl-example-geoarrow-trips-layer",
3+
"version": "0.0.0",
4+
"private": true,
5+
"license": "MIT",
6+
"scripts": {
7+
"start": "vite --open",
8+
"build": "vite build"
9+
},
10+
"dependencies": {
11+
"@geoarrow/deck.gl-layers": "../../",
12+
"apache-arrow": ">=14",
13+
"deck.gl": "^8.9.23",
14+
"react": "^18.0.0",
15+
"react-dom": "^18.0.0",
16+
"react-map-gl": "^5.3.0"
17+
},
18+
"devDependencies": {
19+
"@types/react": "^18.0.0",
20+
"@types/react-dom": "^18.0.0",
21+
"vite": "^4.0.0"
22+
},
23+
"volta": {
24+
"extends": "../../package.json"
25+
}
26+
}

0 commit comments

Comments
 (0)