{"id":"69cac280d3d975d26bea667f","title":"2026 03 30","path":"how-to/committee/release-notes/2026-03-30","contentMarkdown":"# 30-Mar-2026 — area-map: custom group boundaries and parish footpath observer allocations [#201](https://github.com/nbarrett/ngx-ramblers/issues/201)\n\n## [build 568](https://github.com/nbarrett/ngx-ramblers/actions/runs/23760760018) — [commit 34c780a](https://github.com/nbarrett/ngx-ramblers/commit/34c780a12944dea2b499f905782297e9ea8b5c10)\n\n_____\n\n### **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))\n\n## Custom Group Boundaries\n- Support ad-hoc shapefile upload for custom group boundary polygons\n- `customGeometry` field on `AreaGroup` for per-group GeoJSON polygon overrides\n- BNG→WGS84 coordinate transform via `proj4` for uploaded shapefiles\n- Features matched to groups by `GROUP_CODE`/`GROUP_NAME` properties\n- Admin UI with upload button, match preview, and per-group clear\n## Parish Boundary Overlay\n- ONS parish boundary proxy at `/api/parishes/query` fetching from\nParishes_and_Non_Civil_Parished_Areas_December_2024 ArcGIS FeatureServer\n- Server-side pagination, bounding box validation, and 24h in-memory cache\nwith `noCache` query param to force refresh\n- Parish boundaries clipped to group area polygons using point-in-polygon\nray casting, coloured by allocation status (green=allocated, red=vacant)\n- Hover highlights with increased opacity, tooltip showing parish name and status\n## Parish Footpath Observer (PFO) Allocations\n- MongoDB `parishAllocations` collection with compound unique index on\n`(groupCode, parishCode)` storing per-parish allocated/vacant status\n- Full CRUD via `/api/database/parish-allocation` with bulk delete endpoint\n- CSV import at `/api/parishes/import` with upsert logic (matches by\nparishCode or parishName), per-row error detail reporting, and automatic\nfallback code generation for parishes without ONS codes\n- Click-to-toggle admin UI: authenticated admins click a parish on the map\nto toggle allocated/vacant, saved to MongoDB in real time\n## Parish Popup with Member Selector\n- New `ParishPopupComponent` with ng-select dropdown for assigning members\nto parishes via `assigneeMemberId`\n- Compact mode for card-based rendering, Escape key closes popup\n- Dynamic Angular component creation via `createComponent()` replaces\nHTML-string popups\n- Members loaded once in `ngOnInit`, tooltip suppression while popup is open\n- Selecting a member auto-sets status to \"allocated\"; marking vacant clears\nassignee\n## Parish Model & Query\n- Added `assigneeMemberId` field to `ParishAllocation` model and Mongoose schema\n- Added `inSR: 4326` to ArcGIS API call to fix projection mismatch\n- CSV import resolves assignee names to member `_id` via database lookup\n## Admin Settings (Maps Tab)\n- Maps tab split into togglable sub-sections using `SectionToggle`:\nArea Groups, Custom Boundaries, Geographic Data, Parish Allocations\n- Parish Allocations section with:\n- Image-cropper-style file dialog (badge button + drag-and-drop zone)\n- Sortable/filterable allocations table with status badges\n- Clear All Allocations button with bulk delete\n- Full error detail display on import failures\n- ngx-bootstrap tooltips on custom boundary icons (replacing native `title`)\n## CMS Page Editor\n- `AreaMapData` extended with `showAreas`, `showParishes`, colour pickers\n(`parishAllocatedColor`, `parishVacantColor`, `parishBorderColor`),\nand `parishFillOpacity` slider\n- Checkboxes for \"Show group area overlays\" and \"Show civil parish boundaries\"\n- CSV import UI with badge button, drag-and-drop, filename confirmation\n## Event Group Component\n- Added compact input mode (hides \"Group\" heading on cards)\n- Fixed \"undefined\" group name fallback via API lookup\n- Icon and text wrapped in single clickable link with tooltip\n- Shows group name even without login (no URL required)\n- Removed unused service imports\n- Consistent margin spacing in walk view\n## Data Migration & Housekeeping\n- Migration `20260328000000-backfill-area-map-show-areas.ts` sets\n`showAreas: true` on all existing area map rows in pageContent\n- Extracted repeated MongoDB collection name constants into\n`server/lib/mongo/migrations/shared/collection-names.ts`\n- Updated 16 migration files to import from shared module\n- Fixed `\"pagecontent\"` typo to correct `\"pageContent\"` casing\n- Added `MapsSubTab` and `ParishAllocationSortField` enums\n- Removed unused `MarkdownEditorComponent` import from album backgrounds\n- Suppressed browser focus outline on Leaflet map elements\n\n## Summary\nAdds the ability to upload ESRI shapefiles containing custom group\nboundary polygons, replacing the default ONS Local Authority District\nboundaries on the area map. This produces sharper, more accurate group\nboundaries that reflect actual walking group territories rather than\napproximations built from council district polygons.\n## What's New\n### Custom Group Boundary Upload\n- **Upload a shapefile (.zip)** via the new \"Custom Group Boundaries\"\nsection in Admin > System Settings > Area Map\n- Shapefile features are automatically matched to configured groups\nby `GROUP_CODE` or `GROUP_NAME` properties in the .dbf file\n- Coordinates in British National Grid (OSGB36) are automatically\ndetected and transformed to WGS84\n- Geometry is simplified using Turf.js for efficient map rendering\n- Upload results show which features matched to groups and which\ndid not, so administrators can verify the import before saving\n### Per-Group Custom Geometry\n- Each group can now have its own `customGeometry` (Polygon or\nMultiPolygon) stored directly in the system configuration\n- When custom geometry is present, the area map uses it directly\ninstead of looking up and assembling ONS district boundaries\n- Groups without custom geometry continue to use ONS districts\nas before — both sources can be mixed within the same area map\n- A green marker icon in the groups table indicates which groups\nhave custom boundaries applied\n### Clearing Custom Boundaries\n- Clear custom geometry from a single group by clicking the clear\nbutton next to its custom geometry indicator in the groups table\n- Clear all custom boundaries at once with the \"Clear All Custom\nBoundaries\" button, reverting all groups to ONS district lookup\n## API Endpoints\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| POST | `/api/areas/upload-group-boundaries` | Upload shapefile ZIP |\n| DELETE | `/api/areas/group-boundary/:groupCode` | Clear one group |\n| DELETE | `/api/areas/group-boundaries` | Clear all groups |\n## Technical Details\n- New `customGeometry` field on `AreaGroup` interface\n(`system.model.ts`)\n- Reusable shapefile parser extracted to\n`server/lib/map-routes/shapefile-parser.ts`\n- Backend `areas()` and `batchAreas()` endpoints check for\n`customGeometry` before falling back to ONS district lookup\n- No changes required to the frontend area map component — it\nrenders whatever GeoJSON the backend returns\n- Multer middleware handles file upload on the new route\n## Context\nKent area coordinator Robert Peel provided a shapefile\n(`groups_new.zip`) with accurate boundary polygons for all 11\ngeographically-based Kent walking groups. The existing ONS\ndistrict-based approach produced boundaries that were imprecise\nand visually busy. This feature enables any area to upload their\nown authoritative group boundary data.","contentHtml":"<h1>30-Mar-2026 — area-map: custom group boundaries and parish footpath observer allocations <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/201\">#201</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/23760760018\">build 568</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/34c780a12944dea2b499f905782297e9ea8b5c10\">commit 34c780a</a></h2>\n<hr>\n<h3><strong>area-map</strong>: custom group boundaries and parish footpath observer allocations and support ad-hoc shapefile upload for custom group boundaries (<a href=\"https://github.com/nbarrett/ngx-ramblers/issues/201\">#201</a>)</h3>\n<h2>Custom Group Boundaries</h2>\n<ul>\n<li>Support ad-hoc shapefile upload for custom group boundary polygons</li>\n<li><code>customGeometry</code> field on <code>AreaGroup</code> for per-group GeoJSON polygon overrides</li>\n<li>BNG→WGS84 coordinate transform via <code>proj4</code> for uploaded shapefiles</li>\n<li>Features matched to groups by <code>GROUP_CODE</code>/<code>GROUP_NAME</code> properties</li>\n<li>Admin UI with upload button, match preview, and per-group clear</li>\n</ul>\n<h2>Parish Boundary Overlay</h2>\n<ul>\n<li>ONS parish boundary proxy at <code>/api/parishes/query</code> fetching from\nParishes_and_Non_Civil_Parished_Areas_December_2024 ArcGIS FeatureServer</li>\n<li>Server-side pagination, bounding box validation, and 24h in-memory cache\nwith <code>noCache</code> query param to force refresh</li>\n<li>Parish boundaries clipped to group area polygons using point-in-polygon\nray casting, coloured by allocation status (green=allocated, red=vacant)</li>\n<li>Hover highlights with increased opacity, tooltip showing parish name and status</li>\n</ul>\n<h2>Parish Footpath Observer (PFO) Allocations</h2>\n<ul>\n<li>MongoDB <code>parishAllocations</code> collection with compound unique index on\n<code>(groupCode, parishCode)</code> storing per-parish allocated/vacant status</li>\n<li>Full CRUD via <code>/api/database/parish-allocation</code> with bulk delete endpoint</li>\n<li>CSV import at <code>/api/parishes/import</code> with upsert logic (matches by\nparishCode or parishName), per-row error detail reporting, and automatic\nfallback code generation for parishes without ONS codes</li>\n<li>Click-to-toggle admin UI: authenticated admins click a parish on the map\nto toggle allocated/vacant, saved to MongoDB in real time</li>\n</ul>\n<h2>Parish Popup with Member Selector</h2>\n<ul>\n<li>New <code>ParishPopupComponent</code> with ng-select dropdown for assigning members\nto parishes via <code>assigneeMemberId</code></li>\n<li>Compact mode for card-based rendering, Escape key closes popup</li>\n<li>Dynamic Angular component creation via <code>createComponent()</code> replaces\nHTML-string popups</li>\n<li>Members loaded once in <code>ngOnInit</code>, tooltip suppression while popup is open</li>\n<li>Selecting a member auto-sets status to &quot;allocated&quot;; marking vacant clears\nassignee</li>\n</ul>\n<h2>Parish Model &amp; Query</h2>\n<ul>\n<li>Added <code>assigneeMemberId</code> field to <code>ParishAllocation</code> model and Mongoose schema</li>\n<li>Added <code>inSR: 4326</code> to ArcGIS API call to fix projection mismatch</li>\n<li>CSV import resolves assignee names to member <code>_id</code> via database lookup</li>\n</ul>\n<h2>Admin Settings (Maps Tab)</h2>\n<ul>\n<li>Maps tab split into togglable sub-sections using <code>SectionToggle</code>:\nArea Groups, Custom Boundaries, Geographic Data, Parish Allocations</li>\n<li>Parish Allocations section with:</li>\n<li>Image-cropper-style file dialog (badge button + drag-and-drop zone)</li>\n<li>Sortable/filterable allocations table with status badges</li>\n<li>Clear All Allocations button with bulk delete</li>\n<li>Full error detail display on import failures</li>\n<li>ngx-bootstrap tooltips on custom boundary icons (replacing native <code>title</code>)</li>\n</ul>\n<h2>CMS Page Editor</h2>\n<ul>\n<li><code>AreaMapData</code> extended with <code>showAreas</code>, <code>showParishes</code>, colour pickers\n(<code>parishAllocatedColor</code>, <code>parishVacantColor</code>, <code>parishBorderColor</code>),\nand <code>parishFillOpacity</code> slider</li>\n<li>Checkboxes for &quot;Show group area overlays&quot; and &quot;Show civil parish boundaries&quot;</li>\n<li>CSV import UI with badge button, drag-and-drop, filename confirmation</li>\n</ul>\n<h2>Event Group Component</h2>\n<ul>\n<li>Added compact input mode (hides &quot;Group&quot; heading on cards)</li>\n<li>Fixed &quot;undefined&quot; group name fallback via API lookup</li>\n<li>Icon and text wrapped in single clickable link with tooltip</li>\n<li>Shows group name even without login (no URL required)</li>\n<li>Removed unused service imports</li>\n<li>Consistent margin spacing in walk view</li>\n</ul>\n<h2>Data Migration &amp; Housekeeping</h2>\n<ul>\n<li>Migration <code>20260328000000-backfill-area-map-show-areas.ts</code> sets\n<code>showAreas: true</code> on all existing area map rows in pageContent</li>\n<li>Extracted repeated MongoDB collection name constants into\n<code>server/lib/mongo/migrations/shared/collection-names.ts</code></li>\n<li>Updated 16 migration files to import from shared module</li>\n<li>Fixed <code>&quot;pagecontent&quot;</code> typo to correct <code>&quot;pageContent&quot;</code> casing</li>\n<li>Added <code>MapsSubTab</code> and <code>ParishAllocationSortField</code> enums</li>\n<li>Removed unused <code>MarkdownEditorComponent</code> import from album backgrounds</li>\n<li>Suppressed browser focus outline on Leaflet map elements</li>\n</ul>\n<h2>Summary</h2>\n<p>Adds the ability to upload ESRI shapefiles containing custom group\nboundary polygons, replacing the default ONS Local Authority District\nboundaries on the area map. This produces sharper, more accurate group\nboundaries that reflect actual walking group territories rather than\napproximations built from council district polygons.</p>\n<h2>What&#39;s New</h2>\n<h3>Custom Group Boundary Upload</h3>\n<ul>\n<li><strong>Upload a shapefile (.zip)</strong> via the new &quot;Custom Group Boundaries&quot;\nsection in Admin &gt; System Settings &gt; Area Map</li>\n<li>Shapefile features are automatically matched to configured groups\nby <code>GROUP_CODE</code> or <code>GROUP_NAME</code> properties in the .dbf file</li>\n<li>Coordinates in British National Grid (OSGB36) are automatically\ndetected and transformed to WGS84</li>\n<li>Geometry is simplified using Turf.js for efficient map rendering</li>\n<li>Upload results show which features matched to groups and which\ndid not, so administrators can verify the import before saving</li>\n</ul>\n<h3>Per-Group Custom Geometry</h3>\n<ul>\n<li>Each group can now have its own <code>customGeometry</code> (Polygon or\nMultiPolygon) stored directly in the system configuration</li>\n<li>When custom geometry is present, the area map uses it directly\ninstead of looking up and assembling ONS district boundaries</li>\n<li>Groups without custom geometry continue to use ONS districts\nas before — both sources can be mixed within the same area map</li>\n<li>A green marker icon in the groups table indicates which groups\nhave custom boundaries applied</li>\n</ul>\n<h3>Clearing Custom Boundaries</h3>\n<ul>\n<li>Clear custom geometry from a single group by clicking the clear\nbutton next to its custom geometry indicator in the groups table</li>\n<li>Clear all custom boundaries at once with the &quot;Clear All Custom\nBoundaries&quot; button, reverting all groups to ONS district lookup</li>\n</ul>\n<h2>API Endpoints</h2>\n<table>\n<thead>\n<tr>\n<th>Method</th>\n<th>Endpoint</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>POST</td>\n<td><code>/api/areas/upload-group-boundaries</code></td>\n<td>Upload shapefile ZIP</td>\n</tr>\n<tr>\n<td>DELETE</td>\n<td><code>/api/areas/group-boundary/:groupCode</code></td>\n<td>Clear one group</td>\n</tr>\n<tr>\n<td>DELETE</td>\n<td><code>/api/areas/group-boundaries</code></td>\n<td>Clear all groups</td>\n</tr>\n</tbody></table>\n<h2>Technical Details</h2>\n<ul>\n<li>New <code>customGeometry</code> field on <code>AreaGroup</code> interface\n(<code>system.model.ts</code>)</li>\n<li>Reusable shapefile parser extracted to\n<code>server/lib/map-routes/shapefile-parser.ts</code></li>\n<li>Backend <code>areas()</code> and <code>batchAreas()</code> endpoints check for\n<code>customGeometry</code> before falling back to ONS district lookup</li>\n<li>No changes required to the frontend area map component — it\nrenders whatever GeoJSON the backend returns</li>\n<li>Multer middleware handles file upload on the new route</li>\n</ul>\n<h2>Context</h2>\n<p>Kent area coordinator Robert Peel provided a shapefile\n(<code>groups_new.zip</code>) with accurate boundary polygons for all 11\ngeographically-based Kent walking groups. The existing ONS\ndistrict-based approach produced boundaries that were imprecise\nand visually busy. This feature enables any area to upload their\nown authoritative group boundary data.</p>\n"}