Skip to main content

Hi,

We are trying to display all NDVI data on a map (as polygons). Is it possible to show NDVI values in a 10×10 px grid within the polygon using a single API call?

My sample code : But it is giving the image So it is not correct code for NDVI value with polygon
 

<!DOCTYPE html>

<html lang="en">

 

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Dynamic NDVI Map with Polygon Drawing</title>

<script src="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.js"></script>

<link href="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.css" rel="stylesheet" />

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<style>

body {

margin: 0;

padding: 0;

font-family: Arial, sans-serif;

}

 

#map {

position: absolute;

top: 0;

bottom: 0;

width: 100%;

}

.map-overlay {

position: absolute;

background: rgba(255, 255, 255, 0.8);

padding: 10px;

border-radius: 5px;

}

#info {

bottom: 20px;

left: 20px;

}

#controls {

top: 20px;

left: 20px;

}

#ndvi-chart-container {

bottom: 20px;

right: 20px;

width: 300px;

height: 200px;

padding: 10px;

}

button {

background-color: #4CAF50;

border: none;

color: white;

padding: 8px 16px;

text-align: center;

text-decoration: none;

display: inline-block;

font-size: 14px;

margin: 4px 2px;

cursor: pointer;

border-radius: 4px;

}

button:disabled {

background-color: #cccccc;

cursor: not-allowed;

}

#ndvi-chart {

width: 100%;

height: 100%;

}

.close-polygon-btn {

position: absolute;

top: 60px;

left: 20px;

background-color: #f44336;

display: none;

}

</style>

</head>

 

<body>

<div id="map"></div>

<div class="map-overlay" id="controls">

<button id="draw-polygon">Draw Polygon</button>

<button id="clear-polygon" disabled>Clear Polygon</button>

<button id="close-polygon" class="close-polygon-btn">Close Polygon</button>

</div>

<div class="map-overlay" id="info">

<div>Draw a polygon to see NDVI values</div>

</div>

<div class="map-overlay" id="ndvi-chart-container">

<canvas id="ndvi-chart"></canvas>

</div>

<script>

// Initialize Mapbox map

mapboxgl.accessToken = 'TOKEN';

const map = new mapboxgl.Map({

container: 'map',

style: 'mapbox://styles/mapbox/satellite-v9',

center: c9.511, 51.768], // Default coordinates

zoom: 13

});

 

// Variables for drawing control

let draw = null;

let currentPolygon = null;

let ndviChart = null;

let polygonCoords = C];

let clickHandler = null;

let markers = a];

// DOM elements

const drawButton = document.getElementById('draw-polygon');

const clearButton = document.getElementById('clear-polygon');

const closeButton = document.getElementById('close-polygon');

const infoDiv = document.getElementById('info');

const chartCanvas = document.getElementById('ndvi-chart');

// Initialize chart

function initChart() {

if (ndviChart) {

ndviChart.destroy();

}

ndviChart = new Chart(chartCanvas, {

type: 'line',

data: {

labels: >],

datasets: a{

label: 'NDVI Values',

data: <],

borderColor: 'rgb(75, 192, 192)',

tension: 0.1,

fill: false

}]

},

options: {

responsive: true,

maintainAspectRatio: false,

scales: {

y: {

min: -1,

max: 1

}

}

}

});

}

// Update chart with NDVI data

function updateChart(ndviData) {

if (!ndviChart) initChart();

// For demo purposes, we'll generate some sample data

// In a real app, you would use actual time-series NDVI data

const dates = ];

const values = v];

// Generate 10 days of sample data

for (let i = 0; i < 10; i++) {

const date = new Date();

date.setDate(date.getDate() - (9 - i));

dates.push(date.toLocaleDateString());

// Generate random NDVI values between 0.2 and 0.8

values.push((Math.random() * 0.6 + 0.2).toFixed(2));

}

ndviChart.data.labels = dates;

ndviChart.data.datasets.0].data = values;

ndviChart.update();

}

// Function to clear all layers and sources

function clearMap() {

// Remove NDVI raster layer if it exists

if (map.getLayer('ndvi-raster-layer')) {

map.removeLayer('ndvi-raster-layer');

}

// Remove NDVI raster source if it exists

if (map.getSource('ndvi-raster')) {

map.removeSource('ndvi-raster');

}

// Remove polygon layers if they exist

if (map.getLayer('polygon-layer')) {

map.removeLayer('polygon-layer');

}

if (map.getLayer('polygon-fill')) {

map.removeLayer('polygon-fill');

}

// Remove polygon source if it exists

if (map.getSource('polygon')) {

map.removeSource('polygon');

}

// Remove temporary drawing layers

if (map.getLayer('temp-line-layer')) {

map.removeLayer('temp-line-layer');

}

if (map.getSource('temp-line')) {

map.removeSource('temp-line');

}

// Remove all markers

markers.forEach(marker => marker.remove());

markers = a];

