@@ -47,14 +47,6 @@ viewof searchGeoPid = Inputs.text({
4747});
4848```
4949
50- ``` {ojs}
51- //| echo: fenced
52- viewof classifyDots = Inputs.button("Color-code by type (sample/site/both)", {
53- value: null,
54- reduce: () => Date.now()
55- })
56- ```
57-
5850::: {.callout-tip collapse="true"}
5951#### Using a local cached file for faster performance
6052
@@ -137,91 +129,69 @@ async function loadData(query, params = [], waiting_id = null, key = "default")
137129}
138130
139131locations = {
140- // Performance telemetry
141- performance.mark('locations-start');
142-
143- // Get loading indicator element for progress updates
144- const loadingDiv = document.getElementById('loading_1');
145- if (loadingDiv) {
146- loadingDiv.hidden = false;
147- loadingDiv.innerHTML = 'Loading geocodes...';
148- }
149-
150- // Fast query: just get all distinct geocodes (no classification!)
132+ // Get geographic locations with classification by usage type
151133 const query = `
152- SELECT DISTINCT
134+ WITH geo_classification AS (
135+ SELECT
136+ geo.pid,
137+ geo.latitude,
138+ geo.longitude,
139+ MAX(CASE WHEN e.p = 'sample_location' THEN 1 ELSE 0 END) as is_sample_location,
140+ MAX(CASE WHEN e.p = 'site_location' THEN 1 ELSE 0 END) as is_site_location
141+ FROM nodes geo
142+ JOIN nodes e ON (geo.row_id = e.o[1])
143+ WHERE geo.otype = 'GeospatialCoordLocation'
144+ GROUP BY geo.pid, geo.latitude, geo.longitude
145+ )
146+ SELECT
153147 pid,
154148 latitude,
155- longitude
156- FROM nodes
157- WHERE otype = 'GeospatialCoordLocation'
149+ longitude,
150+ CASE
151+ WHEN is_sample_location = 1 AND is_site_location = 1 THEN 'both'
152+ WHEN is_sample_location = 1 THEN 'sample_location_only'
153+ WHEN is_site_location = 1 THEN 'site_location_only'
154+ END as location_type
155+ FROM geo_classification
158156 `;
159-
160- performance.mark('query-start');
161157 const data = await loadData(query, [], "loading_1", "locations");
162- performance.mark('query-end');
163- performance.measure('locations-query', 'query-start', 'query-end');
164- const queryTime = performance.getEntriesByName('locations-query')[0].duration;
165- console.log(`Query executed in ${queryTime.toFixed(0)}ms - retrieved ${data.length} locations`);
166158
167159 // Clear the existing PointPrimitiveCollection
168160 content.points.removeAll();
169161
170- // Single color for all points (blue)
171- const defaultColor = Cesium.Color.fromCssColorString('#2E86AB');
172- const defaultSize = 4;
162+ // Color and size styling by location type
163+ const styles = {
164+ sample_location_only: {
165+ color: Cesium.Color.fromCssColorString('#2E86AB'),
166+ size: 3
167+ }, // Blue - field collection points
168+ site_location_only: {
169+ color: Cesium.Color.fromCssColorString('#A23B72'),
170+ size: 6
171+ }, // Purple - administrative markers
172+ both: {
173+ color: Cesium.Color.fromCssColorString('#F18F01'),
174+ size: 5
175+ } // Orange - dual-purpose
176+ };
173177
174- // Render points in chunks to keep UI responsive
175- const CHUNK_SIZE = 500;
178+ // Create point primitives for cesium display
176179 const scalar = new Cesium.NearFarScalar(1.5e2, 2, 8.0e6, 0.2);
177-
178- performance.mark('render-start');
179- for (let i = 0; i < data.length; i += CHUNK_SIZE) {
180- const chunk = data.slice(i, i + CHUNK_SIZE);
181- const endIdx = Math.min(i + CHUNK_SIZE, data.length);
182-
183- // Update progress indicator
184- if (loadingDiv) {
185- const pct = Math.round((endIdx / data.length) * 100);
186- loadingDiv.innerHTML = `Rendering geocodes... ${endIdx.toLocaleString()}/${data.length.toLocaleString()} (${pct}%)`;
187- }
188-
189- // Add points for this chunk
190- for (const row of chunk) {
191- content.points.add({
192- id: row.pid,
193- position: Cesium.Cartesian3.fromDegrees(
194- row.longitude, //longitude
195- row.latitude, //latitude
196- 0 //elevation, m
197- ),
198- pixelSize: defaultSize,
199- color: defaultColor,
200- scaleByDistance: scalar,
201- });
202- }
203-
204- // Yield to browser between chunks to keep UI responsive
205- if (i + CHUNK_SIZE < data.length) {
206- await new Promise(resolve => setTimeout(resolve, 0));
207- }
208- }
209- performance.mark('render-end');
210- performance.measure('locations-render', 'render-start', 'render-end');
211- const renderTime = performance.getEntriesByName('locations-render')[0].duration;
212-
213- // Hide loading indicator
214- if (loadingDiv) {
215- loadingDiv.hidden = true;
180+ for (const row of data) {
181+ const style = styles[row.location_type] || styles.both; // fallback to orange
182+ content.points.add({
183+ id: row.pid,
184+ // https://cesium.com/learn/cesiumjs/ref-doc/Cartesian3.html#.fromDegrees
185+ position: Cesium.Cartesian3.fromDegrees(
186+ row.longitude, //longitude
187+ row.latitude, //latitude
188+ 0,//randomCoordinateJitter(10.0, 10.0), //elevation, m
189+ ),
190+ pixelSize: style.size,
191+ color: style.color,
192+ scaleByDistance: scalar,
193+ });
216194 }
217-
218- performance.mark('locations-end');
219- performance.measure('locations-total', 'locations-start', 'locations-end');
220- const totalTime = performance.getEntriesByName('locations-total')[0].duration;
221-
222- console.log(`Rendering completed in ${renderTime.toFixed(0)}ms`);
223- console.log(`Total time (query + render): ${totalTime.toFixed(0)}ms`);
224-
225195 content.enableTracking();
226196 return data;
227197}
@@ -376,12 +346,7 @@ async function get_samples_1(pid) {
376346 AND geo.otype = 'GeospatialCoordLocation'
377347 ORDER BY has_thumbnail DESC
378348 `;
379- performance.mark('samples1-start');
380349 const result = await loadData(q, [pid], "loading_s1", "samples_1");
381- performance.mark('samples1-end');
382- performance.measure('samples1-query', 'samples1-start', 'samples1-end');
383- const queryTime = performance.getEntriesByName('samples1-query')[0].duration;
384- console.log(`Path 1 query executed in ${queryTime.toFixed(0)}ms - retrieved ${result?.length || 0} samples`);
385350 return result ?? [];
386351}
387352
@@ -438,12 +403,7 @@ async function get_samples_2(pid) {
438403 AND geo.otype = 'GeospatialCoordLocation'
439404 ORDER BY has_thumbnail DESC
440405 `;
441- performance.mark('samples2-start');
442406 const result = await loadData(q, [pid], "loading_s2", "samples_2");
443- performance.mark('samples2-end');
444- performance.measure('samples2-query', 'samples2-start', 'samples2-end');
445- const queryTime = performance.getEntriesByName('samples2-query')[0].duration;
446- console.log(`Path 2 query executed in ${queryTime.toFixed(0)}ms - retrieved ${result?.length || 0} samples`);
447407 return result ?? [];
448408}
449409
@@ -500,12 +460,7 @@ async function get_samples_at_geo_cord_location_via_sample_event(pid) {
500460 AND geo.otype = 'GeospatialCoordLocation'
501461 ORDER BY has_thumbnail DESC
502462 `;
503- performance.mark('eric-query-start');
504463 const result = await loadData(q, [pid], "loading_combined", "samples_combined");
505- performance.mark('eric-query-end');
506- performance.measure('eric-query', 'eric-query-start', 'eric-query-end');
507- const queryTime = performance.getEntriesByName('eric-query')[0].duration;
508- console.log(`Eric's query executed in ${queryTime.toFixed(0)}ms - retrieved ${result?.length || 0} samples`);
509464 return result ?? [];
510465}
511466
@@ -766,84 +721,6 @@ md`Retrieved ${pointdata.length} locations from ${parquet_path}.`;
766721}
767722```
768723
769- ``` {ojs}
770- //| echo: false
771- // Handle optional classification button: recolor dots by type
772- {
773- if (classifyDots !== null) {
774- console.log("Classifying dots by type...");
775- performance.mark('classify-start');
776-
777- // Run the classification query
778- const query = `
779- WITH geo_classification AS (
780- SELECT
781- geo.pid,
782- MAX(CASE WHEN e.p = 'sample_location' THEN 1 ELSE 0 END) as is_sample_location,
783- MAX(CASE WHEN e.p = 'site_location' THEN 1 ELSE 0 END) as is_site_location
784- FROM nodes geo
785- JOIN nodes e ON (geo.row_id = e.o[1])
786- WHERE geo.otype = 'GeospatialCoordLocation'
787- GROUP BY geo.pid
788- )
789- SELECT
790- pid,
791- CASE
792- WHEN is_sample_location = 1 AND is_site_location = 1 THEN 'both'
793- WHEN is_sample_location = 1 THEN 'sample_location_only'
794- WHEN is_site_location = 1 THEN 'site_location_only'
795- END as location_type
796- FROM geo_classification
797- `;
798-
799- const classifications = await db.query(query);
800-
801- // Build lookup map: pid -> location_type
802- const typeMap = new Map();
803- for (const row of classifications) {
804- typeMap.set(row.pid, row.location_type);
805- }
806-
807- // Color and size styling by location type
808- const styles = {
809- sample_location_only: {
810- color: Cesium.Color.fromCssColorString('#2E86AB'),
811- size: 3
812- }, // Blue - field collection points
813- site_location_only: {
814- color: Cesium.Color.fromCssColorString('#A23B72'),
815- size: 6
816- }, // Purple - administrative markers
817- both: {
818- color: Cesium.Color.fromCssColorString('#F18F01'),
819- size: 5
820- } // Orange - dual-purpose
821- };
822-
823- // Update colors of existing points
824- const points = content.points;
825- for (let i = 0; i < points.length; i++) {
826- const point = points.get(i);
827- const pid = point.id;
828- const locationType = typeMap.get(pid);
829-
830- if (locationType && styles[locationType]) {
831- point.color = styles[locationType].color;
832- point.pixelSize = styles[locationType].size;
833- }
834- }
835-
836- performance.mark('classify-end');
837- performance.measure('classification', 'classify-start', 'classify-end');
838- const classifyTime = performance.getEntriesByName('classification')[0].duration;
839- console.log(`Classification completed in ${classifyTime.toFixed(0)}ms - updated ${points.length} points`);
840- console.log(` - Blue (sample_location_only): field collection points`);
841- console.log(` - Purple (site_location_only): administrative markers`);
842- console.log(` - Orange (both): dual-purpose locations`);
843- }
844- }
845- ```
846-
847724::: {.panel-tabset}
848725
849726## Map
0 commit comments