developmentseed / developmentseed/cng-sandbox
feat: dimension controls UI for multidim datasets in map view
- Dominant language
- TypeScript
- Stars
- 3
- Forks
- 0
- Avg merge
- 2h 21m
- Merged PRs (30d)
- 3
Description
## Summary
Add interactive dimension selection controls to the map sidebar for multidimensional datasets. Users can pick a variable, time step, and values for any extra dimensions (pest, driver, depth, etc.) and see the map update in real-time. This is the frontend for #113.
## Context
When a multidimensional dataset is loaded in the map view, the user needs controls to select which 2D slice to visualize. The titiler-multidim tiler accepts dimension values as query params on the tile URL:
```
/multidim/tiles/{z}/{x}/{y}?url={s3_url}&variable=temperature&time=2024-01-01&depth=100
```
Changing any dimension value reconstructs the tile URL and the map re-fetches tiles for the new slice. This is similar to how band selection already works in `RasterSidebarControls.tsx`.
## Prerequisites
- Multidim tiler running and proxied (infrastructure issue)
- Dataset model has `is_multidim`, `dimensions`, `multidim_variables`, `raw_file_url` (scanner issue)
- Multidim pipeline persists these fields (pipeline issue)
## Tasks
### 1. Create `frontend/src/hooks/useMultidimControls.ts`
State management hook for dimension selections:
```typescript
interface MultidimControlsReturn {
selectedVariable: string;
setSelectedVariable: (v: string) => void;
dimensionValues: Record; // dim name -> selected value
setDimensionValue: (dim: string, value: string) => void;
tileUrl: string; // fully constructed tile URL with all params
}
export function useMultidimControls(item: MapItem | null): MultidimControlsReturn
```
Behavior:
- Initialize defaults: first variable, first value for each dimension
- When `item` changes (different dataset selected), reset to defaults
- Construct tile URL: start with the dataset's base `tileUrl`, append `&variable={var}` and `&{dim}={val}` for each dimension
- The colormap param is handled separately by existing controls and appended at the layer builder level
### 2. Create `frontend/src/components/DimensionControls.tsx`
UI component rendering dimension selectors:
```typescript
interface DimensionControlsProps {
variables: string[];
dimensions: DimensionInfo[];
selectedVariable: string;
onVariableChange: (v: string) => void;
dimensionValues: Record;
onDimensionChange: (dim: string, value: string) => void;
}
```
Layout (follow existing sidebar patterns from `RasterSidebarControls.tsx`):
- **Variable selector**: Chakra `NativeSelect` dropdown listing all `multidim_variables`
- **Dimension selectors**: One control per dimension entry:
- If dimension has `values` (string labels): `NativeSelect` dropdown
- If dimension has no values (index-only): `NativeSelect` with indices 0..size-1
- Special case for time dimensions: could use a slider like `TemporalControls`, but a dropdown is fine for v1
- Each selector has a label showing the dimension name
- Use the same spacing and typography as existing sidebar controls
### 3. Integrate into `MapPage.tsx`
**File**: `frontend/src/pages/MapPage.tsx`
- Import and call `useMultidimControls(activeItem)`
- Pass the hook's `tileUrl` to the layer builder (new prop/option)
- Pass dimension control props to `MapSidePanel`
- For multidim datasets: suppress `TemporalControls` (time is handled by dimension controls instead)
### 4. Update `MapSidePanel.tsx`
**File**: `frontend/src/components/MapSidePanel.tsx`
Conditionally render `DimensionControls` when the active item is a multidim dataset:
```tsx
{item.isMultidim && (
)}
```
Position: above or below the existing raster controls (colormap, opacity). The dimension controls are the primary interaction for multidim datasets, so they should be prominent.
### 5. Update `useLayerBuilder.ts`
**File**: `frontend/src/hooks/useLayerBuilder.ts`
Add a multidim code path. When the dataset is multidim, use the dynamic tile URL from `useMultidimControls` instead of the static `tile_url` from the dataset:
```typescript
if (item.isMultidim && multidimTileUrl) {
// Build raster tile layer using the multidim tile URL
// Append colormap params as usual
return buildRasterLayer({
tileUrl: multidimTileUrl,
colormap,
opacity,
// ... other existing params
});
}
```
### 6. Update `useMapData.ts`
**File**: `frontend/src/hooks/useMapData.ts`
Map the new fields in `datasetToMapItem`:
```typescript
isMultidim: ds.is_multidim ?? false,
dimensions: ds.dimensions ?? [],
multidimVariables: ds.multidim_variables ?? [],
rawFileUrl: ds.raw_file_url ?? null,
```
## Verification
- [ ] Open a multidim dataset in map view → dimension dropdowns appear in sidebar
- [ ] Variable dropdown lists all variables from the dataset
- [ ] Each extra dimension has its own dropdown with correct values
- [ ] Changing variable → map tiles reload with new variable data
- [ ] Changing any dimension value → map tiles reload with new slice
- [ ] Colormap selector still works (param appended to tile URL)
- [ ] Opacity slider still works
- [ ] Opening a non-multidim dataset → no dimension controls shown (existing UI unchanged)
- [ ] Frontend compiles without errors (`cd frontend && npx tsc --noEmit`)
- [ ] Screenshot the sidebar with dimension controls to verify layout
## Files to create/modify
| File | Action |
|------|--------|
| `frontend/src/hooks/useMultidimControls.ts` | **Create** |
| `frontend/src/components/DimensionControls.tsx` | **Create** |
| `frontend/src/pages/MapPage.tsx` | Wire multidim hook, pass props |
| `frontend/src/components/MapSidePanel.tsx` | Render DimensionControls |
| `frontend/src/hooks/useLayerBuilder.ts` | Add multidim tile URL path |
| `frontend/src/hooks/useMapData.ts` | Map new fields |
Part of #113
Contributor guide
Research direction
Start by reading RasterSidebarControls.tsx, TemporalControls, MapPage.tsx, MapSidePanel.tsx, useLayerBuilder.ts, and useMapData.ts, then run the frontend type check. Add the two new controls files and wire the listed multidimensional fields and tile URL through the map; done means the verification checklist passes for multidim and non-multidim datasets.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend, web-dev
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100