This page renders points from a static parquet snapshot of Index to Marine And Lacustrine Geological Samples (IMLGS) data.
mutable selected_repo = {
//return "Alfred Wegener Institut";
return null;
}
recorddata = {
const params = [];
let query = `SELECT * FROM read_parquet('${parquet_path}') WHERE latitude is not null`;
if (selected_repo) {
query += " and repository=?";
params.push(selected_repo);
}
return await loadData(query, params, "loading_1");
}
pointdata = {
const colors = await makeColors();
//const query = `SELECT repository, imlgs_number, igsn, ship_platform, sampl_device, p_investigator, latitude, longitude FROM read_parquet('${parquet_path}') WHERE latitude is not null`;
const data_table = Inputs.table(recorddata, {rows: 100});
const scalar = new Cesium.NearFarScalar(1.5e2, 3, 8.0e6, 0.2);
const color = Cesium.Color.PINK;
const point_size = 4;
for (const row of recorddata) {
const pp = content.points.add({
id: row.imlgs_number,
// https://cesium.com/learn/cesiumjs/ref-doc/Cartesian3.html#.fromDegrees
position: Cesium.Cartesian3.fromDegrees(
row.longitude, //longitude
row.latitude, //latitude
0,//randomCoordinateJitter(10.0, 10.0), //elevation, m
),
pixelSize: point_size,
color: colors[row.repository],
scaleByDistance: scalar,
});
}
mutable num_records = recorddata.length;
content.enableTracking();
return data_table;
}
async function getRecord(pid) {
const query = `SELECT imlgs_number, igsn, repository, ship_platform, cruiseid, sampleid, sampl_device FROM read_parquet('${parquet_path}') WHERE imlgs_number=?`;
const row = await db.queryRow(query, [pid]);
if (row) {
return {
"IMLGS": row.imlgs_number,
"IGSN": row.igsn,
"Repository": row.repository,
"Platform": row.ship_platform,
"CruiseId": row.cruiseid,
"SampleId": row.sampleid,
"Device": row.sampl_device
}
}
return {};
};viewof parquet_path = Inputs.text({label:"Source", value:"https://s3.beehivebeach.com/imlgs/imlgs.parquet", width:"100%", submit:true});
data_loaded = {
if (num_records === 0) {
return "Loading ...";
}
return md`Retrieved ${num_records} locations from: [${parquet_path}](https://manzt.github.io/quak/?source=${parquet_path}).`;
}
// Import Observable's libraries
import {DuckDBClient} from "@observablehq/duckdb"
// Create a DuckDB instance
db = DuckDBClient.of();
async function loadData(query, params=[], waiting_id=null) {
// Get loading indicator
const waiter = document.getElementById(waiting_id);
if (waiter) {
waiter.hidden = false;
}
try {
// Run the (slow) query
console.log(query);
console.log(params);
//const _results = await db.query(query, ...params);
const _results = await db.query(query, params);
return _results;
} catch (error) {
if (waiter) {
waiter.innerHtml = `<pre>${error}</pre>`;
}
return null;
} finally {
// Hide the waiter (if there is one)
if (waiter) {
waiter.hidden = true;
}
}
}
async function makeColors() {
const query = `select distinct repository from read_parquet('${parquet_path}')`;
const results = await db.query(query);
const data = [[173, 216, 230],
[0, 191, 255],
[30, 144, 255],
[0, 0, 255],
[0, 0, 139],
[72, 61, 139],
[123, 104, 238],
[138, 43, 226],
[128, 0, 128],
[218, 112, 214],
[255, 0, 255],
[255, 20, 147],
[176, 48, 96],
[220, 20, 60],
[240, 128, 128],
[255, 69, 0],
[255, 165, 0],
[244, 164, 96],
[240, 230, 140],
[128, 128, 0],
[139, 69, 19],
[255, 255, 0],
[154, 205, 50],
[124, 252, 0],
[144, 238, 144],
[143, 188, 143],
[34, 139, 34],
[0, 255, 127],
[0, 255, 255],
[0, 139, 139],
[128, 128, 128],
[255, 255, 255]];
const colors = {};
let last_c = 0;
for (let row of results) {
const c = data[last_c];
last_c += 1;
colors[row.repository] = new Cesium.Color(c[0]/255, c[1]/255, c[2]/255, 0.9);
}
return colors;
}
mutable num_records = {
return 0;
}
class Overlay {
constructor(target, width="140px") {
this.data = {}
this.ele = document.createElement("div")
this.ele.setAttribute("class", "cesium-topleft");
this.ele.style.width = width;
const target_ele = document.getElementById(target);
const container = target_ele.querySelector(".cesium-widget");
container.insertBefore(this.ele, container.firstChild);
}
render() {
const rows=[];
if (this.data.longitude === ""){
rows.push(`<tr><td>XY</td><td></td></tr>`);
} else {
rows.push(`<tr><td>XY</td><td>${this.data.longitude}, ${this.data.latitude}</td></tr>`);
}
for (const [k,v] of Object.entries(this.data)){
if ((k === "latitude") || (k === "longitude")) {
continue;
}
rows.push(`<tr><td>${k}</td><td>${((v === null) ? "" : v)}</td></tr>`);
}
this.ele.innerHTML = `<table>${rows.join("\n")}</table>`;
}
update(data, do_render=true) {
for (const [key,value] of Object.entries(data)) {
this.data[key] = value;
}
if (do_render) {
this.render();
}
}
}
function createShowPrimitive(viewer) {
return async function(movement) {
// Get the point at the mouse end position
const selectPoint = viewer.viewer.scene.pick(movement.endPosition);
// Clear the current selection, if there is one and it is different to the selectPoint
if (viewer.currentSelection !== null) {
//console.log(`selected.p ${viewer.currentSelection}`)
if (Cesium.defined(selectPoint) && selectPoint !== viewer.currentSelection) {
console.log(`selected.p 2 ${viewer.currentSelection}`)
viewer.currentSelection.primitive.pixelSize = 4;
viewer.currentSelection.primitive.outlineColor = Cesium.Color.TRANSPARENT;
viewer.currentSelection.outlineWidth = 0;
viewer.currentSelection = null;
}
}
// If selectPoint is valid and no currently selected point
if (Cesium.defined(selectPoint) && selectPoint.hasOwnProperty("primitive")) {
//console.log(`showPrimitiveId ${selectPoint.id}`);
const carto = Cesium.Cartographic.fromCartesian(selectPoint.primitive.position)
viewer.pointLabel.position = selectPoint.primitive.position;
viewer.pointLabel.label.show = true;
//viewer.pointLabel.label.text = `id:${selectPoint.id}, ${carto}`;
viewer.pointLabel.label.text = `${selectPoint.id}`;
selectPoint.primitive.pixelSize = 20;
selectPoint.primitive.outlineColor = Cesium.Color.YELLOW;
selectPoint.primitive.outlineWidth = 3;
viewer.currentSelection = selectPoint;
//don't do this:
//getRecord(selectPoint.id).then((data) => {
// viewer.overlay.update(data);
//})
//or this:
//viewer.overlay.update(await getRecord(selectPoint.id));
// They are too slow. Either create a timeout for de-bouncing or use a click event.
} else {
viewer.pointLabel.label.show = false;
}
// Mouse coordinates
const ray = viewer.viewer.camera.getPickRay(movement.endPosition);
const mousePosition = viewer.viewer.scene.globe.pick(ray, viewer.viewer.scene);
if (Cesium.defined(mousePosition)) {
const cartographic = Cesium.Cartographic.fromCartesian(mousePosition);
viewer.overlay.update({
longitude: Cesium.Math.toDegrees(cartographic.longitude).toFixed(3),
latitude: Cesium.Math.toDegrees(cartographic.latitude).toFixed(3)
})
} else {
viewer.overlay.update({latitude:"", longitude:""});
}
}
}
class CView {
constructor(target) {
this.viewer = new Cesium.Viewer(
target, {
timeline: false,
animation: false,
baseLayerPicker: false,
fullscreenElement: target,
terrain: Cesium.Terrain.fromWorldTerrain()
});
this.overlay = new Overlay(target);
this.currentSelection = null;
this.point_size = 1;
this.n_points = 0;
// https://cesium.com/learn/cesiumjs/ref-doc/PointPrimitiveCollection.html
this.points = new Cesium.PointPrimitiveCollection();
this.viewer.scene.primitives.add(this.points);
this.pointLabel = this.viewer.entities.add({
label: {
show: false,
showBackground: true,
font: "14px monospace",
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(15, 0),
// this attribute will prevent this entity clipped by the terrain
disableDepthTestDistance: Number.POSITIVE_INFINITY,
text:"",
},
});
this.pickHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
// Can also do this rather than wait for the points to be generated
//this.pickHandler.setInputAction(createShowPrimitive(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.selectHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.selectHandler.setInputAction(async (e) => {
const selectPoint = this.viewer.scene.pick(e.position);
if (Cesium.defined(selectPoint) && selectPoint.hasOwnProperty("primitive")) {
this.overlay.update(await getRecord(selectPoint.id));
} else {
this.overlay.update(await getRecord(null));
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
enableTracking() {
this.pickHandler.setInputAction(createShowPrimitive(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
}
content = new CView("cesiumContainer");
pointdata;