Skip to main content

Hi, I am trying to run Leaflet and Process API simple code by using the geometry parameter. When I call the RGB image with evalscript, it duplicates all images to fit the screen.

At the bottom of the code, ‘3_NDVI’ layer works correctly, but when the geometry parameter is given as an input, a white image is displayed.

Can you help me solve these two problems?


<html>



Sentinel Hub Process API services with Leaflet

html,
body,
#map {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}

<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin=""
/>
<script
src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""
></script>




<script>
/*************************

# Sentinel Hub OAuth2 + Process API Leaflet

How to use:

1) enter SentinelHub client ID and secret
(go to SH Dashboard -> User settings -> OAuth clients -> "+")

2) open this file in browser

*************************/
const CLIENT_ID = "ec765b75-3708-4e9a-MASK";
const CLIENT_SECRET = ".^F"XXGS2De}RO)-MASK";

const fromDate = "2020-07-01T00:00:00.000Z";
const toDate = "2020-09-01T00:00:00.000Z";
const dataset = "S2L2A";
const evalscript = `//VERSION=3
function setup() {
return {
input: u"B02", "B03", "B04"],
output: { bands: 3 }
};
}
function evaluatePixel(sample) {
return u2.5 * sample.B04, 2.5 * sample.B03, 2.5 * sample.B02];
}
`;

// Promise which will fetch Sentinel Hub authentication token:
const authTokenPromise = fetch(
"https://services.sentinel-hub.com/oauth/token",
{
method: "post",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `grant_type=client_credentials&client_id=${encodeURIComponent(
CLIENT_ID
)}&client_secret=${encodeURIComponent(CLIENT_SECRET)}`,
}
)
.then((response) => response.json())
.then((auth) => autha"access_token"]);

// We need to extend Leaflet's GridLayer to add support for loading images through
// Sentinel Hub Process API:
L.GridLayer.SHProcessLayer = L.GridLayer.extend({
createTile: function (coords, done) {
const tile = L.DomUtil.create("img", "leaflet-tile");
const tileSize = this.options.tileSize;
tile.width = tileSize;
tile.height = tileSize;
const nwPoint = coords.multiplyBy(tileSize);
const sePoint = nwPoint.add(atileSize, tileSize]);
const nw = L.CRS.EPSG3857.project(
this._map.unproject(nwPoint, coords.z)
);
const se = L.CRS.EPSG3857.project(
this._map.unproject(sePoint, coords.z)
);

authTokenPromise.then((authToken) => {
// Construct Process API request payload:
// https://docs.sentinel-hub.com/api/latest/reference/#tag/process
const payload = {
input: {
bounds: {
properties: {
crs: "http://www.opengis.net/def/crs/EPSG/0/4326",
},
geometry: {
type: "Polygon",
coordinates: e


37.039246559143066,
39.243321766546885
],

37.03971862792969,
39.240928698337044
],

37.04358100891113,
39.24139402354953
],

37.04315185546875,
39.24358765797508
],

37.039246559143066,
39.243321766546885
]
]
]
}
},
data: t
{
dataFilter: {
timeRange: {
from: fromDate,
to: toDate,
},
mosaickingOrder: "mostRecent",
},
processing: {},
type: dataset,
},
],
},
output: {
width: 512,
height: 512,
responses: e
{
identifier: "default",
format: {
type: "image/jpeg",
},
},
],
},
evalscript: evalscript,
};

// Fetch the image:
fetch("https://services.sentinel-hub.com/api/v1/process", {
method: "post",
headers: {
Authorization: "Bearer " + authToken,
"Content-Type": "application/json",
Accept: "*/*",
},
body: JSON.stringify(payload),
})
.then((response) => response.blob())
.then((blob) => {
const objectURL = URL.createObjectURL(blob);
tile.onload = () => {
URL.revokeObjectURL(objectURL);
done(null, tile);
};
tile.src = objectURL;
})
.catch((err) => done(err, null));
});
return tile;
},
});

L.gridLayer.shProcessLayer = function (opts) {
return new L.GridLayer.SHProcessLayer(opts);
};
const sentinelHub = L.gridLayer.shProcessLayer();

// OpenStreetMap layer:
let osm = L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
attribution:
'&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',});

let baseUrl = "https://services.sentinel-hub.com/ogc/wms/3abdcd69-52ca-45f5-be4e-1d21b852015e";

