Skip to content

Commit 317c817

Browse files
committed
split parquet experiment
1 parent a9f9254 commit 317c817

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed

tutorials/parquet_cesium_split.qmd

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
---
2+
title: Using Cesium for display of remote parquet.
3+
categories: [parquet, spatial, recipe]
4+
---
5+
6+
This page renders points from an iSamples parquet file on cesium using point primitives.
7+
8+
<script src="https://cesium.com/downloads/cesiumjs/releases/1.127/Build/Cesium/Cesium.js"></script>
9+
<link href="https://cesium.com/downloads/cesiumjs/releases/1.127/Build/Cesium/Widgets/widgets.css" rel="stylesheet"></link>
10+
<style>
11+
div.cesium-topleft {
12+
display: block;
13+
position: absolute;
14+
background: #00000099;
15+
color: white;
16+
height: auto;
17+
z-index: 999;
18+
}
19+
#cesiumContainer {
20+
aspect-ratio: 1/1;
21+
}
22+
</style>
23+
24+
```{ojs}
25+
//| output: false
26+
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwNzk3NjkyMy1iNGI1LTRkN2UtODRiMy04OTYwYWE0N2M3ZTkiLCJpZCI6Njk1MTcsImlhdCI6MTYzMzU0MTQ3N30.e70dpNzOCDRLDGxRguQCC-tRzGzA-23Xgno5lNgCeB4';
27+
```
28+
29+
```{ojs}
30+
//| echo: false
31+
viewof nodes_path = Inputs.text({
32+
label:"Source",
33+
value:"https://s3.beehivebeach.com/isamples-data/oc_nodes.parquet",
34+
width:"100%",
35+
submit:true
36+
});
37+
viewof edges_path = Inputs.text({
38+
label:"Source",
39+
value:"https://s3.beehivebeach.com/isamples-data/oc_edge.parquet",
40+
width:"100%",
41+
submit:true
42+
});
43+
```
44+
45+
```{ojs}
46+
//| code-fold: true
47+
48+
// Create a DuckDB instance
49+
db = {
50+
const instance = await DuckDBClient.of();
51+
await instance.query(`create view nodes as select * from read_parquet('${nodes_path}')`);
52+
instance.query(`create table edges as select s,p,o from read_parquet('${edges_path}')`);
53+
return instance;
54+
}
55+
56+
57+
async function loadData(query, params=[], waiting_id=null) {
58+
// Get loading indicator
59+
const waiter = document.getElementById(waiting_id);
60+
if (waiter) {
61+
waiter.hidden = false;
62+
}
63+
try {
64+
// Run the (slow) query
65+
const _results = await db.query(query, ...params);
66+
return _results;
67+
} catch (error) {
68+
if (waiter) {
69+
waiter.innerHtml = `<pre>${error}</pre>`;
70+
}
71+
return null;
72+
} finally {
73+
// Hide the waiter (if there is one)
74+
if (waiter) {
75+
waiter.hidden = true;
76+
}
77+
}
78+
}
79+
80+
locations = {
81+
// get the content form the parquet file
82+
const query = `SELECT pid, latitude, longitude FROM nodes WHERE otype='GeospatialCoordLocation'`;
83+
const data = await loadData(query, [], "loading_1");
84+
85+
// Clear the existing PointPrimitiveCollection
86+
content.points.removeAll();
87+
//content.points = new Cesium.PointPrimitiveCollection();
88+
89+
// create point primitives for cesium display
90+
const scalar = new Cesium.NearFarScalar(1.5e2, 2, 8.0e6, 0.2);
91+
const color = Cesium.Color.PINK;
92+
const point_size = 4;
93+
for (const row of data) {
94+
content.points.add({
95+
id: row.pid,
96+
// https://cesium.com/learn/cesiumjs/ref-doc/Cartesian3.html#.fromDegrees
97+
position: Cesium.Cartesian3.fromDegrees(
98+
row.longitude, //longitude
99+
row.latitude, //latitude
100+
0,//randomCoordinateJitter(10.0, 10.0), //elevation, m
101+
),
102+
pixelSize: point_size,
103+
color: color,
104+
scaleByDistance: scalar,
105+
});
106+
}
107+
content.enableTracking();
108+
return data;
109+
}
110+
111+
112+
function createShowPrimitive(viewer) {
113+
return function(movement) {
114+
// Get the point at the mouse end position
115+
const selectPoint = viewer.viewer.scene.pick(movement.endPosition);
116+
117+
// Clear the current selection, if there is one and it is different to the selectPoint
118+
if (viewer.currentSelection !== null) {
119+
//console.log(`selected.p ${viewer.currentSelection}`)
120+
if (Cesium.defined(selectPoint) && selectPoint !== viewer.currentSelection) {
121+
console.log(`selected.p 2 ${viewer.currentSelection}`)
122+
viewer.currentSelection.primitive.pixelSize = 4;
123+
viewer.currentSelection.primitive.outlineColor = Cesium.Color.TRANSPARENT;
124+
viewer.currentSelection.outlineWidth = 0;
125+
viewer.currentSelection = null;
126+
}
127+
}
128+
129+
// If selectPoint is valid and no currently selected point
130+
if (Cesium.defined(selectPoint) && selectPoint.hasOwnProperty("primitive")) {
131+
//console.log(`showPrimitiveId ${selectPoint.id}`);
132+
//const carto = Cesium.Cartographic.fromCartesian(selectPoint.primitive.position)
133+
viewer.pointLabel.position = selectPoint.primitive.position;
134+
viewer.pointLabel.label.show = true;
135+
//viewer.pointLabel.label.text = `id:${selectPoint.id}, ${carto}`;
136+
viewer.pointLabel.label.text = `${selectPoint.id}`;
137+
selectPoint.primitive.pixelSize = 20;
138+
selectPoint.primitive.outlineColor = Cesium.Color.YELLOW;
139+
selectPoint.primitive.outlineWidth = 3;
140+
viewer.currentSelection = selectPoint;
141+
} else {
142+
viewer.pointLabel.label.show = false;
143+
}
144+
}
145+
}
146+
147+
class CView {
148+
constructor(target) {
149+
this.viewer = new Cesium.Viewer(
150+
target, {
151+
timeline: false,
152+
animation: false,
153+
baseLayerPicker: false,
154+
fullscreenElement: target,
155+
terrain: Cesium.Terrain.fromWorldTerrain()
156+
});
157+
this.currentSelection = null;
158+
this.point_size = 1;
159+
this.n_points = 0;
160+
// https://cesium.com/learn/cesiumjs/ref-doc/PointPrimitiveCollection.html
161+
this.points = new Cesium.PointPrimitiveCollection();
162+
this.viewer.scene.primitives.add(this.points);
163+
164+
this.pointLabel = this.viewer.entities.add({
165+
label: {
166+
show: false,
167+
showBackground: true,
168+
font: "14px monospace",
169+
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
170+
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
171+
pixelOffset: new Cesium.Cartesian2(15, 0),
172+
// this attribute will prevent this entity clipped by the terrain
173+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
174+
text:"",
175+
},
176+
});
177+
178+
this.pickHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
179+
// Can also do this rather than wait for the points to be generated
180+
//this.pickHandler.setInputAction(createShowPrimitive(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
181+
182+
this.selectHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
183+
this.selectHandler.setInputAction((e) => {
184+
const selectPoint = this.viewer.scene.pick(e.position);
185+
if (Cesium.defined(selectPoint) && selectPoint.hasOwnProperty("primitive")) {
186+
mutable clickedPointId = selectPoint.id;
187+
}
188+
},Cesium.ScreenSpaceEventType.LEFT_CLICK);
189+
190+
}
191+
192+
enableTracking() {
193+
this.pickHandler.setInputAction(createShowPrimitive(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
194+
}
195+
}
196+
197+
content = new CView("cesiumContainer");
198+
199+
async function getGeoRecord(pid) {
200+
if (pid === null || pid ==="" || pid == "unset") {
201+
return "unset";
202+
}
203+
const q = `SELECT row_id, pid, otype, latitude, longitude FROM nodes WHERE otype='GeospatialCoordLocation' AND pid=?`;
204+
const result = await db.queryRow(q, [pid]);
205+
return result;
206+
}
207+
208+
async function locationUsedBy(rowid){
209+
if (rowid === undefined || rowid === null) {
210+
return [];
211+
}
212+
const q = `select pid, otype from nodes where row_id in (select edges.s from edges where edges.o=?);`;
213+
return db.query(q, [rowid]);
214+
}
215+
216+
async function samplesAtLocation(rowid) {
217+
if (rowid === undefined || rowid === null) {
218+
return [];
219+
}
220+
const q = `select pid, label, description from nodes where row_id in (
221+
with recursive efor(s,p,o) as (
222+
select s,p,o from edges where o=?
223+
union all
224+
select e.s, e.p, e.o from edges as e, efor as ef where ef.s = e.o
225+
) select s from efor where p='produced_by');`;
226+
return db.query(q, [rowid]);
227+
}
228+
229+
mutable clickedPointId = "unset";
230+
selectedGeoRecord = await getGeoRecord(clickedPointId);
231+
232+
md`Retrieved ${pointdata.length} locations from ${nodes_path}.`;
233+
```
234+
235+
::: {.panel-tabset}
236+
237+
## Map
238+
239+
<div id="cesiumContainer"></div>
240+
241+
## Data
242+
243+
<div id="loading_1">Loading...</div>
244+
245+
```{ojs}
246+
//| code-fold: true
247+
248+
viewof pointdata = {
249+
const data_table = Inputs.table(locations, {
250+
header: {
251+
row_id:"Row ID",
252+
pid: "PID",
253+
latitude: "Latitude",
254+
longitude: "Longitude"
255+
},
256+
});
257+
return data_table;
258+
}
259+
```
260+
261+
:::
262+
263+
The click point ID is "${clickedPointId}".
264+
265+
```{ojs}
266+
//| echo: false
267+
md`\`\`\`
268+
${JSON.stringify(selectedGeoRecord, null, 2)}
269+
\`\`\`
270+
`
271+
```
272+
273+
```{ojs}
274+
Inputs.table(locationUsedBy(selectedGeoRecord.row_id));
275+
```
276+
277+
```{ojs}
278+
viewof usedby = {
279+
const table = Inputs.table(samplesAtLocation(selectedGeoRecord.row_id));
280+
return table;
281+
}
282+
```

0 commit comments

Comments
 (0)