Skip to content

hotfix: Add polygon map layers for 7 campaigns (Issue #207)#10

Open
echelonnought wants to merge 2 commits into
mainfrom
hotfix/207-campaign-polygon-layers
Open

hotfix: Add polygon map layers for 7 campaigns (Issue #207)#10
echelonnought wants to merge 2 commits into
mainfrom
hotfix/207-campaign-polygon-layers

Conversation

@echelonnought

@echelonnought echelonnought commented May 2, 2026

Copy link
Copy Markdown

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

Campaign Acreage Project Type Status
Rio Negro Conservation 18,500 River Conservation Active
Solimoes Wetland Restoration 22,000 Wetland Restoration Active
Taruma Biodiversity Corridor 9,800 Wildlife Corridor Planning
Encontro das Aguas Reserve 31,000 Ecological Reserve Active
Ducke Forest Research 10,000 Research Station Active
Puraquequara Lake Protection 7,500 Lake Protection Planning
Anavilhanas Archipelago Monitoring 42,000 Environmental Monitoring Active

Feature Properties

Each GeoJSON feature includes these properties for future use:

  • name, campaign, status, partner, acreage, watershed, projectType

Files Added (7 new GeoJSON fixtures)

All files in public/fixtures/campaign_*.geojson

Verification

  • All files follow existing fixture format (FeatureCollection + CRS + name)
  • TypeScript compiles cleanly
  • Polygons positioned around Manaus map view
  • No code changes required -- auto-discovered by fixture reader

Summary by CodeRabbit

  • New Features
    • Added seven new campaign GeoJSON fixtures (Anavilhanas Archipelago, Ducke Forest, Encontro das Águas, Puraquequara Lake Protection, Rio Negro Conservation, Solimões Wetland Restoration, and Tarumã Biodiversity Corridor) with mapped boundaries and campaign metadata (status, partner, acreage, watershed, project type).
  • Bug Fixes
    • Improved map rendering by computing a valid feature center when geometry isn’t a direct point, preventing missing overlays.
    • Expanded popup details (including partner, acreage, and restoration services) with safer fallbacks for missing values.

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Seven new GeoJSON campaign fixture files were added to public/fixtures/, each containing a FeatureCollection with one Polygon Feature and campaign metadata. The Map Layer component was updated to import centroid from @turf/turf, implement defensive coordinate extraction that handles non-Point geometries by computing centroids, and expand popup content to display partner, acreage, and restoration services.

Changes

Campaign Fixtures and Map Rendering

Layer / File(s) Summary
Campaign GeoJSON fixture data
public/fixtures/campaign_*.geojson
Seven new GeoJSON FeatureCollection fixtures added: campaign_anavilhanas_archipelago, campaign_ducke_forest, campaign_encontro_das_aguas, campaign_puraquequara_lake, campaign_rio_negro_conservation, campaign_solimoes_wetland, campaign_taruma_corridor. Each file contains one Polygon Feature with campaign properties (id, name, campaign, status, partner, acreage, watershed, projectType) and coordinate geometry.
Map Layer centroid and popup rendering
src/components/Map/Layer.tsx
Imports centroid from @turf/turf and replaces direct Point coordinate extraction with defensive logic that attempts centroid computation for non-Point geometries, falling back safely if calculation fails. Popup content expanded to include partner, acreage, and restoration services fields with safe defaults. Circle rendering made conditional on center existence, and radius computation updated to use optional chaining with measurement ?? 0.

Sequence Diagram(s)

sequenceDiagram
  participant GeoJSON as GeoJSON Fixture<br/>(Polygon)
  participant Layer as Layer.tsx<br/>Component
  participant Turf as Turf.centroid()
  participant Popup as Popup UI
  
  GeoJSON->>Layer: Feature with Polygon geometry
  Layer->>Layer: Check if Point geometry
  alt Polygon geometry
    Layer->>Turf: centroid(feature)
    Turf->>Layer: center coordinates
  end
  Layer->>Popup: Render with center + campaign details
  Popup->>Popup: Display id, name, partner, acreage, services
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 I hopped through lines of GeoJSON bright,
Polygons planted by moonlit night,
Seven campaigns set by river and tree,
Centroids calculated, popups for all to see — hooray! 🌿✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding seven GeoJSON campaign polygon fixtures and updating the Layer component to support Polygon geometries.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/207-campaign-polygon-layers

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@echelonnought

Copy link
Copy Markdown
Author

Detailed Implementation Notes

Why 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 structure

Each file follows the exact same pattern as the existing fixtures:

  • type: FeatureCollection (required by GeoJSON spec)
  • name: 'Campaign: ' (used by the layer controls for display)
  • crs: OGC CRS84 (standard WGS84 coordinate reference system)
  • features: array of Feature objects with Polygon geometry

Campaign metadata in properties

Each 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 positioning

All 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 nature

As 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between c4ded60 and 56f21e4.

📒 Files selected for processing (7)
  • public/fixtures/campaign_anavilhanas_archipelago.geojson
  • public/fixtures/campaign_ducke_forest.geojson
  • public/fixtures/campaign_encontro_das_aguas.geojson
  • public/fixtures/campaign_puraquequara_lake.geojson
  • public/fixtures/campaign_rio_negro_conservation.geojson
  • public/fixtures/campaign_solimoes_wetland.geojson
  • public/fixtures/campaign_taruma_corridor.geojson

Comment on lines +1 to +37
{
"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]
]
]
}
}
]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 ts