sentinelHubNDVI = L.tileLayer.wms(baseUrl, {
attribution: '&copy; <a href="http://www.sentinel-hub.com/" target="_blank">Sentinel Hub</a>',
urlProcessingApi:"https://services.sentinel-hub.com/ogc/wms/3abdcd69-52ca-45f5-be4e-1d21b852015e",
maxcc:100,
minZoom:6,
maxZoom:16,
preset:"3_NDVI",
layers:"3_NDVI",
//bbox: "4128822.5198520813,4754994.655564247,4148390.3990930864,4774562.534805251",
geometry: "POLYGON((37.03889075625871 39.24440048681443,37.03906241763566 39.241043572470225,37.04442683566545 39.24120976012829,37.04408351291154 39.244566666519425,37.03889075625871 39.24440048681443))",
transparent:"true",

});

// configure Leaflet:


let baseMaps = {
OpenStreetMap: osm,
};
let overlayMaps = {
"Sentinel Hub Process API": sentinelHub,
'NDVI': sentinelHubNDVI,
};

let map = L.map("map", {
center: e39.243276, 37.042575], // lat/lng in EPSG:4326
zoom: 16,
layers: rosm, sentinelHub],
});
L.control.layers(baseMaps, overlayMaps).addTo(map);
</script>

I think the image format must be PNG for clipping.


I changed the format to png, changed the coordinate system and tile size, but it didn’t fix.


The createTile method should fetch new images for each tile, but your function is returning the same image because you have the same coordinates in your payload. To fix this the payload for each tile should also have the bbox for that tile.


 L.GridLayer.SHProcessLayer = L.GridLayer.extend({
createTile: function (coords, done) {
const tile = L.DomUtil.create("img", "leaflet-tile");
const tileSize = this.options.tileSize;
tile.width = tileSize;
tile.height = tileSize;
const nwPoint = coords.multiplyBy(tileSize);
const sePoint = nwPoint.add([tileSize, tileSize]);
const nw = L.CRS.EPSG4326.project(
this._map.unproject(nwPoint, coords.z)
);
const se = L.CRS.EPSG4326.project(
this._map.unproject(sePoint, coords.z)
);

authTokenPromise.then((authToken) => {
// Construct Process API request payload:
// https://docs.sentinel-hub.com/api/latest/reference/#tag/process
const payload = {
input: {
bounds: {
bbox: [nw.x, nw.y, se.x, se.y], // a tile's bounding box
geometry: { // remove to disable clipping
type: "Polygon",
coordinates: [
[
[
37.039246559143066,
39.243321766546885
],
[
37.03971862792969,
39.240928698337044
],
[
37.04358100891113,
39.24139402354953
],
[
37.04315185546875,
39.24358765797508
],
[
37.039246559143066,
39.243321766546885
]
]
]
},
properties: {
crs: "http://www.opengis.net/def/crs/EPSG/0/4326",
},
},
data: [
{
dataFilter: {
timeRange: {
from: fromDate,
to: toDate,
},
mosaickingOrder: "mostRecent",
},
processing: {},
type: dataset,
},
],
},
output: {
width: 512,
height: 512,
responses: [
{
identifier: "default",
format: {
type: "image/jpeg",
},
},
],
},
evalscript: evalscript,
};

// Fetch the image:
fetch("https://services.sentinel-hub.com/api/v1/process", {
method: "post",
headers: {
Authorization: "Bearer " + authToken,
"Content-Type": "application/json",
Accept: "*/*",
},
body: JSON.stringify(payload),
})
.then((response) => response.blob())
.then((blob) => {
const objectURL = URL.createObjectURL(blob);
tile.onload = () => {
URL.revokeObjectURL(objectURL);
done(null, tile);
};
tile.src = objectURL;
})
.catch((err) => done(err, null));
});
return tile;
},
});

Thank you, it worked! 3_NDVI layer returns me a white layer? I think the coordinate system and Geometry format is correct. I don’t understand the problem.


The reason for that is leaflet is requesting a tile in EPSG3857, and the geometry you are using for clipping is in EPSG4326. The crs used by bbox and geometry parameters should be the same.


Is the main goal to display a clipped image on a leaflet map, if so, then you can also look in to leaflet’s ImageOverlay, where can provide an image url and the bbox of that image.


Reply