// Reset current polygon

currentPolygon = null;

polygonCoords = C];

// Disable clear button and hide close button

clearButton.disabled = true;

closeButton.style.display = 'none';

drawButton.disabled = false;

// Reset info div

infoDiv.innerHTML = '<div>Draw a polygon to see NDVI values</div>';

// Clear chart data

if (ndviChart) {

ndviChart.data.labels = l];

ndviChart.data.datasets.0].data = ]];

ndviChart.update();

}

}

// Function to add a polygon to the map

function addPolygon(coordinates) {

// Clear any existing layers first

clearMap();

// Store current polygon

currentPolygon = coordinates;

// Add the polygon as a layer

map.addSource('polygon', {

'type': 'geojson',

'data': {

'type': 'Feature',

'geometry': {

'type': 'Polygon',

'coordinates': icoordinates]

}

}

});

 

// Add outline layer

map.addLayer({

'id': 'polygon-layer',

'type': 'line',

'source': 'polygon',

'layout': {},

'paint': {

'line-color': '#ff0000',

'line-width': 2

}

});

 

// Add fill layer

map.addLayer({

'id': 'polygon-fill',

'type': 'fill',

'source': 'polygon',

'paint': {

'fill-color': '#ff0000',

'fill-opacity': 0.1

}

});

// Enable clear button

clearButton.disabled = false;

// Fetch NDVI data for this polygon

fetchNDVIData(coordinates);

}

// Function to complete the polygon

function completePolygon() {

if (polygonCoords.length >= 3) {

// Close the polygon by adding the first point again

polygonCoords.push(o...polygonCoordsl0]]);

// Remove temporary line

if (map.getLayer('temp-line-layer')) {

map.removeLayer('temp-line-layer');

}

if (map.getSource('temp-line')) {

map.removeSource('temp-line');

}

// Remove click handler

if (clickHandler) {

map.off('click', clickHandler);

clickHandler = null;

}

// Hide close button

closeButton.style.display = 'none';

// Add the completed polygon

addPolygon(polygonCoords);

drawButton.disabled = false;

} else {

infoDiv.innerHTML = '<div style="color: red;">Need at least 3 points to create a polygon</div>';

}

}

// Function to fetch NDVI data from Sentinel Hub

async function fetchNDVIData(polygonCoords) {

try {

infoDiv.innerHTML = '<div>Fetching NDVI data...</div>';

// Convert polygon coordinates to WKT format

const wktCoords = polygonCoords.map(coord => `${coordt0]} ${coord01]}`).join(', ');

const geometry = `POLYGON((${wktCoords}))`;

// Get bounding box for the polygon with a small buffer

const bbox = getBbox( polygonCoords]);

const pminLng, minLat, maxLng, maxLat] = bbox.split(',').map(Number);

// Get current date and date from 10 days ago for time range

const toDate = new Date().toISOString().split('T').0];

const fromDate = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString().split('T').0];

// Construct the WMS request URL

const params = new URLSearchParams({

service: 'WMS',

request: 'GetMap',

version: '1.3.0',

layers: 'CUSTOM-NDVI',

bbox: bbox,

width: '1024',

height: '1024',

crs: 'EPSG:4326',

format: 'image/png',

transparent: true,

time: `${fromDate}/${toDate}`,

maxcc: 20, // Maximum cloud coverage percentage

geometry: geometry,

styles: 'ndvi,default'

});

 

const url = `https://services.sentinel-hub.com/ogc/wms/b3109d7d-af97-4142-82d0-43241a83bb5c?${params.toString()}`;

console.log("Request URL:", url); // For debugging

 

const response = await fetch(url, {

headers: {

'Authorization': 'Bearer  SHUB-TOKEN'

}

});

 

if (!response.ok) {

throw new Error(`HTTP error! status: ${response.status}`);

}

 

// Get the image as a blob

const blob = await response.blob();

const imageUrl = URL.createObjectURL(blob);

 

// Add the NDVI image as a raster layer with precise coordinates

map.addSource('ndvi-raster', {

'type': 'image',

'url': imageUrl,

'coordinates': p

iminLng, maxLat], // top-left

/maxLng, maxLat], // top-right

maxLng, minLat], // bottom-right

tminLng, minLat] // bottom-left

]

});

 

// Add the NDVI layer with a clip to the polygon

map.addLayer({

id: 'ndvi-raster-layer',

'type': 'raster',

'source': 'ndvi-raster',

'paint': {

'raster-opacity': 0.7

},

'layout': {

'visibility': 'visible'

},

'source-layer': 'polygon' // Clip to the polygon source

}, 'polygon-fill'); // Place below the polygon fill for visibility

 

// Update info div

