# 30-Mar-2026 — area-map: custom group boundaries and parish footpath observer allocations [#201](https://github.com/nbarrett/ngx-ramblers/issues/201)

## [build 568](https://github.com/nbarrett/ngx-ramblers/actions/runs/23760760018) — [commit 34c780a](https://github.com/nbarrett/ngx-ramblers/commit/34c780a12944dea2b499f905782297e9ea8b5c10)

_____

### **area-map**: custom group boundaries and parish footpath observer allocations and support ad-hoc shapefile upload for custom group boundaries ([#201](https://github.com/nbarrett/ngx-ramblers/issues/201))

## Custom Group Boundaries
- Support ad-hoc shapefile upload for custom group boundary polygons
- `customGeometry` field on `AreaGroup` for per-group GeoJSON polygon overrides
- BNG→WGS84 coordinate transform via `proj4` for uploaded shapefiles
- Features matched to groups by `GROUP_CODE`/`GROUP_NAME` properties
- Admin UI with upload button, match preview, and per-group clear
## Parish Boundary Overlay
- ONS parish boundary proxy at `/api/parishes/query` fetching from
Parishes_and_Non_Civil_Parished_Areas_December_2024 ArcGIS FeatureServer
- Server-side pagination, bounding box validation, and 24h in-memory cache
with `noCache` query param to force refresh
- Parish boundaries clipped to group area polygons using point-in-polygon
ray casting, coloured by allocation status (green=allocated, red=vacant)
- Hover highlights with increased opacity, tooltip showing parish name and status
## Parish Footpath Observer (PFO) Allocations
- MongoDB `parishAllocations` collection with compound unique index on
`(groupCode, parishCode)` storing per-parish allocated/vacant status
- Full CRUD via `/api/database/parish-allocation` with bulk delete endpoint
- CSV import at `/api/parishes/import` with upsert logic (matches by
parishCode or parishName), per-row error detail reporting, and automatic
fallback code generation for parishes without ONS codes
- Click-to-toggle admin UI: authenticated admins click a parish on the map
to toggle allocated/vacant, saved to MongoDB in real time
## Parish Popup with Member Selector
- New `ParishPopupComponent` with ng-select dropdown for assigning members
to parishes via `assigneeMemberId`
- Compact mode for card-based rendering, Escape key closes popup
- Dynamic Angular component creation via `createComponent()` replaces
HTML-string popups
- Members loaded once in `ngOnInit`, tooltip suppression while popup is open
- Selecting a member auto-sets status to "allocated"; marking vacant clears
assignee
## Parish Model & Query
- Added `assigneeMemberId` field to `ParishAllocation` model and Mongoose schema
- Added `inSR: 4326` to ArcGIS API call to fix projection mismatch
- CSV import resolves assignee names to member `_id` via database lookup
## Admin Settings (Maps Tab)
- Maps tab split into togglable sub-sections using `SectionToggle`:
Area Groups, Custom Boundaries, Geographic Data, Parish Allocations
- Parish Allocations section with:
- Image-cropper-style file dialog (badge button + drag-and-drop zone)
- Sortable/filterable allocations table with status badges
- Clear All Allocations button with bulk delete
- Full error detail display on import failures
- ngx-bootstrap tooltips on custom boundary icons (replacing native `title`)
## CMS Page Editor
- `AreaMapData` extended with `showAreas`, `showParishes`, colour pickers
(`parishAllocatedColor`, `parishVacantColor`, `parishBorderColor`),
and `parishFillOpacity` slider
- Checkboxes for "Show group area overlays" and "Show civil parish boundaries"
- CSV import UI with badge button, drag-and-drop, filename confirmation
## Event Group Component
- Added compact input mode (hides "Group" heading on cards)
- Fixed "undefined" group name fallback via API lookup
- Icon and text wrapped in single clickable link with tooltip
- Shows group name even without login (no URL required)
- Removed unused service imports
- Consistent margin spacing in walk view
## Data Migration & Housekeeping
- Migration `20260328000000-backfill-area-map-show-areas.ts` sets
`showAreas: true` on all existing area map rows in pageContent
- Extracted repeated MongoDB collection name constants into
`server/lib/mongo/migrations/shared/collection-names.ts`
- Updated 16 migration files to import from shared module
- Fixed `"pagecontent"` typo to correct `"pageContent"` casing
- Added `MapsSubTab` and `ParishAllocationSortField` enums
- Removed unused `MarkdownEditorComponent` import from album backgrounds
- Suppressed browser focus outline on Leaflet map elements

## Summary
Adds the ability to upload ESRI shapefiles containing custom group
boundary polygons, replacing the default ONS Local Authority District
boundaries on the area map. This produces sharper, more accurate group
boundaries that reflect actual walking group territories rather than
approximations built from council district polygons.
## What's New
### Custom Group Boundary Upload
- **Upload a shapefile (.zip)** via the new "Custom Group Boundaries"
section in Admin > System Settings > Area Map
- Shapefile features are automatically matched to configured groups
by `GROUP_CODE` or `GROUP_NAME` properties in the .dbf file
- Coordinates in British National Grid (OSGB36) are automatically
detected and transformed to WGS84
- Geometry is simplified using Turf.js for efficient map rendering
- Upload results show which features matched to groups and which
did not, so administrators can verify the import before saving
### Per-Group Custom Geometry
- Each group can now have its own `customGeometry` (Polygon or
MultiPolygon) stored directly in the system configuration
- When custom geometry is present, the area map uses it directly
instead of looking up and assembling ONS district boundaries
- Groups without custom geometry continue to use ONS districts
as before — both sources can be mixed within the same area map
- A green marker icon in the groups table indicates which groups
have custom boundaries applied
### Clearing Custom Boundaries
- Clear custom geometry from a single group by clicking the clear
button next to its custom geometry indicator in the groups table
- Clear all custom boundaries at once with the "Clear All Custom
Boundaries" button, reverting all groups to ONS district lookup
## API Endpoints
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/areas/upload-group-boundaries` | Upload shapefile ZIP |
| DELETE | `/api/areas/group-boundary/:groupCode` | Clear one group |
| DELETE | `/api/areas/group-boundaries` | Clear all groups |
## Technical Details
- New `customGeometry` field on `AreaGroup` interface
(`system.model.ts`)
- Reusable shapefile parser extracted to
`server/lib/map-routes/shapefile-parser.ts`
- Backend `areas()` and `batchAreas()` endpoints check for
`customGeometry` before falling back to ONS district lookup
- No changes required to the frontend area map component — it
renders whatever GeoJSON the backend returns
- Multer middleware handles file upload on the new route
## Context
Kent area coordinator Robert Peel provided a shapefile
(`groups_new.zip`) with accurate boundary polygons for all 11
geographically-based Kent walking groups. The existing ONS
district-based approach produced boundaries that were imprecise
and visually busy. This feature enables any area to upload their
own authoritative group boundary data.