Calculate altitude using Leaflet - leaflet

I want to calculate altitude of a marker but I didn't find any solution in Leaflet. I found the elevation plugin which allow to draw elevation profil on map, but I don't need that. Do you have idea how to calculate altitude? Thank you.

Edit: A nice option
Adding this for anyone who might want it: since having written this answer, I wrote the plugin leaflet-topography to make doing this very fast and easy.
Original answer:
Not sure if anyone has answered yet but I cracked my head over this for a week and found a nice solution. First, you need to get the leaflet plugin leaflet-tileLayer-colorPicker:
https://github.com/frogcat/leaflet-tilelayer-colorpicker
This plugin enables you to get the color of a certain pixel on the map, given the latitude and longitude. Then, you need a dataset. One that's designed for this is the mapbox Terrain-RGB tiles:
https://docs.mapbox.com/help/troubleshooting/access-elevation-data/
These tiles have elevation data encoded in RGB format. The description on mapbox's site was not step-by-step enough for a beginner like me, so here's how you can do it:
Add the Terrain-RGB tilelayer
var colorPicker = L.tileLayer.colorPicker('https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token={accessToken}', {
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: 'mapbox.outdoors',
accessToken: 'your mapbox accesstoken'
}).addTo(yourMap)
.setOpacity(0); // optional
Create a function which takes in latlng and outputs RGB as an array
function getElevation(location){
var color = colorPicker.getColor(location)
// location is an array [lat, lng]
Pick apart the RGB values of the array and put into mapbox's elevation equation
let R = color[0];
let G = color[1];
let B = color[2];
let height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)
let heightRounded = Math.round( height * 10) / 10 + ' meters';
return heightRounded;
}
That last bit is just to give a nice number rounded to the first decimal, output as a string like "182 meters", but you can write it however you like.

I had a similar problem working on a Shiny app with leaflet and found a workaround using Google Elevation API and google_elevation() function of googleway package
The latter (R part) is probably not in your interest but the Google Elevation API implementation should work for you.

Related

Leaflet convert meters to pixels

I am creating an app using leaflet
I have the leaflet map up and running and have a Geoserver service that return points that are shown on the map.
I have one additional field that is distance between my Points on the Map and it is in meters ?
My question is how could I convert it to pixels? Is there any Leaflet function or anything ?
You can use this function with the L.GeometryUtil library.
L.GeometryUtil CDN
function disToPixeldistance(distance){
var l2 = L.GeometryUtil.destination(map.getCenter(),90,distance);
var p1 = map.latLngToContainerPoint(map.getCenter())
var p2 = map.latLngToContainerPoint(l2)
return p1.distanceTo(p2)
}
But think of that the pixel distance is changing every time you zoom

Atmospheric correction for Sentinel-2 imagery in Google Earth Engine

I want to apply atmospheric correction on Sentinel-2 imagery in Google Earth Engine(GEE). I saw the Sammurphy code which is written in Python and unfortunately it did not work for me. I tried the dark pixel subtraction method using the code(Java) below but it results in a total dark image over my region of interest.
I am new to both Earth Engine and JavaScript. Has anyone tried using the dark pixel subtraction or any other atmospheric correction on Sentinel 2 imagery in GEE (preferably a code written in Java)?
var toa=maskedComposite1;
var thresh= 0.5;
var dark=findDarkPixels(toa, thresh)
print(dark);
//Function to find dark pixels from threshold on sum of NIR, SWIR1, & SWIR2 bands
//Returns classified image with binary [0,1] 'dark' band
// toa: Sentinel 2 image converted to surface radiance
// thresh: threshold (0.2 - 0.5) value for sum of NIR, SWIR1 & SWIR2 bands
function findDarkPixels(toa, thresh) {
var darkPixels1 = toa.select(['B8','B11','B12']);
var darkPixels = darkPixels1.reduce(ee.Reducer.sum()).lt(thresh);
var filtered = darkPixels.focal_mode(0.1, 'square', 'pixels');
Map.addLayer(filtered,{},'darkPixel');
return filtered.rename(['dark']);
}
If you do not need specific atmospheric correction then you can use the Level-2A Sentinel-2 data already available in GEE. Here is the link to dataset info. The atmospheric correction for this data set is performed by sen2cor. Note the time period the data are available for as Level-2A data is not available for the entire data archive.

Can map.getBounds be executed for a different coordinate system?

