Skip to content

Commit 5384c1e

Browse files
committed
feat: enhance UI with theme-aware address highlighting and improve layout padding
1 parent 1b111bb commit 5384c1e

File tree

4 files changed

+278
-12
lines changed

4 files changed

+278
-12
lines changed

docs/.vitepress/theme/components/DggsAddressTypesDemo.vue

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ function loadScript(src) {
770770
border-bottom: 1px solid var(--vp-c-divider, #e2e2e3);
771771
}
772772
.conv-table td {
773-
padding: 6px 8px 6px 0;
773+
padding: 6px 8px 6px 10px;
774774
border-bottom: 1px solid var(--vp-c-divider, #eee);
775775
}
776776
.type-label {
@@ -921,10 +921,10 @@ function loadScript(src) {
921921
font-size: 12px;
922922
margin-top: 4px;
923923
}
924-
.children-table th {
925-
text-align: left;
926-
font-size: 10px;
927-
font-weight: 600;
924+
.children-table td {
925+
padding: 6px 8px 6px 10px;
926+
border-bottom: 1px solid var(--vp-c-divider, #eee);
927+
}
928928
color: var(--vp-c-text-3, #999);
929929
text-transform: uppercase;
930930
letter-spacing: 0.3px;
@@ -948,9 +948,17 @@ function loadScript(src) {
948948
}
949949
.zorder-center {
950950
background: #e8f4fd;
951+
color: #222;
952+
}
953+
@media (prefers-color-scheme: dark) {
954+
.zorder-center {
955+
background: #23405a;
956+
color: #fff;
957+
}
951958
}
952959
.zorder-center td {
953960
font-weight: 600;
961+
padding-left: 10px;
954962
}
955963
956964
/* Prefix bar */

docs/.vitepress/theme/components/DggsD3HierarchyDemo.vue

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ function loadScript(src) {
461461
<div class="section-title">Address Conversions</div>
462462
<table class="addr-table">
463463
<tbody>
464-
<tr>
464+
<tr class="addr-highlight">
465465
<td class="addr-label">SEQNUM</td>
466466
<td class="addr-value">{{ state.selectedCell.toString() }}</td>
467467
<td class="addr-label">VERTEX2DD</td>
@@ -666,7 +666,7 @@ function loadScript(src) {
666666
font-family: var(--vp-font-family-mono, monospace);
667667
}
668668
.addr-table td {
669-
padding: 4px 12px 4px 0;
669+
padding: 4px 12px 4px 10px;
670670
vertical-align: top;
671671
}
672672
.addr-label {
@@ -682,3 +682,14 @@ function loadScript(src) {
682682
padding-right: 24px;
683683
}
684684
</style>
685+
/* Highlighted address row for ZORDER table (theme-aware) */
686+
.addr-highlight {
687+
background: var(--vp-c-brand-soft, #e0f0ff);
688+
color: var(--vp-c-text-1, #222);
689+
}
690+
@media (prefers-color-scheme: dark) {
691+
.addr-highlight {
692+
background: #23405a;
693+
color: #fff;
694+
}
695+
}

docs/.vitepress/theme/components/DggsGlobe.vue

Lines changed: 223 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup>
2-
import { ref, onMounted, onUnmounted, watch } from 'vue'
2+
import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
33
import { ylOrBrRgba, processFcForGlobe } from '../utils/globeUtils.js'
44
55
const props = defineProps({
@@ -51,6 +51,26 @@ const ctrlMultiRes = ref(false)
5151
const ctrlZoomOffset = ref(0)
5252
const 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) ---
5575
let map = null
5676
let 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

readme.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,35 @@ const seqNum = dggs.geoToSequenceNum([[0, 0]]);
6161
6262
## API
6363
64-
### Core Methods
65-
`setDggs` · `getResolution` · `setResolution` · `geoToSequenceNum` · `sequenceNumToGeo` · `sequenceNumToGrid` · `sequenceNumToGridFeatureCollection` · `geoToGeo` · `cellAreaKM` · `cellDistKM` · `nCells`
64+
### Lifecycle
65+
`Webdggrid.load` · `Webdggrid.unload`
66+
67+
### Configuration
68+
`setDggs` · `getResolution` · `setResolution` · `version`
69+
70+
### Coordinate Conversion
71+
`geoToSequenceNum` · `sequenceNumToGeo` · `geoToGeo` · `sequenceNumToGrid` · `sequenceNumToGridFeatureCollection`
72+
73+
### Grid Statistics
74+
`nCells` · `cellAreaKM` · `cellDistKM` · `gridStatCLS`
75+
76+
### Hierarchical Operations
77+
`sequenceNumNeighbors` · `sequenceNumParent` · `sequenceNumChildren`
78+
79+
### Hierarchical Address Types
80+
`sequenceNumToVertex2DD` · `vertex2DDToSequenceNum` · `sequenceNumToZOrder` · `zOrderToSequenceNum` · `sequenceNumToZ3` · `z3ToSequenceNum` · `sequenceNumToZ7` · `z7ToSequenceNum`
81+
82+
### Low-Level Coordinate Systems
83+
`geoToPlane` · `geoToProjtri` · `geoToQ2dd` · `geoToQ2di` · `sequenceNumToPlane` · `sequenceNumToProjtri` · `sequenceNumToQ2dd` · `sequenceNumToQ2di`
84+
85+
### Q2DI Conversions
86+
`q2diToGeo` · `q2diToSequenceNum` · `q2diToPlane` · `q2diToProjtri` · `q2diToQ2dd`
87+
88+
### Q2DD Conversions
89+
`q2ddToGeo` · `q2ddToSequenceNum` · `q2ddToPlane` · `q2ddToProjtri` · `q2ddToQ2di`
90+
91+
### PROJTRI Conversions
92+
`projtriToGeo` · `projtriToSequenceNum` · `projtriToPlane` · `projtriToQ2dd` · `projtriToQ2di`
6693
6794
### Multi-Aperture Support
6895

0 commit comments

Comments
 (0)