11<script setup>
2- import { ref , onMounted , onUnmounted , watch } from ' vue'
2+ import { ref , reactive , computed , onMounted , onUnmounted , watch } from ' vue'
33import { ylOrBrRgba , processFcForGlobe } from ' ../utils/globeUtils.js'
44
55const props = defineProps ({
@@ -51,6 +51,26 @@ const ctrlMultiRes = ref(false)
5151const ctrlZoomOffset = ref (0 )
5252const showAperture = ref (props .initialTopology === ' HEXAGON' )
5353
54+ // --- hierarchy selection state ---
55+ const selectedCellId = ref (null )
56+ const selectedCellRes = ref (null )
57+ const hierarchyInfo = reactive ({
58+ parent: null ,
59+ children: [],
60+ neighbors: [],
61+ })
62+ const ctrlIndexType = ref (' SEQNUM' )
63+ const availableIndexTypes = computed (() => {
64+ const types = [' SEQNUM' ]
65+ if (ctrlTopology .value === ' HEXAGON' ) {
66+ types .push (' VERTEX2DD' )
67+ if (ctrlAperture .value !== 7 ) types .push (' ZORDER' )
68+ if (ctrlAperture .value === 3 ) types .push (' Z3' )
69+ if (ctrlAperture .value === 7 ) types .push (' Z7' )
70+ }
71+ return types
72+ })
73+
5474// --- non-reactive map state (reactivity on MapLibre objects causes issues) ---
5575let map = null
5676let deckOverlay = null
@@ -119,7 +139,15 @@ function makeCellLayer(id, fc, colored, isBase) {
119139 }
120140 },
121141 onClick: isBase || ! props .showControls ? undefined : (info ) => {
122- if (info .object ) console .log (' Cell geometry:' , info .object .geometry )
142+ if (info .object ) {
143+ const cellId = info .object .properties ? .id ?? info .object .id
144+ if (cellId != null ) {
145+ const resolution = ctrlMultiRes .value
146+ ? zoomToResolution (map .getZoom (), ctrlResolution .value )
147+ : ctrlResolution .value
148+ selectCell (cellId, resolution)
149+ }
150+ }
123151 },
124152 updateTriggers: { getFillColor: [colored] },
125153 })
@@ -293,6 +321,198 @@ function onMoveEnd() {
293321 multiResTimer = setTimeout (generateGrid, 300 )
294322}
295323
324+ // ---------------------------------------------------------------------------
325+ // Hierarchy selection
326+ // ---------------------------------------------------------------------------
327+
328+ function getCellIndexLabel (seqnum , resolution ) {
329+ try {
330+ switch (ctrlIndexType .value ) {
331+ case ' SEQNUM' : return seqnum .toString ()
332+ case ' VERTEX2DD' : {
333+ const v = webdggrid .sequenceNumToVertex2DD (seqnum, resolution)
334+ return ` v${ v .vertNum } t${ v .triNum } `
335+ }
336+ case ' ZORDER' : return webdggrid .sequenceNumToZOrder (seqnum, resolution).toString ()
337+ case ' Z3' : return webdggrid .sequenceNumToZ3 (seqnum, resolution).toString ()
338+ case ' Z7' : return webdggrid .sequenceNumToZ7 (seqnum, resolution).toString ()
339+ default : return seqnum .toString ()
340+ }
341+ } catch {
342+ return seqnum .toString ()
343+ }
344+ }
345+
346+ function selectCell (cellId , resolution ) {
347+ if (! webdggrid) return
348+
349+ const seqnum = typeof cellId === ' bigint' ? cellId : BigInt (cellId)
350+ selectedCellId .value = seqnum
351+ selectedCellRes .value = resolution
352+
353+ // Get hierarchical relationships
354+ try {
355+ hierarchyInfo .neighbors = webdggrid .sequenceNumNeighbors ([seqnum], resolution)[0 ]
356+ } catch { hierarchyInfo .neighbors = [] }
357+ try {
358+ hierarchyInfo .parent = resolution > 0 ? webdggrid .sequenceNumParent ([seqnum], resolution)[0 ] : null
359+ } catch { hierarchyInfo .parent = null }
360+ try {
361+ hierarchyInfo .children = webdggrid .sequenceNumChildren ([seqnum], resolution)[0 ]
362+ } catch { hierarchyInfo .children = [] }
363+
364+ updateHierarchyLayers (seqnum, resolution)
365+ }
366+
367+ function clearSelection () {
368+ selectedCellId .value = null
369+ selectedCellRes .value = null
370+ hierarchyInfo .parent = null
371+ hierarchyInfo .children = []
372+ hierarchyInfo .neighbors = []
373+ // Remove hierarchy layers, keep grid layers
374+ if (deckOverlay && lastFineFc) {
375+ updateDeckLayers (lastFineFc, lastBaseFc)
376+ }
377+ }
378+
379+ function updateHierarchyLayers (seqnum , resolution ) {
380+ if (! deckOverlay) return
381+
382+ const { GeoJsonLayer , TextLayer } = window .deck
383+ const colored = ctrlColorCells .value
384+ const layers = []
385+
386+ // Base grid layers
387+ if (lastBaseFc) layers .push (makeCellLayer (' dggrid-base' , lastBaseFc, false , true ))
388+ if (lastFineFc) layers .push (makeCellLayer (' dggrid-cells' , lastFineFc, colored, false ))
389+
390+ // Parent cell layer
391+ if (hierarchyInfo .parent !== null && resolution > 0 ) {
392+ try {
393+ const parentFc = webdggrid .sequenceNumToGridFeatureCollection ([hierarchyInfo .parent ], resolution - 1 )
394+ parentFc .features .forEach (f => {
395+ if (typeof f .id === ' bigint' ) f .id = f .id .toString ()
396+ if (f .properties ) for (const k of Object .keys (f .properties )) if (typeof f .properties [k] === ' bigint' ) f .properties [k] = f .properties [k].toString ()
397+ })
398+ layers .push (new GeoJsonLayer ({
399+ id: ' hierarchy-parent' ,
400+ data: processFcForGlobe (parentFc),
401+ filled: true ,
402+ stroked: true ,
403+ wrapLongitude: true ,
404+ getFillColor: [51 , 204 , 51 , 50 ],
405+ getLineColor: [51 , 204 , 51 , 220 ],
406+ getLineWidth: 3 ,
407+ lineWidthUnits: ' pixels' ,
408+ }))
409+ } catch { /* skip */ }
410+ }
411+
412+ // Children layer
413+ if (hierarchyInfo .children .length > 0 ) {
414+ try {
415+ const childFc = webdggrid .sequenceNumToGridFeatureCollection (hierarchyInfo .children , resolution + 1 )
416+ childFc .features .forEach (f => {
417+ if (typeof f .id === ' bigint' ) f .id = f .id .toString ()
418+ if (f .properties ) for (const k of Object .keys (f .properties )) if (typeof f .properties [k] === ' bigint' ) f .properties [k] = f .properties [k].toString ()
419+ })
420+ layers .push (new GeoJsonLayer ({
421+ id: ' hierarchy-children' ,
422+ data: processFcForGlobe (childFc),
423+ filled: true ,
424+ stroked: true ,
425+ wrapLongitude: true ,
426+ getFillColor: [255 , 204 , 0 , 100 ],
427+ getLineColor: [204 , 153 , 0 , 220 ],
428+ getLineWidth: 2 ,
429+ lineWidthUnits: ' pixels' ,
430+ }))
431+ } catch { /* skip */ }
432+ }
433+
434+ // Selected cell highlight
435+ try {
436+ const centerFc = webdggrid .sequenceNumToGridFeatureCollection ([seqnum], resolution)
437+ centerFc .features .forEach (f => {
438+ if (typeof f .id === ' bigint' ) f .id = f .id .toString ()
439+ if (f .properties ) for (const k of Object .keys (f .properties )) if (typeof f .properties [k] === ' bigint' ) f .properties [k] = f .properties [k].toString ()
440+ })
441+ layers .push (new GeoJsonLayer ({
442+ id: ' hierarchy-selected' ,
443+ data: processFcForGlobe (centerFc),
444+ filled: true ,
445+ stroked: true ,
446+ wrapLongitude: true ,
447+ getFillColor: [255 , 51 , 51 , 80 ],
448+ getLineColor: [255 , 51 , 51 , 255 ],
449+ getLineWidth: 4 ,
450+ lineWidthUnits: ' pixels' ,
451+ }))
452+ } catch { /* skip */ }
453+
454+ // --- Text labels for all hierarchical cells ---
455+ const labelData = []
456+
457+ // Selected cell label
458+ try {
459+ const geo = webdggrid .sequenceNumToGeo ([seqnum], resolution)[0 ]
460+ labelData .push ({
461+ position: [geo[0 ], geo[1 ]],
462+ text: getCellIndexLabel (seqnum, resolution),
463+ color: [255 , 51 , 51 ],
464+ size: 14 ,
465+ })
466+ } catch { /* skip */ }
467+
468+ // Parent label
469+ if (hierarchyInfo .parent !== null && resolution > 0 ) {
470+ try {
471+ const geo = webdggrid .sequenceNumToGeo ([hierarchyInfo .parent ], resolution - 1 )[0 ]
472+ labelData .push ({
473+ position: [geo[0 ], geo[1 ]],
474+ text: getCellIndexLabel (hierarchyInfo .parent , resolution - 1 ),
475+ color: [30 , 150 , 30 ],
476+ size: 12 ,
477+ })
478+ } catch { /* skip */ }
479+ }
480+
481+ // Children labels
482+ for (const child of hierarchyInfo .children ) {
483+ try {
484+ const geo = webdggrid .sequenceNumToGeo ([child], resolution + 1 )[0 ]
485+ labelData .push ({
486+ position: [geo[0 ], geo[1 ]],
487+ text: getCellIndexLabel (child, resolution + 1 ),
488+ color: [180 , 130 , 0 ],
489+ size: 10 ,
490+ })
491+ } catch { /* skip */ }
492+ }
493+
494+ if (labelData .length > 0 ) {
495+ layers .push (new TextLayer ({
496+ id: ' hierarchy-labels' ,
497+ data: labelData,
498+ getPosition : d => d .position ,
499+ getText : d => d .text ,
500+ getColor : d => [... d .color , 255 ],
501+ getSize : d => d .size ,
502+ getTextAnchor: ' middle' ,
503+ getAlignmentBaseline: ' center' ,
504+ fontFamily: ' monospace' ,
505+ fontWeight: 700 ,
506+ outlineWidth: 3 ,
507+ outlineColor: [255 , 255 , 255 , 220 ],
508+ billboard: true ,
509+ sizeUnits: ' pixels' ,
510+ }))
511+ }
512+
513+ deckOverlay .setProps ({ layers })
514+ }
515+
296516// ---------------------------------------------------------------------------
297517// Lifecycle
298518// ---------------------------------------------------------------------------
@@ -313,7 +533,7 @@ onMounted(async () => {
313533 loadScript (' https://unpkg.com/deck.gl@9/dist.min.js' ),
314534 ])
315535
316- const { Webdggrid } = await new Function ( ' return import("https://cdn.jsdelivr.net/npm/ webdggrid/dist/index.js") ' )( )
536+ const { Webdggrid } = await import (' webdggrid' )
317537
318538 const isDark = document .documentElement .classList .contains (' dark' )
319539
0 commit comments