hotfix: Add polygon map layers for 7 campaigns (Issue #207)#10
hotfix: Add polygon map layers for 7 campaigns (Issue #207)#10echelonnought wants to merge 2 commits into
Conversation
Implements Issue #207 — Add polygon/map layer for each campaign. Adds 7 GeoJSON fixture files to public/fixtures/ that are automatically discovered by the existing fixture-reader.ts (import.meta.glob pattern). No code changes required — the layer controls will show the new campaign layers as toggleable items. New fixture files: - campaign_rio_negro_conservation.geojson (18,500 acres, River Conservation) - campaign_solimoes_wetland.geojson (22,000 acres, Wetland Restoration) - campaign_taruma_corridor.geojson (9,800 acres, Wildlife Corridor) - campaign_encontro_das_aguas.geojson (31,000 acres, Ecological Reserve) - campaign_ducke_forest.geojson (10,000 acres, Research Station) - campaign_puraquequara_lake.geojson (7,500 acres, Lake Protection) - campaign_anavilhanas_archipelago.geojson (42,000 acres, Environmental Monitoring) Each GeoJSON follows the existing fixture format: - type: FeatureCollection with CRS header - name: 'Campaign: <name>' for layer control display - features: Polygon geometry with campaign properties (name, status, partner, acreage, watershed, projectType) All polygons are positioned around the Manaus region to be visible on the existing map view centered at -3.1319, -60.0261.
📝 WalkthroughWalkthroughSeven new GeoJSON campaign fixture files are added to ChangesCampaign Fixture Data
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Detailed Implementation NotesWhy no code changes?The fixture-reader.ts already uses Vite's import.meta.glob('/public/fixtures/*.geojson') to auto-discover all GeoJSON files. Adding new .geojson files to that directory is all that's needed -- the fixture reader picks them up, and the LayerControls component renders them as toggleable layers using the 'name' field from each FeatureCollection. GeoJSON structureEach file follows the exact same pattern as the existing fixtures:
Campaign metadata in propertiesEach feature's properties include structured campaign data (name, status, partner, acreage, watershed, projectType). This data is ready to be consumed by the ProjectDetailView component from PR #8 when features are clicked. Geographic positioningAll 7 campaign polygons are placed in the greater Manaus area (-3.1319, -60.0261) to be immediately visible on the existing map view without needing to pan or zoom. Temporary natureAs noted in the issue, this is a hotfix. Once the backend endpoints from Issue #206 are operational, these static fixture files should be replaced with API-driven campaign data. |
Standardize property names across campaign fixtures to match the shared FeatureProperties interface agreed with Glenn (DietBepis1): - name -> identifier (avoids collision with FeatureCollection name) - partner -> partners (string array, supports multiple) - acreage -> measurement + unitOfMeasure (flexible units) - watershed -> watershedRegion (more descriptive) - Add restorationServices (empty array, ready for future data) - Keep: id, campaign, status, projectType (unique to campaigns)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@public/fixtures/campaign_encontro_das_aguas.geojson`:
- Around line 23-33: The polygon exterior ring in the "coordinates" array for
campaign_encontro_das_aguas.geojson is wound clockwise (violates RFC 7946
§3.1.6); fix it by reversing the order of the interior coordinate sequence for
each exterior LinearRing in the "coordinates" arrays (preserve the final closing
coordinate equal to the first), i.e., reverse the array of points inside each
top-level coordinates[0] ring so they become counterclockwise; apply the same
reversal pattern to the other six fixture files (or run a tool like
geojson-rewind) to ensure all exterior rings are CCW.
- Around line 1-37: Define a concrete FeatureProperties interface matching the
campaign fixture keys (identifier: string, partners: string[], measurement:
number, unitOfMeasure: string, watershedRegion: string, projectType: string,
restorationServices: any[] — plus any existing common fields like id, campaign,
status) in src/types/geometry.ts (or new src/types/campaign.ts) and export it as
FeatureProperties; then update the Feature generic usage in fixture-reader.ts
(replace plain geojson.Feature with geojson.Feature<FeatureProperties> or your
exported Feature type) so campaign feature properties are strongly typed and not
inferred as any. Ensure imports/exports are adjusted and any consumers of the
Feature type are updated to use the new typed FeatureProperties.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e3f7f0f0-5728-4f53-92c3-13f5fa19bd8f
📒 Files selected for processing (7)
public/fixtures/campaign_anavilhanas_archipelago.geojsonpublic/fixtures/campaign_ducke_forest.geojsonpublic/fixtures/campaign_encontro_das_aguas.geojsonpublic/fixtures/campaign_puraquequara_lake.geojsonpublic/fixtures/campaign_rio_negro_conservation.geojsonpublic/fixtures/campaign_solimoes_wetland.geojsonpublic/fixtures/campaign_taruma_corridor.geojson
| { | ||
| "type": "FeatureCollection", | ||
| "name": "Campaign: Encontro das Aguas Reserve", | ||
| "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, | ||
| "features": [ | ||
| { | ||
| "type": "Feature", | ||
| "id": "encontro-1", | ||
| "properties": { | ||
| "id": "encontro-1", | ||
| "identifier": "Encontro das Aguas Reserve", | ||
| "campaign": "Encontro das Aguas Reserve", | ||
| "status": "active", | ||
| "partners": ["Meeting of the Waters Foundation"], | ||
| "measurement": 31000, | ||
| "unitOfMeasure": "acre", | ||
| "watershedRegion": "Negro-Solimoes Confluence", | ||
| "projectType": "Ecological Reserve", | ||
| "restorationServices": [] | ||
| }, | ||
| "geometry": { | ||
| "type": "Polygon", | ||
| "coordinates": [ | ||
| [ | ||
| [-59.9800, -3.0800], | ||
| [-59.9200, -3.0800], | ||
| [-59.9000, -3.1100], | ||
| [-59.9200, -3.1400], | ||
| [-59.9600, -3.1400], | ||
| [-59.9800, -3.1200], | ||
| [-59.9800, -3.0800] | ||
| ] | ||
| ] | ||
| } | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for the FeatureProperties type/interface to confirm schema alignment
rg -n --type ts -A 20 "FeatureProperties"Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 2071
🏁 Script executed:
#!/bin/bash
# Search for imports of the campaign fixture file
rg -n "campaign_encontro_das_aguas" --type tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 67
🏁 Script executed:
#!/bin/bash
# Search for FeatureProperties interface (excluding QGIS)
rg -n "interface FeatureProperties|type FeatureProperties" --type ts -A 15Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 67
🏁 Script executed:
#!/bin/bash
# Search for ProjectDetailView since it was mentioned as a consumer
rg -n "ProjectDetailView" --type ts -A 10 -B 5Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 67
🏁 Script executed:
#!/bin/bash
# List the overall structure
ls -la src/Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 721
🏁 Script executed:
#!/bin/bash
# Find all TypeScript files to understand project structure
fd -e ts -e tsx src/Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 407
🏁 Script executed:
#!/bin/bash
# Search for "Feature" related type definitions
rg -n "interface.*Feature|type.*Feature" --type tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 1051
🏁 Script executed:
#!/bin/bash
# Look for import.meta.glob patterns that might load fixtures
rg -n "import.meta.glob" --type tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 181
🏁 Script executed:
#!/bin/bash
# Read the geometry types file
cat -n src/types/geometry.tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 432
🏁 Script executed:
#!/bin/bash
# Read the fixture-reader.ts file
cat -n src/data/fixture-reader.tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 909
🏁 Script executed:
#!/bin/bash
# Search for Feature type with properties in TypeScript files
rg -n "properties:" --type ts -B 3 -A 3 | head -60Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 700
🏁 Script executed:
#!/bin/bash
# Search for components that use FixtureReader or access fixture data
rg -n "FixtureReader|fixture" --type ts -B 3 -A 3Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 2462
🏁 Script executed:
#!/bin/bash
# Search for components that might access feature properties like "identifier" or "partners"
rg -n "\.properties\.|identifier|partners" --type tsx -B 2 -A 2Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 113
🏁 Script executed:
#!/bin/bash
# Look at the components directory structure
find src/components -type f -name "*.tsx" -o -name "*.ts"Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 284
🏁 Script executed:
#!/bin/bash
# Check Layer.tsx which likely iterates over features
cat -n src/components/Map/Layer.tsxRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 793
🏁 Script executed:
#!/bin/bash
# Check MapContainer.tsx
cat -n src/components/Map/MapContainer.tsxRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 3684
🏁 Script executed:
#!/bin/bash
# Search for any property access on feature objects
rg -n "feature\.properties|\.properties\[" --type tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 228
🏁 Script executed:
#!/bin/bash
# Search for detail views or components that might show campaign info
rg -n "Detail|Campaign|campaign" --type ts --type tsx -iRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 113
🏁 Script executed:
#!/bin/bash
# Check what the geojson types define for properties
rg -n "properties" --type ts src/hooks src/componentsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 204
🏁 Script executed:
#!/bin/bash
# Search for campaign or detail related files without tsx flag
find src -type f \( -name "*.ts" -o -name "*.tsx" \) | xargs grep -l "campaign\|Detail\|properties" -iRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 188
🏁 Script executed:
#!/bin/bash
# Check if there's a types file for campaign or feature-specific properties
ls -la src/types/Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 313
🏁 Script executed:
#!/bin/bash
# Search for all files that might define property-related interfaces
rg -n "interface.*Properties|type.*Properties" -uRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 150
🏁 Script executed:
#!/bin/bash
# Check the map.ts file for property-related types
cat -n src/types/map.tsRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 3475
🏁 Script executed:
#!/bin/bash
# Check if there are any recent commits that might show schema changes
git log --oneline -20 2>/dev/null | head -20Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 156
🏁 Script executed:
#!/bin/bash
# Show the refactoring commit details
git show 56f21e4 --stat 2>/dev/nullRepository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 7156
🏁 Script executed:
#!/bin/bash
# Show actual changes in that commit
git show 56f21e4 2>/dev/null | head -100Repository: OpenSourceFellows/map_dashboard_hackathon
Length of output: 3115
Create FeatureProperties TypeScript interface for campaign features
The refactoring commit standardized campaign fixture properties (identifier, partners, measurement, unitOfMeasure, watershedRegion, projectType, restorationServices) but the corresponding TypeScript interface was not created. Without it, these properties will be typed as any from the geojson library's default, making property access unsafe. Define a FeatureProperties interface in src/types/geometry.ts (or a new src/types/campaign.ts) that matches the fixture schema, and apply it to the Feature type used by fixture-reader.ts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@public/fixtures/campaign_encontro_das_aguas.geojson` around lines 1 - 37,
Define a concrete FeatureProperties interface matching the campaign fixture keys
(identifier: string, partners: string[], measurement: number, unitOfMeasure:
string, watershedRegion: string, projectType: string, restorationServices: any[]
— plus any existing common fields like id, campaign, status) in
src/types/geometry.ts (or new src/types/campaign.ts) and export it as
FeatureProperties; then update the Feature generic usage in fixture-reader.ts
(replace plain geojson.Feature with geojson.Feature<FeatureProperties> or your
exported Feature type) so campaign feature properties are strongly typed and not
inferred as any. Ensure imports/exports are adjusted and any consumers of the
Feature type are updated to use the new typed FeatureProperties.
| "coordinates": [ | ||
| [ | ||
| [-59.9800, -3.0800], | ||
| [-59.9200, -3.0800], | ||
| [-59.9000, -3.1100], | ||
| [-59.9200, -3.1400], | ||
| [-59.9600, -3.1400], | ||
| [-59.9800, -3.1200], | ||
| [-59.9800, -3.0800] | ||
| ] | ||
| ] |
There was a problem hiding this comment.
All 7 exterior polygon rings are wound clockwise — violates RFC 7946 §3.1.6
RFC 7946 §3.1.6 mandates that "a linear ring MUST follow the right-hand rule with respect to the area it bounds, i.e., exterior rings are counterclockwise, and holes are clockwise." Applying the shoelace signed-area formula to each fixture confirms every exterior ring has a negative signed area (clockwise). The violation is consistent across all seven files.
For backwards compatibility, parsers SHOULD NOT reject polygons that do not follow the right-hand rule, so current Leaflet/Mapbox rendering will be unaffected. However, strict geospatial backends (PostGIS with ST_IsValid, certain spatial databases, and validation pipelines) do enforce winding order, which matters once the backend endpoints from Issue #206 land and these fixtures are used as reference shapes.
The fix is simply to reverse the interior points of each ring while keeping the closing coordinate:
🔧 Proposed fix for `campaign_encontro_das_aguas.geojson` (apply same reversal pattern to all 7 files)
"coordinates": [
[
- [-59.9800, -3.0800],
- [-59.9200, -3.0800],
- [-59.9000, -3.1100],
- [-59.9200, -3.1400],
- [-59.9600, -3.1400],
- [-59.9800, -3.1200],
- [-59.9800, -3.0800]
+ [-59.9800, -3.0800],
+ [-59.9800, -3.1200],
+ [-59.9600, -3.1400],
+ [-59.9200, -3.1400],
+ [-59.9000, -3.1100],
+ [-59.9200, -3.0800],
+ [-59.9800, -3.0800]
]
]You can automate rewinding all seven files at once using the geojson-rewind CLI or the equivalent npm package before this branch is merged.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "coordinates": [ | |
| [ | |
| [-59.9800, -3.0800], | |
| [-59.9200, -3.0800], | |
| [-59.9000, -3.1100], | |
| [-59.9200, -3.1400], | |
| [-59.9600, -3.1400], | |
| [-59.9800, -3.1200], | |
| [-59.9800, -3.0800] | |
| ] | |
| ] | |
| "coordinates": [ | |
| [ | |
| [-59.9800, -3.0800], | |
| [-59.9800, -3.1200], | |
| [-59.9600, -3.1400], | |
| [-59.9200, -3.1400], | |
| [-59.9000, -3.1100], | |
| [-59.9200, -3.0800], | |
| [-59.9800, -3.0800] | |
| ] | |
| ] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@public/fixtures/campaign_encontro_das_aguas.geojson` around lines 23 - 33,
The polygon exterior ring in the "coordinates" array for
campaign_encontro_das_aguas.geojson is wound clockwise (violates RFC 7946
§3.1.6); fix it by reversing the order of the interior coordinate sequence for
each exterior LinearRing in the "coordinates" arrays (preserve the final closing
coordinate equal to the first), i.e., reverse the array of points inside each
top-level coordinates[0] ring so they become counterclockwise; apply the same
reversal pattern to the other six fixture files (or run a tool like
geojson-rewind) to ensure all exterior rings are CCW.
Unified GeoJSON Properties SchemaUpdated all 7 campaign fixture files to align with the shared FeatureProperties schema from Glenn's PR #211 (map-dashboard). Changes across all fixtures:
Kept unchanged: This ensures both repos can share a single TypeScript interface for rendering map features uniformly. |
Summary
Implements Issue #207 -- Add polygon/map layer for each campaign.
This is a quick-and-dirty hotfix to get campaign polygon layers on the map while the backend is still in progress. Each campaign is represented as a GeoJSON FeatureCollection with Polygon geometry and campaign metadata in the properties.
How It Works
The existing fixture-reader.ts uses import.meta.glob to auto-discover all .geojson files in public/fixtures/. By simply adding new .geojson files to that directory, the campaigns appear as new toggleable layers in the layer controls -- zero code changes needed.
7 Campaign Layers Added
Feature Properties
Each GeoJSON feature includes these properties for future use:
Files Added (7 new GeoJSON fixtures)
All files in public/fixtures/campaign_*.geojson
Verification
Summary by CodeRabbit