Skip to content

Commit 9b1ebad

Browse files
committed
Add Heatmap example
1 parent 32eb16f commit 9b1ebad

File tree

8 files changed

+1657
-0
lines changed

8 files changed

+1657
-0
lines changed

examples/heatmap/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## Example: Use `@geoarrow/deck.gl-layers` with GeoArrow point data to render a Heatmap
2+
3+
## Data for example
4+
5+
```
6+
wget https://ookla-open-data.s3.us-west-2.amazonaws.com/parquet/performance/type=mobile/year=2019/quarter=1/2019-01-01_performance_mobile_tiles.parquet
7+
poetry install
8+
poetry run python generate_data.py
9+
```
10+
11+
## Serve data
12+
13+
```
14+
npx http-server --cors
15+
```
16+
17+
## Usage
18+
19+
To install dependencies:
20+
21+
```bash
22+
npm install
23+
# or
24+
yarn
25+
```
26+
27+
Commands:
28+
29+
* `npm start` is the development target, to serve the app and hot reload.
30+
* `npm run build` is the production target, to create the final bundle and write to disk.

examples/heatmap/app.tsx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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";
5+
import { GeoArrowHeatmapLayer } from "@geoarrow/deck.gl-layers";
6+
import * as arrow from "apache-arrow";
7+
8+
const GEOARROW_POINT_DATA =
9+
"http://localhost:8080/2019-01-01_performance_mobile_tiles.feather";
10+
11+
const INITIAL_VIEW_STATE = {
12+
latitude: 20,
13+
longitude: 0,
14+
zoom: 2,
15+
bearing: 0,
16+
pitch: 0,
17+
};
18+
19+
const MAP_STYLE =
20+
"https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json";
21+
const NAV_CONTROL_STYLE = {
22+
position: "absolute",
23+
top: 10,
24+
left: 10,
25+
};
26+
27+
function Root() {
28+
const onClick = (info: PickingInfo) => {
29+
if (info.object) {
30+
console.log(JSON.stringify(info.object.toJSON()));
31+
}
32+
};
33+
34+
const [table, setTable] = useState<arrow.Table | null>(null);
35+
36+
useEffect(() => {
37+
// declare the data fetching function
38+
const fetchData = async () => {
39+
const data = await fetch(GEOARROW_POINT_DATA);
40+
const buffer = await data.arrayBuffer();
41+
const table = arrow.tableFromIPC(buffer);
42+
setTable(table);
43+
};
44+
45+
if (!table) {
46+
fetchData().catch(console.error);
47+
}
48+
});
49+
50+
const layers: Layer[] = [];
51+
52+
// Scenario 1: Just pass the table to the layer, with no additional properties. As the layer is a GeoArrowLayer, it should automatically detect the columns that are needed for the visualization.
53+
// table &&
54+
// layers.push(
55+
// new GeoArrowHeatmapLayer({
56+
// id: "geoarrow-heatmap",
57+
// data: table,
58+
// }),
59+
// );
60+
61+
// Scenario 2: pass a getWeight function returning a random number, which
62+
// avoids the error in the construction, but do not render anything.
63+
table &&
64+
layers.push(
65+
new GeoArrowHeatmapLayer({
66+
id: "geoarrow-heatmap",
67+
data: table,
68+
getWeight: (d: any) => Math.random() * 100 + 1,
69+
}),
70+
);
71+
72+
return (
73+
<DeckGL
74+
initialViewState={INITIAL_VIEW_STATE}
75+
controller={true}
76+
layers={layers}
77+
ContextProvider={MapContext.Provider}
78+
onClick={onClick}
79+
>
80+
<StaticMap mapStyle={MAP_STYLE} />
81+
<NavigationControl style={NAV_CONTROL_STYLE} />
82+
</DeckGL>
83+
);
84+
}
85+
86+
/* global document */
87+
const container = document.body.appendChild(document.createElement("div"));
88+
createRoot(container).render(<Root />);

examples/heatmap/generate_data.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from pathlib import Path
2+
3+
import geopandas as gpd
4+
import pandas as pd
5+
import pyarrow as pa
6+
import pyarrow.feather as feather
7+
import shapely
8+
from lonboard._geoarrow.geopandas_interop import geopandas_to_geoarrow
9+
from lonboard.colormap import apply_continuous_cmap
10+
from palettable.colorbrewer.diverging import BrBG_10
11+
12+
url = "https://ookla-open-data.s3.us-west-2.amazonaws.com/parquet/performance/type=mobile/year=2019/quarter=1/2019-01-01_performance_mobile_tiles.parquet"
13+
14+
path = Path("2019-01-01_performance_mobile_tiles.parquet")
15+
16+
17+
def main():
18+
if not path.exists():
19+
msg = f"Please download file to this directory from {url=}."
20+
raise ValueError(msg)
21+
22+
df = pd.read_parquet(path)
23+
centroids = shapely.centroid(shapely.from_wkt(df["tile"]))
24+
25+
# Save space by using a smaller data type
26+
df_cols = ["avg_d_kbps", "avg_u_kbps", "avg_lat_ms"]
27+
for col in df_cols:
28+
df[col] = pd.to_numeric(df[col], downcast="unsigned")
29+
30+
gdf = gpd.GeoDataFrame(df[df_cols], geometry=centroids)
31+
table = geopandas_to_geoarrow(gdf, preserve_index=False)
32+
33+
min_bound = 5000
34+
max_bound = 50000
35+
download_speed = gdf["avg_d_kbps"]
36+
normalized_download_speed = (download_speed - min_bound) / (max_bound - min_bound)
37+
38+
colors = apply_continuous_cmap(normalized_download_speed, BrBG_10)
39+
table = table.append_column(
40+
"colors", pa.FixedSizeListArray.from_arrays(colors.flatten("C"), 3)
41+
)
42+
43+
feather.write_feather(
44+
table, "2019-01-01_performance_mobile_tiles.feather", compression="uncompressed"
45+
)
46+
47+
48+
if __name__ == "__main__":
49+
main()

examples/heatmap/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 GeoArrowPointLayer 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/heatmap/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "deckgl-example-geoarrow-heatmap-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+
"@loaders.gl/compression": "^4.1.4",
12+
"@loaders.gl/crypto": "^4.1.4",
13+
"apache-arrow": ">=14",
14+
"deck.gl": "^9.0.27",
15+
"react": "^18.0.0",
16+
"react-dom": "^18.0.0",
17+
"react-map-gl": "^5.3.0"
18+
},
19+
"devDependencies": {
20+
"@types/react": "^18.0.0",
21+
"@types/react-dom": "^18.0.0",
22+
"vite": "^4.0.0"
23+
},
24+
"volta": {
25+
"extends": "../../package.json"
26+
}
27+
}

0 commit comments

Comments
 (0)