I execute the following code in my leaflet webmap
map.getBounds().getWest() + "&y1=" +
map.getBounds().getSouth() + "&x2=" +
map.getBounds().getEast() + "&y2=" +
map.getBounds().getNorth()
This results in a result showing me four coordinates in the WGS84 (standard) coordinate system.
Is there any way to alter this so it will output 28992 coordinates instead?
I guess that by "28992 coordinates" you're referring to the EPSG:28992 Coordinate Reference System.
The canonical way to use "non-standard" CRSs in Leaflet is to leverage proj4leaflet. This answer assumes that you're already doing so.
So the getBounds() method of L.Map always returns a L.LatLngBounds instance, which refer to unprojected WGS84 coordinates. However, we can use the map's CRS to project a L.LatLng into a L.Point with the projected coordinates, in the map's display CRS; e.g.
var map = L.map('containerId`, { crs: crsForEpsg28992 });
var foo = map.options.crs.project(L.latLng([60.3,21.1]));
var qux = map.options.crs.project(map.getCenter());
Because of how map projections work (they rotate and bend the coordinate spaces), and because of how proj4js is implemented, it's not possible to project a bounding box into a bounding box. (In most cases, the projection of a bounding box would be a curved polygon!). This image from an article by Gregor Aisch illustrates the issue:
We can, however, do an approximation: project the four corners of the bounding box, e.g.:
var mapBounds = map.getBounds();
var crs = map.options.crs;
var nw = crs.project(mapBounds.getNorthWest());
var ne = crs.project(mapBounds.getNorthEast());
var sw = crs.project(mapBounds.getSouthWest());
var se = crs.project(mapBounds.getSouthEast());
We can even create a L.Bounds (but not a L.LatLngBounds!) from those projected coordinates; that'll be a bbox in the specified CRS that contains all corners, e.g.:
var bbox = L.bounds([nw, ne, sw, se]);
It's not gonna be perfect, but that approximation should be enough for most use cases.
See also this working example (based off on one of the proj4leaflet examples), which should further illustrate the issue.

How to get the edge of a polygon drawn on map within Leaflet

I am working with Leaflet and Leaflet-Draw in Angular to draw some polygons on the Google Map. How can I implement a listener when the user clicks exactly on the edge of the drawn polygons and get the lat and lng of that edge. I know a similar situation can be implemented with Google Map API like the code below, but I can not find any source to help me implement the same thing in Leaflet.
google.maps.event.addListener(polygon, 'click', function (event) { console.log(event.edge) }
Google Map Documentation: https://developers.google.com/maps/documentation/javascript/reference/polygon#PolyMouseEvent
For those who come across this question: I found a solution myself!
I didn't find anything directly from Leaflet draw library that I could use, so I defined the problem for myself as a trigonometry problem and solve it that way.
I defined a function in which on polygon click, it converts the event.latlng and loops over polygon.getLatLngs()[0] taking a pair of A and B points. A is the first coordinates, B is the next and if it reaches to the end of array, B will be the first point. Then using Collinear Function of 3 points with x, y, I checked if the clicked x, y has a same slope as point A and B.(has to be rounded it up), if so, I would save that A and B point pair with their latLng information and further used it in my project.
Although this method works, I would appreciate if anybody would know a better solution or library built-in function that can be used instead. Thanks!
When the user clicks on the polygon you can loop through all corners and check if he clicked in the near of the corner.
poly.on('click', function(e){
var latlng = e.latlng;
var corners = poly.getLatLngs();
if(!L.LineUtil.isFlat(corners)){ //Convert to a flat array
corners = corners[0];
}
//Convert the point to pixels
var point = mymap.latLngToContainerPoint(latlng);
//Loop through each corner
corners.forEach(function(ll){
//Convert the point to pixels
var point1 = mymap.latLngToContainerPoint(ll);
var distance = Math.sqrt(Math.pow(point1.x - point.x, 2) + Math.pow(point.y - point1.y, 2));
//Check if distance between pixels is smaller then 10
if(distance < 10){
console.log('corner clicked');
}
});
});
This is plain JS you have to convert it self to angular.
A alternativ is to place on each corner a DivMarker or a CircleMarker and fire a event if the marker is clicked.
Looks like: https://geoman.io/leaflet-geoman

Real-time Data Visualization using any Earth API

How to create something like on this video (1-2 minutes http://www.ted.com/index.php/talks/sergey_brin_and_larry_page_on_google.html) using "Google Earth API" or some other?
Especially: I have an online game and want to show dynamical data on some "virtual earth". 3 types of objects changing their state in real-time. It's enough to update each 5 seconds. I already have open api for it.
The problem is i do not know if it's possible to draw something like colored lines from a sphere's center and change them dynamically.
Sorry for an abstract question, but the goal is the same.
Well, if you're using the Google Earth API (requires that the Google Earth Plugin be installed), you can just create a bunch of extruded polygons. For example, if you go to the Earth API Interactive Sampler and paste/run this:
var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
var lat = lookAt.getLatitude();
var lng = lookAt.getLongitude();
// first create inner and outer boundaries
// outer boundary is a square
var outerBoundary = ge.createLinearRing('');
var coords = outerBoundary.getCoordinates();
coords.pushLatLngAlt(lat - 0.5, lng - 0.5, 1000000);
coords.pushLatLngAlt(lat - 0.5, lng + 0.5, 1000000);
coords.pushLatLngAlt(lat + 0.5, lng + 0.5, 1000000);
coords.pushLatLngAlt(lat + 0.5, lng - 0.5, 1000000);
// create the polygon and set its boundaries
var polygon = ge.createPolygon('');
polygon.setExtrude(true);
polygon.setAltitudeMode(ge.ALTITUDE_RELATIVE_TO_GROUND);
polygon.setOuterBoundary(outerBoundary);
// create the polygon placemark and add it to Earth
var polygonPlacemark = ge.createPlacemark('');
polygonPlacemark.setGeometry(polygon);
ge.getFeatures().appendChild(polygonPlacemark);
// persist the placemark for other interactive samples
window.placemark = polygonPlacemark;
window.polygonPlacemark = polygonPlacemark;
You'll see a 3D polygon extruded out of the globe.
There's much more you can do with this; I suggest playing with the Earth API and KML (foundation for geometry primitives in the Earth API) by visiting code.google.com/apis/earth and code.google.com/apis/kml.