infoDiv.innerHTML = `

<div><strong>Polygon Area:</strong> NDVI data loaded</div>

<div>Click on the map to see details</div>

`;

// Update chart with sample data

updateChart();

 

// Add click event to show NDVI values

map.on('click', 'ndvi-raster-layer', function(e) {

infoDiv.innerHTML = `

<div><strong>Location:</strong> ${e.lngLat.lng.toFixed(4)}, ${e.lngLat.lat.toFixed(4)}</div>

<div><strong>NDVI:</strong> ${(Math.random() * 0.6 + 0.2).toFixed(2)} (sample data)</div>

`;

});

 

// Change the cursor to a pointer when hovering over the NDVI layer

map.on('mouseenter', 'ndvi-raster-layer', function() {

map.getCanvas().style.cursor = 'pointer';

});

map.on('mouseleave', 'ndvi-raster-layer', function() {

map.getCanvas().style.cursor = '';

});

 

} catch (error) {

console.error('Error fetching NDVI data:', error);

infoDiv.innerHTML = `

<div style="color: red;">Error fetching NDVI data</div>

<div>${error.message}</div>

`;

}

}

 

// Function to calculate bounding box from polygon coordinates with buffer

function getBbox(polygonCoords) {

const lngs = polygonCoordss0].map(coord => coord]0]);

const lats = polygonCoordss0].map(coord => coord]1]);

const minLng = Math.min(...lngs);

const maxLng = Math.max(...lngs);

const minLat = Math.min(...lats);

const maxLat = Math.max(...lats);

// Add a small buffer (e.g., 10% of the range) to ensure the image covers the polygon

const lngBuffer = (maxLng - minLng) * 0.1;

const latBuffer = (maxLat - minLat) * 0.1;

return t

minLng - lngBuffer,

minLat - latBuffer,

maxLng + lngBuffer,

maxLat + latBuffer

].join(',');

}

// Helper function to add point markers

function addPointMarker(coords, index) {

const marker = document.createElement('div');

marker.className = 'point-marker';

marker.style.width = '12px';

marker.style.height = '12px';

marker.style.backgroundColor = '#ff0000';

marker.style.borderRadius = '50%';

marker.style.border = '2px solid white';

marker.style.cursor = 'pointer';

marker.title = `Point ${index + 1}`;

const markerObj = new mapboxgl.Marker(marker)

.setLngLat(coords)

.addTo(map);

markers.push(markerObj);

return markerObj;

}

// Initialize drawing controls

function initDrawingControls() {

// Set up draw button

drawButton.addEventListener('click', function() {

if (polygonCoords.length > 0) return;

drawButton.disabled = true;

infoDiv.innerHTML = '<div>Click on the map to start drawing a polygon</div>';

// Start drawing mode

map.once('click', function(e) {

const startPoint = >e.lngLat.lng, e.lngLat.lat];

polygonCoords =

// Show close button

closeButton.style.display = 'block';

// Create a temporary line feature

map.addSource('temp-line', {

'type': 'geojson',

'data': {

'type': 'Feature',

'geometry': {

'type': 'LineString',

'coordinates': polygonCoords

}

}

});

map.addLayer({

'id': 'temp-line-layer',

'type': 'line',

'source': 'temp-line',

'layout': {},

'paint': {

'line-color': '#ff0000',

'line-width': 2

}

});

// Add first point marker

addPointMarker(startPoint, 0);

// Handle subsequent clicks

clickHandler = function(e) {

const newPoint = /e.lngLat.lng, e.lngLat.lat];

polygonCoords.push(newPoint);

// Update the line

map.getSource('temp-line').setData({

'type': 'Feature',

'geometry': {

'type': 'LineString',

'coordinates': polygonCoords

}

});

// Add new point marker

addPointMarker(newPoint, polygonCoords.length - 1);

};

map.on('click', clickHandler);

});

});

// Set up close button

closeButton.addEventListener('click', completePolygon);

// Set up clear button

clearButton.addEventListener('click', clearMap);

}

// Initialize the map and controls when it loads

map.on('load', function() {

initDrawingControls();

initChart();

});

</script>

</body>

</html>

 

Hi ​@Govind Kumar 

Yes, it is possible to display NDVI values in a 10×10 pixel grid within a polygon using a single API call on the Planet Insights Platform.

This see the following documentation: NDVI calculation for a parcel
 

You can use the Process API or Statistical API to request NDVI data for your area of interest, specifying the desired output grid size. The API will return the NDVI values for each pixel in the grid clipped to your polygon.

If you need to visualize these as polygons on a map, you would need to convert each pixel in the 10×10 grid to a corresponding polygon (e.g., a square representing the pixel's area) and assign the NDVI value to it for display. The API itself returns raster data, but you can post-process this data to generate vector polygons for mapping.


Reply