Converting DOM pixel coordinates to latlng coordinates - leaflet

I am trying to get the latlng coordinates of the bottom left and top right corners of an svg element (orange box in the picture) I overlayed on top of the map. However, the latlng coordinates I am getting from transforming the pixel coordinates is off by a wide margin.
Coordinates I should be getting from this view:
BL: [-18.9, -39.1],
TR: [48.8, 39.3]
Coordinates I am actually getting:
BL: [-30.3, 127.0],
TR: [40.9, 205.9]
This is the code I am using to determine the pixel position:
this.navigation.on('moveend zoomend', () => {
const bounds = L.DomUtil.get("compBorder")!.getBoundingClientRect();
const bl1 = L.point(bounds.left, bounds.bottom)
const tr1 = L.point(bounds.right, bounds.top)
const bl = this.navigation.layerPointToLatLng(bl1);
const tr = this.navigation.layerPointToLatLng(tr1);
this.cs.setBlTr([bl.lat, bl.lng], [tr.lat, tr.lng]);
this.cs.setCenter(this.navigation.getCenter());
this.cs.setZoom(this.navigation.getZoom());
});
From what I am seeing it doesn't seem to make a difference whether I use the layerPointToLatlng() or the containerPointToLatlng() function.

Related

Leaflet - Draw a Circle that fits around a Bounding Box

In Leaflet docs, L.Circle Radius is based on meters, but how can I draw a Circle based on coordinates instead?
More specifically, I want to draw a circle that encompasses a specific bounding box
const myCenter = [37, -122];
const myBoundingBox = {
northEast: [myCenter[0]+2, myCenter[1]+2],
southWest: [myCenter[0]-2, myCenter[1]-2]
};
const circle = L.circle(myCenter, {radius: ??}).addTo(map);
I've figured it out, use map.distance function to get distance in meters between the diagonal points of the bounds
const circleRadius = map.distance(myBoundingBox[northEast], myBoundingBox[southWest])/2
Then just create the circle normally using the radius in meters

Converting from WGS84 to EPSG:27700 raster tiles without drawing a map

Using this example from OS Data Hub - https://labs.os.uk/public/os-data-hub-examples/os-maps-api/zxy-27700-basic-map
I can get a list of tiles displayed on the map, I would like to get the coordinates of the tile without drawing the map.
Starting from a single point in WGS84 (lat/long) I can convert this to EPSG:27700 using Proj4js
var source = new proj4.Proj('EPSG:4326');
proj4.defs("EPSG:27700","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs");
var dest = new proj4.Proj('EPSG:27700');
var coords=proj4.transform(source, dest, [X,Y]);
I then need to translate this into coordinates for the raster tile, which is done in the leaflet example with this code:
var crs = new L.Proj.CRS('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs', {
resolutions: [ 896.0, 448.0, 224.0, 112.0, 56.0, 28.0, 14.0, 7.0, 3.5, 1.75 ],
origin: [ -238375.0, 1376256.0 ]
});
How can i replicate this step to produce the tile coordinates, without having to draw the leaflet map?
I ultimately want to use the coordinates to grab & save a single tile from the OS Data Hub with this format:
https://api.os.uk/maps/raster/v1/zxy/layer/%7Bz%7D/%7Bx%7D/%7By%7D.png?key=
Using the EPSG:27700 coords calculated using proj4, and the zoom level resolutions (which are meters per pixel) and tile grid origin coordinates used in the definition you can calculate the {x} and {y} values in https://api.os.uk/maps/raster/v1/zxy/layer/{z}/{x}/{y}.png?key= for any zoom level {z} based on the standard tile size of 256 pixels as
x = Math.floor((coords[0] - origin[0]) / (resolutions[z] * 256));
y = Math.floor((origin[1] - coords[1]) / (resolutions[z] * 256));

How to change the map center position to top place using Leaflet open street map in angular

