// Panels: left (layers), right (inspector), and inline UI atoms
const { useState: useStateP } = React;
// Small icon set (line SVGs)
function I({ name, size = 16 }) {
const s = { width: size, height: size };
const common = { fill: 'none', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' };
switch(name) {
case 'select': return ;
case 'pan': return ;
case 'measure': return ;
case 'draw': return ;
case 'polygon': return ;
case 'edit': return ;
case 'query': return ;
case 'buffer': return ;
case 'ai': return ;
case '3d': return ;
case 'layer': return ;
case 'route': return ;
case 'sat': return ;
case 'drone': return ;
case 'download': return ;
case 'upload': return ;
case 'chart': return ;
case 'save': return ;
case 'print': return ;
case 'settings': return ;
case 'compass': return ;
case 'grid': return ;
default: return null;
}
}
// ---------- Ribbon ----------
function Ribbon({ activeTool, setActiveTool }) {
const groups = [
{
label: 'File',
tools: [
{ id: 'open', icon: 'upload', label: 'Open' },
{ id: 'save', icon: 'save', label: 'Save' },
{ id: 'export', icon: 'download', label: 'Export' },
{ id: 'print', icon: 'print', label: 'Print' },
]
},
{
label: 'Navigate',
tools: [
{ id: 'select', icon: 'select', label: 'Select' },
{ id: 'pan', icon: 'pan', label: 'Pan' },
{ id: 'zoom', icon: 'query', label: 'Zoom' },
{ id: 'measure',icon: 'measure', label: 'Measure' },
]
},
{
label: 'Edit',
tools: [
{ id: 'draw', icon: 'draw', label: 'Draw' },
{ id: 'polygon',icon: 'polygon', label: 'Polygon' },
{ id: 'vertex', icon: 'edit', label: 'Vertex' },
]
},
{
label: 'Analyze',
tools: [
{ id: 'buffer', icon: 'buffer', label: 'Buffer' },
{ id: 'overlay', icon: 'layer', label: 'Overlay' },
{ id: 'route', icon: 'route', label: 'Route' },
{ id: 'chart', icon: 'chart', label: 'Charts' },
]
},
{
label: 'AI / Remote Sensing',
tools: [
{ id: 'aidet', icon: 'ai', label: 'Detect' },
{ id: 'segm', icon: 'grid', label: 'Segment' },
{ id: 'sat', icon: 'sat', label: 'Imagery' },
{ id: 'drone', icon: 'drone', label: 'Drone' },
]
},
{
label: '3D & Twin',
tools: [
{ id: '3d', icon: '3d', label: '3D View' },
{ id: 'globe', icon: 'compass', label: 'Globe' },
]
},
{
label: 'System',
tools: [
{ id: 'settings', icon: 'settings', label: 'Settings' },
]
},
];
return (
{groups.map(g => (
{g.tools.map(t => (
))}
{g.label}
))}
);
}
// ---------- Left Panel — Layers ----------
function LayersPanel({ layers, setLayers }) {
const [tab, setTab] = useStateP('layers');
const toggle = (id) => setLayers(prev => ({ ...prev, [id]: { ...prev[id], on: !prev[id].on }}));
const groups = [
{
label: 'AI / Analysis',
layers: [
{ id: 'ai', name: 'AI Detections', swatch: '#d97706', count: 8, hatch: true },
{ id: 'buffer', name: 'Buffer 5/10/15km', swatch: '#e8a33d', count: 3 },
{ id: 'routes', name: 'Optimized Routes', swatch: '#d97706', count: 2 },
{ id: 'heatmap', name: 'Population Heatmap', swatch: '#c94a3a', count: '—' },
]
},
{
label: 'Vector',
layers: [
{ id: 'assets', name: 'Utility Assets', swatch: '#2f7d4f', count: 7 },
{ id: 'cities', name: 'Cities & Towns', swatch: '#0a1a35', count: 20 },
{ id: 'labels', name: 'Place Labels', swatch: 'transparent', count: '—' },
{ id: 'roads', name: 'National Highways', swatch: '#c94a3a', count: 15 },
{ id: 'rivers', name: 'Rivers & Streams', swatch: '#4a90c8', count: 8 },
]
},
{
label: 'Boundaries',
layers: [
{ id: 'districts', name: 'District Boundaries', swatch: '#7a8a68', count: 776 },
{ id: 'states', name: 'State Boundaries', swatch: '#3d5978', count: 36 },
]
},
{
label: 'Reference',
layers: [
{ id: 'graticule', name: 'Lat/Lon Graticule', swatch: '#a8b8c8', count: '—' },
]
}
];
return (
{tab === 'layers' && (
<>
Project
SAVED
Karnataka LIS Master
KA-LIS-2026-Q2.bcgp • v3.4.1
{groups.map(g => (
{g.label}
{g.layers.length} layers
{g.layers.map(l => (
toggle(l.id)}>
{l.name}
{l.count}
))}
))}
>
)}
{tab === 'data' && (
Connected Sources
{[
{ name: 'PostGIS · ka_lis_prod', kind: 'PostgreSQL 16 · PostGIS 3.4', st: 'ok', ct: 24 },
{ name: 'GeoServer · WMS/WFS', kind: 'geoserver.bhoochitra.gov.in', st: 'ok', ct: 42 },
{ name: 'S3 · sentinel-imagery', kind: 'GeoTIFF · 4.2 TB', st: 'ok', ct: 1204 },
{ name: 'Oracle Spatial · legacy',kind: 'Read-only replication', st: 'warn', ct: 8 },
{ name: 'ISRO Bhuvan · WMTS', kind: 'External tile service', st: 'ok', ct: '—' },
].map((d, i) => (
{d.st.toUpperCase()}
{d.name}
{d.ct}
{d.kind}
))}
)}
{tab === 'book' && (
Saved Views
{[
{ name: 'National Overview', s: '1:12,000,000', by: 'default' },
{ name: 'NCR — Encroachment Scan', s: '1:80,000', by: 'ai.workflow' },
{ name: 'Krishna Basin', s: '1:1,200,000', by: 'hydro.team' },
{ name: 'Mumbai Metro Utilities', s: '1:20,000', by: 'utility.mh' },
{ name: 'NE Forest Cover', s: '1:800,000', by: 'forest.dept' },
].map((b,i) => (
))}
)}
);
}
// ---------- Right Panel — Inspector ----------
function InspectorPanel({ selected, activeTool }) {
const [tab, setTab] = useStateP('attr');
const renderAttributes = () => {
if (!selected) {
return (
No feature selected
Use the Select tool on the map — click a city, asset, AI detection, or state to inspect its attributes.
);
}
if (selected.kind === 'city') {
const c = selected.data;
return (
<>
Populated Place · Tier {c.tier === 'metro' ? '01' : '02'}
{c.name}
FID · {c.id.toUpperCase()}-1832 · CRS EPSG:4326
{[
['Population', c.pop],
['State', c.state],
['Category', c.tier === 'metro' ? 'Metropolitan' : 'Tier-I City'],
['Class', 'Municipal Corporation'],
['Latitude', approxLatLon(c).lat.toFixed(5) + '° N'],
['Longitude', approxLatLon(c).lon.toFixed(5) + '° E'],
['Elevation (SRTM)', Math.round(60 + Math.random()*300) + ' m'],
['Land Area', (300 + Math.random()*900).toFixed(1) + ' km²'],
['Last Updated', '2026-06-12'],
['Source', 'Census + ULB verified'],
].map(([k,v]) => (
))}
Tags
SMART_CITY
ULB
AMRUT
>
);
}
if (selected.kind === 'ai') {
const d = selected.data;
const sev = d.conf > 0.9 ? 'HIGH' : d.conf > 0.85 ? 'MED' : 'LOW';
return (
<>
AI Detection · GeoAI v2.7
{d.kind}
DET-{d.id.toUpperCase()}-{Math.floor(Math.random()*9999).toString().padStart(4,'0')}
Confidence
{(d.conf*100).toFixed(1)}%
Source Imagery
Sentinel-2 · 2026-07-11
Ground Truth
Pending field verification
Assigned
field.officer.mh_2
Detection Bounds
{d.x-d.w/2},{d.y-d.h/2} → {d.x+d.w/2},{d.y+d.h/2}
>
);
}
if (selected.kind === 'asset') {
const a = selected.data;
return (
<>
Utility Asset
{a.kind}
AST-{a.id.toUpperCase()}-91024
Status
{a.status.toUpperCase()}
Last Inspection
2026-05-22
>
);
}
if (selected.kind === 'state') {
return (
<>
Administrative · Level 1
{selected.name}
STATE-{selected.id.toUpperCase()} · IN-{selected.id.toUpperCase()}
Feature Type
State / UT Polygon
Vertices
{Math.floor(2000 + Math.random()*8000)}
Last Edit
2026-04-18 14:22
>
);
}
return null;
};
return (
{tab === 'attr' && renderAttributes()}
{tab === 'ana' && (
<>
Features Visible
1,284,441
+18,240 this week
Selected
{selected ? '1' : '0'}
—
Basemap Cache
2.1 GB
Offline ready
Active Analysis
{[
{ name: 'Buffer 15 km @ Delhi', status: 'DONE', kind: 'proximity' },
{ name: 'Overlay: LULC × Parcels',status: 'RUN', kind: 'overlay' },
{ name: 'Hotspot: Encroachment', status: 'DONE', kind: 'hotspot' },
{ name: 'Route: Del → Kol', status: 'DONE', kind: 'route' },
{ name: 'NDVI Δ 2024 → 2026', status: 'RUN', kind: 'raster' },
].map((r,i) => (
{r.status}
{r.name}
{r.kind}
))}
Bar · Detections by class
>
)}
{tab === 'ai' && (
<>
GeoAI Model Queue
4 RUNNING
{[
{ name: 'YOLOv8 · Building Detection', pct: 82, area: 'Karnataka' },
{ name: 'U-Net · Land Cover Classify', pct: 47, area: 'Krishna Basin' },
{ name: 'SAM · Field Segmentation', pct: 96, area: 'Punjab Kharif' },
{ name: 'LSTM · Flood Forecast 7d', pct: 34, area: 'Assam' },
{ name: 'Change Detection · Δ2024', pct: 100,area: 'Odisha Coast' },
].map((m,i) => (
{m.name}
{m.pct}%
Region · {m.area}
))}
Prediction · Flood Risk (7d)
Jul 15peak: Jul 28Jul 31
Available Models
{['building.det','road.seg','water.det','crop.seg','forest.mon','mining.det','encr.det','fire.det','flood.pred','landslide.pred','ndvi.calc','change.det','traffic.ana','geoai.gen'].map(m => (
{m}
))}
>
)}
);
}
// ---------- Small chart atoms ----------
function BarChart({ data }) {
const max = Math.max(...data.map(d => d.v));
return (
);
}
function Sparkline({ values, color }) {
const w = 280, h = 44;
const max = Math.max(...values), min = Math.min(...values);
const step = w / (values.length - 1);
const norm = (v) => h - ((v - min) / (max - min || 1)) * (h - 4) - 2;
const path = values.map((v,i) => `${i===0?'M':'L'}${i*step},${norm(v)}`).join(' ');
const area = path + ` L${w},${h} L0,${h} Z`;
return (
);
}
// Approximate SVG coord → lat/lon
function approxLatLon(c) {
const lon = 68 + (c.x / 612) * (97 - 68);
const lat = 37 - (c.y / 696) * (37 - 8);
return { lat, lon };
}
Object.assign(window, { Ribbon, LayersPanel, InspectorPanel, I, BarChart, Sparkline, approxLatLon });