Repository: 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 15

Repository: 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 5

Repository: 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 ts

Repository: 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 ts

Repository: OpenSourceFellows/map_dashboard_hackathon

Length of output: 181


🏁 Script executed:

#!/bin/bash
# Read the geometry types file
cat -n src/types/geometry.ts

Repository: OpenSourceFellows/map_dashboard_hackathon

Length of output: 432


🏁 Script executed:

#!/bin/bash
# Read the fixture-reader.ts file
cat -n src/data/fixture-reader.ts

Repository: 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 -60

Repository: 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 3

Repository: 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 2

Repository: 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.tsx

Repository: OpenSourceFellows/map_dashboard_hackathon

Length of output: 793


🏁 Script executed:

#!/bin/bash
# Check MapContainer.tsx
cat -n src/components/Map/MapContainer.tsx

Repository: 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 ts

Repository: 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 -i

Repository: 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/components

Repository: 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" -i

Repository: 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" -u

Repository: 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.ts

Repository: 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 -20

Repository: OpenSourceFellows/map_dashboard_hackathon

Length of output: 156


🏁 Script executed:

#!/bin/bash
# Show the refactoring commit details
git show 56f21e4 --stat 2>/dev/null

Repository: 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 -100

Repository: 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.

Comment on lines +23 to +33
"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]
]
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
"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.

@echelonnought

Copy link
Copy Markdown
Author

Unified GeoJSON Properties Schema

Updated all 7 campaign fixture files to align with the shared FeatureProperties schema from Glenn's PR #211 (map-dashboard). Changes across all fixtures:

Old Property New Property Rationale
name identifier Avoids collision with FeatureCollection-level name
partner (string) partners (string[]) Supports multiple partners per feature
acreage (number) measurement + unitOfMeasure More flexible for different units (acre, hectare, sqkm)
watershed watershedRegion More descriptive, matches Glenn's naming
(new) restorationServices (string[]) Empty array for now, ready for future data

Kept unchanged: id, campaign, status, projectType

This ensures both repos can share a single TypeScript interface for rendering map features uniformly.