In my angular application I have created the leaflet map and over the leaflet map I have created two more panels data in overlapping manner And I have created the circle of 5 km radius on the map. But my problem is the panels are covering the circle on the leaflet map
So my requirement is to move the center position of the map i.e circle to top position(top middle) than only the circle will be visible otherwise it will be covered by the panels on the map.
component.ts
map = L.map('map').setView([13.0827, 80.2707], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
L.control.zoom({
position: 'bottomright',
}).addTo(map);
I am new to leaflet map Can anyone help me regarding this.
You can let the map fit the circle bounds, like:
var circle = L.circle() // Your circle
map.fitBounds(circle.getBounds())
To show the circle on the left side of the map:
map.setView(circle.getLatLng(),map.getZoom(),{animate: false})
sw = map.getBounds().getSouthWest();
psw = map.latLngToContainerPoint(sw);
center = map.getCenter();
pc = map.latLngToContainerPoint(center);
dis = pc.x - psw.x;
middle = dis / 2
pnewCenter = L.point(pc.x+middle,pc.y)
center2 = map.containerPointToLatLng(pnewCenter);
map.setView(center2,map.getZoom(),{animate: true})

Get Bounding Box used By queryRenderedFeatures

I have an application that serves vector tiles. The features in the tiles are clickable. When a user clicks the map, I pass mapbox-gl's queryRenderedFeatures a 5 x 5px bounding box around the clicked point.
Is there a way to ascertain the lat-lon bounding box that mapbox uses to query its cached tiles? I would like to have this bounding box so I can query the database for the features around the clicked point. I can use the ids of the features in the vector tiles, but this becomes cumbersome/untenable when there are 1000s of features.
Here's how I am getting features near point, where:
map is the mapbox map object
mapboxLayers are the names of the layers I want to query
point is point property of the click event
export const getMapFeaturesNearPoint = ({ map, mapboxLayers, point }) => {
const { x, y } = point;
const halfPixels = 5;
// set bbox as 5px rectangle around clicked point
const bbox = [
[x - halfPixels, y - halfPixels],
[x + halfPixels, y + halfPixels],
];
const features = map.queryRenderedFeatures(bbox, { layers: [...mapboxLayers] })
return features;
};
Note: I have tried doing the following with the bbox defined above: bbox.map(pt => map.unproject(pt)) to get the lat lon bounding box. From my examination of the mapboxgl source code, it seems the process to unproject queryRenderedFeatures coordinates is a bit more complex than that.
Not very clear to me what you are trying to get, but conceptually speaking, what any queryRenderedFeatures is doing with no filters, is basically find all the tiles in the cache (the ones cached are in the viewport), then per tile, get all the features on them, and merge them all into the result. So, if you use queryRenderedFeatures with no arguments or with only a options argument is equivalent to passing a bounding box encompassing the entire map viewport.
You can check the source code of query_features.js file in Mapbox github repo
With those features in the viewport, it would be easy to reduce them to a lnglat bounding box through a .reduce function that you can use, for instance, as a bounding box to use map.fitBounds.
var coordinates = points;
var bounds = coordinates.reduce(function(bounds, coord) {
return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(coordinates[0], coordinates[0]));
map.fitBounds(bounds, {
padding: { top: 50, bottom: 50, left: 50, right: 50 },
easing(t) {
return t * (2 - t);
}
});
But as said, not sure if that's what you are looking for.
Other option would be to use the canvas width and height and unproject them... I found this code while ago,
const canvas = map.getCanvas()
const w = canvas.width
const h = canvas.height
const cUL = map.unproject ([0,0]).toArray()
const cUR = map.unproject ([w,0]).toArray()
const cLR = map.unproject ([w,h]).toArray()
const cLL = map.unproject ([0,h]).toArray()
const coordinates = [cUL,cUR,cLR,cLL,cUL]

How to set correct image dimensions by LatLngBounds using ImageOverlay?

I want to use ImageOverlays as markers, because I want the images to scale with zoom. Markers icons always resize to keep their size the same when you zoom.
My problem is that I can't figure out how to transform pixels to cords, so my image isn't stretched.
For instance, I decided my south-west LatLng to be [50, 50]. My image dimensions are 24px/24px.
How do I calculate the north-east LatLng based on the image pixels?
You are probably looking for map conversion methods.
In particular, you could use:
latLngToContainerPoint: Given a geographical coordinate, returns the corresponding pixel coordinate relative to the map container.
containerPointToLatLng: Given a pixel coordinate relative to the map container, returns the corresponding geographical coordinate (for the current zoom level).
// 1) Convert LatLng into container pixel position.
var originPoint = map.latLngToContainerPoint(originLatLng);
// 2) Add the image pixel dimensions.
// Positive x to go right (East).
// Negative y to go up (North).
var nextCornerPoint = originPoint.add({x: 24, y: -24});
// 3) Convert back into LatLng.
var nextCornerLatLng = map.containerPointToLatLng(nextCornerPoint);
var imageOverlay = L.imageOverlay(
'path/to/image',
[originLatLng, nextCornerLatLng]
).addTo(map);
Demo: http://playground-leaflet.rhcloud.com/tehi/1/edit?html,output