Skip to content

feat: NDVI Satellite Imagery Integration (Issue #53)#62

Open
Excalibur677 wants to merge 5 commits into
jpdevhub:mainfrom
Excalibur677:feature/ndvi-satellite-imagery
Open

feat: NDVI Satellite Imagery Integration (Issue #53)#62
Excalibur677 wants to merge 5 commits into
jpdevhub:mainfrom
Excalibur677:feature/ndvi-satellite-imagery

Conversation

@Excalibur677

@Excalibur677 Excalibur677 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Hey @jpdevhub

Here's my implementation for #53.

What's changed

Backend

  • Added GET /api/ndvi endpoint using Sentinel Hub's Python SDK
  • Results are cached in Supabase for 24 hours to avoid hitting API limits on every load
  • New migration 20260616000000_ndvi_cache.sql creates the ndvi_cache table

Frontend

  • Show NDVI / Hide NDVI toggle button added to the farm map
  • NDVI overlay renders on the Leaflet map with an opacity slider
  • Color-scale legend shows red → green with the farm's average NDVI and a health label
  • Lazy-loaded with next/dynamic to avoid SSR issues with Leaflet

Setup

Add these to .env (already in .env.example):

  • SENTINEL_HUB_CLIENT_ID=your_client_id
  • SENTINEL_HUB_CLIENT_SECRET=your_client_secret

Then run:
supabase db push

Free-tier Sentinel Hub account is enough for testing.

Closes #53

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

@Excalibur677 is attempting to deploy a commit to the karan3431's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Excalibur677, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 12 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bae5188-bd2c-4b02-959f-d36293325756

📥 Commits

Reviewing files that changed from the base of the PR and between c6ac88d and 335c637.

📒 Files selected for processing (8)
  • .env.example
  • backend/main.py
  • backend/requirements.txt
  • backend/supabase/migrations/20260616000000_ndvi_cache.sql
  • frontend/src/components/FarmMap.tsx
  • frontend/src/components/NDVILayer.tsx
  • frontend/src/components/NDVILegend.tsx
  • frontend/src/hooks/useNDVI.ts
📝 Walkthrough

Walkthrough

Adds full-stack NDVI satellite imagery support. A new ndvi_cache Postgres table stores computed results. A new GET /api/ndvi backend endpoint authenticates farm ownership, returns a cached result if under 24 hours old, or calls Sentinel Hub to compute min/max/mean NDVI. Frontend adds a useNDVI hook, NDVILayer Leaflet overlay, NDVILegend, and a toggle button in FarmMap.

Changes

NDVI Satellite Imagery via Sentinel Hub

Layer / File(s) Summary
Database cache table and backend configuration
backend/supabase/migrations/20260616000000_ndvi_cache.sql, backend/requirements.txt, .env.example
Creates the ndvi_cache table with farm_id FK (cascade delete), date range, optional NDVI statistics as numeric(6,4), a bbox JSONB field, and two indexes. Adds sentinelhub>=3.10.0 dependency and SENTINEL_HUB_CLIENT_ID/SENTINEL_HUB_CLIENT_SECRET credential placeholders.
NDVIRequest model and GET /api/ndvi endpoint
backend/main.py
Adds the NDVIRequest Pydantic model and an authenticated GET /api/ndvi route that verifies farm ownership, serves a fresh cached result from ndvi_cache when available, otherwise requests Sentinel-2 L2A NDVI from Sentinel Hub, computes statistics from the returned TIFF, writes to ndvi_cache, and raises HTTP errors for access, coordinate, credential, and Sentinel Hub failures.
useNDVI data-fetching hook
frontend/src/hooks/useNDVI.ts
New hook managing ndviData, loading, and error state. Exposes fetchNDVI(dateFrom?, dateTo?) that calls GET /api/ndvi, extracts err.response.data.detail on failure, and always resets loading in a finally block.
NDVILegend and NDVILayer overlay components
frontend/src/components/NDVILegend.tsx, frontend/src/components/NDVILayer.tsx
NDVILegend renders a fixed color/label list and computes a health label from ndviMean thresholds. NDVILayer gates rendering on visible, fetches via useNDVI, shows loading/error panels, and when data is available mounts an NDVIOverlay (canvas gradient Leaflet imageOverlay with reactive setOpacity) plus an opacity slider.
FarmMap toggle button and NDVILayer wiring
frontend/src/components/FarmMap.tsx
Lazy-loads NDVILayer with ssr: false, adds ndviVisible state, conditionally renders a Show/Hide NDVI toggle button when farmId is set, and mounts <NDVILayer> inside the map when both farmId and ndviVisible are true.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FarmMap
    participant NDVILayer
    participant useNDVI
    participant BackendAPI
    participant SentinelHub

    rect rgba(34, 139, 34, 0.5)
        Note over User,FarmMap: Toggle overlay
        User->>FarmMap: Click "Show NDVI"
        FarmMap->>NDVILayer: render with visible=true
    end

    rect rgba(70, 130, 180, 0.5)
        Note over NDVILayer,BackendAPI: Data fetch
        NDVILayer->>useNDVI: fetchNDVI(dateFrom, dateTo)
        useNDVI->>BackendAPI: GET /api/ndvi?farm_id=...
        alt ndvi_cache hit (<24h)
            BackendAPI-->>useNDVI: cached ndvi_min/max/mean + bbox
        else cache miss
            BackendAPI->>SentinelHub: Sentinel-2 L2A evalscript
            SentinelHub-->>BackendAPI: TIFF data
            BackendAPI->>BackendAPI: compute NDVI stats
            BackendAPI-->>useNDVI: computed ndvi_min/max/mean + bbox
        end
        useNDVI-->>NDVILayer: ndviData
    end

    rect rgba(184, 134, 11, 0.5)
        Note over NDVILayer,User: Render overlay
        NDVILayer->>NDVILayer: draw canvas gradient → Leaflet imageOverlay
        NDVILayer->>User: show NDVILegend + opacity slider
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hop, hop! The fields glow green and bright,
Sentinel watches from satellite height.
NDVI paints the farm's health in a flash,
Cached in Postgres to spare the bandwidth clash.
The rabbit slides opacity left and right—
🌿 Lush vegetation, what a joyful sight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change—introducing NDVI Satellite Imagery Integration to the system—matching the comprehensive changeset across backend, database, and frontend components.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Excalibur677!

Please make sure CI passes and the checklist in the PR template is complete. A maintainer will review this soon.

— The AgroNavis team

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

🧹 Nitpick comments (1)
backend/supabase/migrations/20260616000000_ndvi_cache.sql (1)

13-14: ⚡ Quick win

Use a composite index for the actual cache lookup path.

The current lookup pattern is farm_id filter + created_at DESC sort + LIMIT 1. A composite index will avoid extra sorting work as cache rows grow.

⚙️ Suggested migration tweak
-create index if not exists ndvi_cache_farm_id_idx on ndvi_cache(farm_id);
-create index if not exists ndvi_cache_created_at_idx on ndvi_cache(created_at desc);
+create index if not exists ndvi_cache_farm_created_at_idx
+    on ndvi_cache(farm_id, created_at desc);
🤖 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 `@backend/supabase/migrations/20260616000000_ndvi_cache.sql` around lines 13 -
14, Replace the two separate indexes ndvi_cache_farm_id_idx and
ndvi_cache_created_at_idx with a single composite index on the (farm_id,
created_at desc) column combination. This composite index will optimize the
actual cache lookup query pattern which filters by farm_id, orders by created_at
descending, and limits to 1 result, eliminating the need for additional sorting
work as the table grows.
🤖 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 `@backend/main.py`:
- Around line 860-865: The cache lookup in the supabase table select ignores the
requested date_from and date_to parameters, allowing mismatched cache hits
across different date ranges. Additionally, the cached response returns raw
table rows while fresh responses include a resolution field, creating an
inconsistent API schema. Fix this by including date_from and date_to in the
cache query filtering logic alongside farm_id, and normalize the cached response
object to include the same fields (such as resolution) that are present in fresh
responses before returning it.
- Around line 883-884: The coordinate validation at the farm location check uses
truthiness evaluation with `if not lat or not lng:`, which incorrectly rejects
valid zero values for coordinates on the equator or prime meridian. Replace the
truthiness checks with explicit `None` comparisons so that `0.0` values are
treated as valid coordinates while still catching missing (None) values. Change
the condition to check `if lat is None or lng is None:` instead.
- Around line 922-925: Add a guard check before the lines that access
ndvi_data[0] to ensure that ndvi_data is not empty. Before assigning ndvi_array
from ndvi_data[0], verify that ndvi_data has at least one element; if it is
empty, handle this gracefully by either returning an appropriate error response
or setting default values. This prevents the IndexError from being raised when
Sentinel Hub returns no scenes for the requested interval.

In `@frontend/src/components/NDVILayer.tsx`:
- Around line 22-38: The canvas gradient being created in the NDVILayer
component is static and does not represent actual NDVI data values from the
satellite/raster source. Replace the hardcoded canvas-based gradient approach
with either (1) a real raster/tiles data source from the backend (or Sentinel
Hub WMS/WCS/Process output) that maps actual pixel values to the color scale, or
(2) if that is out of scope for this PR, update the layer label and any
documentation to clearly indicate that this is an illustrative overlay rather
than actual NDVI imagery. The current implementation where a fixed gradient is
created, converted to a data URL via canvas.toDataURL(), and then passed to
L.imageOverlay() needs to be replaced with logic that either fetches and renders
real data or appropriately disclaims the synthetic nature of the visualization.

In `@frontend/src/components/NDVILegend.tsx`:
- Around line 35-37: The legend labels in NDVILegend.tsx contain corrupted
characters (displayed as `�`) in the range specifications within the labels for
entries like 'Moderate (0.4–0.6)', 'Sparse (0.2–0.4)', and elsewhere. Replace
all occurrences of the corrupted `�` character with the correct dash character
(en-dash or minus sign) in the range labels throughout the file. This issue
appears at lines 35-37 in the legend color definitions and also at line 49 in
the average line label, so ensure both locations are corrected to display the
range separators properly.

---

Nitpick comments:
In `@backend/supabase/migrations/20260616000000_ndvi_cache.sql`:
- Around line 13-14: Replace the two separate indexes ndvi_cache_farm_id_idx and
ndvi_cache_created_at_idx with a single composite index on the (farm_id,
created_at desc) column combination. This composite index will optimize the
actual cache lookup query pattern which filters by farm_id, orders by created_at
descending, and limits to 1 result, eliminating the need for additional sorting
work as the table grows.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 428abb86-8818-4cf7-90c4-9dfcf41eafb3

📥 Commits

Reviewing files that changed from the base of the PR and between fe43800 and c6ac88d.

📒 Files selected for processing (8)
  • .env.example
  • backend/main.py
  • backend/requirements.txt
  • backend/supabase/migrations/20260616000000_ndvi_cache.sql
  • frontend/src/components/FarmMap.tsx
  • frontend/src/components/NDVILayer.tsx
  • frontend/src/components/NDVILegend.tsx
  • frontend/src/hooks/useNDVI.ts

Comment thread backend/main.py Outdated
Comment thread backend/main.py Outdated
Comment thread backend/main.py
Comment thread frontend/src/components/NDVILayer.tsx
Comment thread frontend/src/components/NDVILegend.tsx Outdated
@Excalibur677 Excalibur677 force-pushed the feature/ndvi-satellite-imagery branch from c6ac88d to b9a3ee9 Compare June 16, 2026 06:34
@jpdevhub

Copy link
Copy Markdown
Owner

Sentinel hub is a paid one try to get the free open source alternative

@jpdevhub

Copy link
Copy Markdown
Owner

Hey, this feature is incredibly well-written, and the caching logic is brilliant! However, since this is an open-source project, we want to avoid relying on paid commercial APIs like Sentinel Hub. Could we refactor the backend/main.py endpoint to pull the Sentinel-2 data for free using the AWS Open Data Registry via STAC (pystac-client + rasterio) or Google Earth Engine instead?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NDVI Satellite Imagery Integration

2 participants