@SAUMILDHANKAR SAUMILDHANKAR requested a review from antaerus June 9, 2026 22:03
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.
@echelonnought echelonnought force-pushed the hotfix/207-campaign-polygon-layers branch from 56f21e4 to 48e02f4 Compare June 13, 2026 15:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_anavilhanas_archipelago.geojson`:
- Around line 9-18: The fixture properties use the old schema and must be
updated to the unified schema Layer.tsx expects: replace `name` with
`identifier`, convert `partner` (string) into `partners` (array of strings),
split `acreage` into `measurement` (numeric) and `unitOfMeasure` (e.g., "acres"
or "ha"), rename `watershed` to `watershedRegion`, and add an empty array
`restorationServices: []`; specifically update
public/fixtures/campaign_anavilhanas_archipelago.geojson (lines 9-18) to include
identifier, partners[], measurement, unitOfMeasure, watershedRegion and
restorationServices: [] instead of name/partner/acreage/watershed, and make the
identical transformation in public/fixtures/campaign_solimoes_wetland.geojson
(lines 9-18) so both fixtures conform to Layer.tsx's expected properties
(`identifier`, `partners`, `measurement`, `unitOfMeasure`, `watershedRegion`,
`restorationServices`).
- Around line 19-32: Layer.tsx currently assumes feature.geometry is a Point and
reads coordinates[0]/[1] to set a Circle center; update it to handle Polygon
fixtures by computing a representative point (centroid or first coordinate) when
geometry.type === "Polygon" and fall back to using coordinates for "Point" so
Circle centers are correct; modify the logic in the component/function that
reads feature.geometry (Layer.tsx) to detect geometry.type and extract center
accordingly (e.g., compute polygon centroid from geometry.coordinates[0] or use
turf.centroid if available), ensure typing reflects Geometry | Polygon handling,
and add tests or safeguards for null/invalid geometry. For the two fixture sites
referenced, update files as follows:
public/fixtures/campaign_anavilhanas_archipelago.geojson (lines 19-32) — no
direct change required to the fixture if Layer.tsx is fixed (do not convert the
Polygon here); public/fixtures/campaign_solimoes_wetland.geojson (lines 19-31) —
same: no fixture changes, fix is in Layer.tsx to support Polygons.
🪄 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: 60d48794-292c-4829-b120-3bc57d555eb1

📥 Commits

Reviewing files that changed from the base of the PR and between 56f21e4 and 48e02f4.

📒 Files selected for processing (7)
  • public/fixtures/campaign_anavilhanas_archipelago.geojson
  • public/fixtures/campaign_ducke_forest.geojson
  • public/fixtures/campaign_encontro_das_aguas.geojson
  • public/fixtures/campaign_puraquequara_lake.geojson
  • public/fixtures/campaign_rio_negro_conservation.geojson
  • public/fixtures/campaign_solimoes_wetland.geojson
  • public/fixtures/campaign_taruma_corridor.geojson
✅ Files skipped from review due to trivial changes (5)
  • public/fixtures/campaign_rio_negro_conservation.geojson
  • public/fixtures/campaign_ducke_forest.geojson
  • public/fixtures/campaign_taruma_corridor.geojson
  • public/fixtures/campaign_puraquequara_lake.geojson
  • public/fixtures/campaign_encontro_das_aguas.geojson

Comment on lines +9 to +18
"properties": {
"id": "anavilhanas-1",
"name": "Anavilhanas Archipelago Monitoring",
"campaign": "Anavilhanas Archipelago Monitoring",
"status": "active",
"partner": "ICMBio Brazil",
"acreage": 42000,
"watershed": "Rio Negro Archipelago",
"projectType": "Environmental Monitoring"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: All fixture feature properties must match the Layer.tsx rendering contract.

Both reviewed fixtures (and likely all seven fixtures mentioned in the PR) use the old property schema instead of the unified schema described in PR objectives. Layer.tsx expects identifier, partners (array), measurement, restorationServices (array) and will crash when calling .join() on undefined or string values.

Apply the schema transformation to each fixture:

  • public/fixtures/campaign_anavilhanas_archipelago.geojson#L9-L18: Transform properties from name/partner/acreage/watershed to identifier/partners[]/measurement+unitOfMeasure/watershedRegion and add restorationServices: []
  • public/fixtures/campaign_solimoes_wetland.geojson#L9-L18: Apply the same transformation (see diff in first per-site comment for exact structure)
📍 Affects 2 files
  • public/fixtures/campaign_anavilhanas_archipelago.geojson#L9-L18 (this comment)
  • public/fixtures/campaign_solimoes_wetland.geojson#L9-L18
🤖 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_anavilhanas_archipelago.geojson` around lines 9 -
18, The fixture properties use the old schema and must be updated to the unified
schema Layer.tsx expects: replace `name` with `identifier`, convert `partner`
(string) into `partners` (array of strings), split `acreage` into `measurement`
(numeric) and `unitOfMeasure` (e.g., "acres" or "ha"), rename `watershed` to
`watershedRegion`, and add an empty array `restorationServices: []`;
specifically update public/fixtures/campaign_anavilhanas_archipelago.geojson
(lines 9-18) to include identifier, partners[], measurement, unitOfMeasure,
watershedRegion and restorationServices: [] instead of
name/partner/acreage/watershed, and make the identical transformation in
public/fixtures/campaign_solimoes_wetland.geojson (lines 9-18) so both fixtures
conform to Layer.tsx's expected properties (`identifier`, `partners`,
`measurement`, `unitOfMeasure`, `watershedRegion`, `restorationServices`).

Comment thread public/fixtures/campaign_anavilhanas_archipelago.geojson

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@src/components/Map/Layer.tsx`:
- Around line 35-47: The coordinate validation before building the `center`
variable only checks the length of the coordinates array but doesn't validate
that both `coordinates[0]` and `coordinates[1]` are actually numbers. This can
allow invalid (non-numeric) coordinate values to be passed to Leaflet.
Strengthen the condition that constructs the `center` variable to verify not
only that the coordinates array has at least 2 elements but also that both
`coordinates[0]` and `coordinates[1]` are valid numbers using typeof checks,
similar to the validation already applied in the Point geometry check above.
- Around line 59-65: The popup label and circle radius calculation in the Layer
component are hardcoded for acreage, which conflicts with the new measurement +
unitOfMeasure contract. Replace the static "Current Acreage" label in the p tag
with a dynamic label that uses the feature.properties?.unitOfMeasure value, and
update the Circle radius calculation to use the appropriate conversion factor
based on the unitOfMeasure (currently hardcoding the acres-to-square-meters
conversion of 4046.86). This ensures the component correctly labels and converts
measurements regardless of their actual unit type.
- Around line 33-40: Remove the explicit `any` type casts on the geom variable
assignment and the centroid function call within the Layer.tsx geometry handling
logic. Instead of casting feature.geometry as any, determine the proper type for
the geometry object based on the GeoJSON or geometry library types being used.
Similarly, replace the centroid function call cast with the appropriate type
that reflects what the centroid function accepts and returns. This will restore
compile-time type safety in this hot rendering path and eliminate ESLint
warnings.
🪄 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: bef521bf-ae71-4e9d-8302-6344794036fc

📥 Commits

Reviewing files that changed from the base of the PR and between 48e02f4 and eb74a3c.

📒 Files selected for processing (1)
  • src/components/Map/Layer.tsx

Comment on lines +33 to +40
const geom = feature.geometry as any
let coordinates: number[] = []
if (geom?.type === 'Point' && Array.isArray(geom.coordinates) && typeof geom.coordinates[0] === 'number') {
coordinates = geom.coordinates as number[]
} else {
try {
const c = centroid(feature as any)
coordinates = c?.geometry?.coordinates ?? []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove any casts in geometry handling to avoid lint/build failures.

Lines 33 and 39 use explicit any, which is currently flagged by ESLint and bypasses compile-time safety in the hot path for map rendering.

Suggested typed fix
-import { type Point } from 'geojson'
+import type { Feature, Geometry, Point } from 'geojson'

-      const geom = feature.geometry as any
+      const geom = feature.geometry as Geometry | null

...
-          const c = centroid(feature as any)
+          const c = centroid(feature as Feature)
🧰 Tools
🪛 ESLint

[error] 33-33: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)


[error] 39-39: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 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 `@src/components/Map/Layer.tsx` around lines 33 - 40, Remove the explicit `any`
type casts on the geom variable assignment and the centroid function call within
the Layer.tsx geometry handling logic. Instead of casting feature.geometry as
any, determine the proper type for the geometry object based on the GeoJSON or
geometry library types being used. Similarly, replace the centroid function call
cast with the appropriate type that reflects what the centroid function accepts
and returns. This will restore compile-time type safety in this hot rendering
path and eliminate ESLint warnings.

Source: Linters/SAST tools

Comment on lines +35 to 47
if (geom?.type === 'Point' && Array.isArray(geom.coordinates) && typeof geom.coordinates[0] === 'number') {
coordinates = geom.coordinates as number[]
} else {
try {
const c = centroid(feature as any)
coordinates = c?.geometry?.coordinates ?? []
} catch {
coordinates = []
}
}

const center = coordinates.length >= 2 ? [coordinates[1], coordinates[0]] as [number, number] : undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden coordinate validation before building center.

Current checks can accept partially invalid coordinate tuples (e.g., non-numeric coordinates[1]), which can propagate invalid centers to Leaflet.

Suggested guard
-      if (geom?.type === 'Point' && Array.isArray(geom.coordinates) && typeof geom.coordinates[0] === 'number') {
+      if (
+        geom?.type === 'Point' &&
+        Array.isArray(geom.coordinates) &&
+        Number.isFinite(geom.coordinates[0]) &&
+        Number.isFinite(geom.coordinates[1])
+      ) {
         coordinates = geom.coordinates as number[]
       } else {
...
-      const center = coordinates.length >= 2 ? [coordinates[1], coordinates[0]] as [number, number] : undefined
+      const center =
+        coordinates.length >= 2 &&
+        Number.isFinite(coordinates[0]) &&
+        Number.isFinite(coordinates[1])
+          ? [coordinates[1], coordinates[0]] as [number, number]
+          : undefined
📝 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.

Suggested change
if (geom?.type === 'Point' && Array.isArray(geom.coordinates) && typeof geom.coordinates[0] === 'number') {
coordinates = geom.coordinates as number[]
} else {
try {
const c = centroid(feature as any)
coordinates = c?.geometry?.coordinates ?? []
} catch {
coordinates = []
}
}
const center = coordinates.length >= 2 ? [coordinates[1], coordinates[0]] as [number, number] : undefined
if (
geom?.type === 'Point' &&
Array.isArray(geom.coordinates) &&
Number.isFinite(geom.coordinates[0]) &&
Number.isFinite(geom.coordinates[1])
) {
coordinates = geom.coordinates as number[]
} else {
try {
const c = centroid(feature as any)
coordinates = c?.geometry?.coordinates ?? []
} catch {
coordinates = []
}
}
const center =
coordinates.length >= 2 &&
Number.isFinite(coordinates[0]) &&
Number.isFinite(coordinates[1])
? [coordinates[1], coordinates[0]] as [number, number]
: undefined
🧰 Tools
🪛 ESLint

[error] 39-39: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 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 `@src/components/Map/Layer.tsx` around lines 35 - 47, The coordinate validation
before building the `center` variable only checks the length of the coordinates
array but doesn't validate that both `coordinates[0]` and `coordinates[1]` are
actually numbers. This can allow invalid (non-numeric) coordinate values to be
passed to Leaflet. Strengthen the condition that constructs the `center`
variable to verify not only that the coordinates array has at least 2 elements
but also that both `coordinates[0]` and `coordinates[1]` are valid numbers using
typeof checks, similar to the validation already applied in the Point geometry
check above.

Comment on lines +59 to +65
<p>Current Acreage: {feature.properties?.measurement ?? ''}</p>
<p>Restoration Services: {(feature.properties?.restorationServices ?? []).join(', ')}</p>
</Popup>
<Circle
center={[coordinates[1], coordinates[0]]}
radius={Math.sqrt(feature.properties?.measurement * 4046.86)} // 1 acre = 4046.86 m^2
/>
{center && (
<Circle
center={center}
radius={Math.sqrt((feature.properties?.measurement ?? 0) * 4046.86)} // 1 acre = 4046.86 m^2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use unitOfMeasure when labeling and converting measurement.

The component currently hardcodes acreage semantics (Current Acreage, acres→m² conversion), which conflicts with the new measurement + unitOfMeasure contract and can render incorrect popup text and circle radius.

Suggested contract-aligned update
-          <p>Current Acreage: {feature.properties?.measurement ?? ''}</p>
+          <p>
+            Measurement: {feature.properties?.measurement ?? ''} {feature.properties?.unitOfMeasure ?? ''}
+          </p>

-            radius={Math.sqrt((feature.properties?.measurement ?? 0) * 4046.86)} // 1 acre = 4046.86 m^2
+            radius={Math.sqrt(
+              (feature.properties?.unitOfMeasure === 'acre'
+                ? (feature.properties?.measurement ?? 0) * 4046.86
+                : 0)
+            )}
🤖 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 `@src/components/Map/Layer.tsx` around lines 59 - 65, The popup label and
circle radius calculation in the Layer component are hardcoded for acreage,
which conflicts with the new measurement + unitOfMeasure contract. Replace the
static "Current Acreage" label in the p tag with a dynamic label that uses the
feature.properties?.unitOfMeasure value, and update the Circle radius
calculation to use the appropriate conversion factor based on the unitOfMeasure
(currently hardcoding the acres-to-square-meters conversion of 4046.86). This
ensures the component correctly labels and converts measurements regardless of
their actual unit type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant