Convert coordinates to pixels on screen (and back again)
Asked Answered
C

3

12

This is what I'm doing: Clicking a marker on the map to open a side panel and center the map on the marker. The side panel takes up 3/4 of the right side of the screen.

This is what I need to happen: Center the marker according to the 1/4 of the viewport that is left after the panel opens.

I can get the pixel coordinates of the marker and do the calculations of where it needs to translate to while the panel is animating open. The problem is that flyTo() only accepts LngLatLike objects and I cannot convert my pixel coordinates to latitude and longitude. Leaflet.js has a function called containerPointToLatLng() that came in handy before I switched to Mapbox GL.

Given the sofistication of Mapbox GL, despite its newness, I can only imagine this is a possibility. But how?

Colorific answered 14/12, 2016 at 1:2 Comment(0)
L
19

The project and unproject methods make a lot of logic like this possible. If you have a click at a certain lngLat, and you want to position the map such that that geographic position is centered horizontally and at the 1/8 mark, you could:

  1. Take the geographic position and turn it into a pixel position with project
  2. Shift that pixel position right by (1/2-1/8) = 4/8-1/8 = 3/8 = 0.375 * map._containerDimensions()[0], by adding that number to it
  3. Turn that shifted pixel position back into a geographic position with unproject

Or, you might also be able to do it the easy way by using the offset option in your flyTo call.

Liss answered 17/12, 2016 at 0:42 Comment(2)
Aha! I project and unproject are exactly what I was looking for. That worked, but I now realize I failed to mention that I am zooming in to a fixed zoom point during the flyTo, this only works when I stay at the same zoom level. Any idea how to accommodate for that?Colorific
Just add a zoom property to your options object. Docs. I like the use of offset here as its doing the calculation for you -- just pass in a PointLike value [x, y].Malapropism
S
4

MapBox now has an example of how to do this on their site, using the padding option (though offset could work just as well).

Sigma answered 15/3, 2021 at 14:53 Comment(0)
H
-1

I'm in a similar situation, only my side-nav menu is 400px (same principal). I encounter the same 2 obstacles:

  1. Calculating the correction from pixels to lat/lng for the flyto function
  2. Correcting for different zoom levels

@tmcw answer did an amazing job for the first one, but unfortunately I the offset option in flyto function is no longer available.

It figures that in order to get linear ratio between different zoom levels you need to divide 2zoom1:2zoom2 according to the docs.

Full example:

// Desired zoom level at the end of flyTo
const zoomLevel = 16;
const zoomCorrection = Math.pow(2, this.map.getZoom()) / Math.pow(2, zoomLevel);

// Desired left shift/offset in px at the end of the flyTo. Use minus for right shift.
const leftShift = 200;


const lat = SelectedMarker.longitude;
const lng = SelectedMarker.latitude;
const coordinates = this.map.project([lat, lng]);
const offsetcoordinates = this.map.unproject([coordinates.x + (leftShift * zoomCorrection), coordinates.y]);

this.map.flyTo({
  animate: true,
  zoom: zoomLevel,
  center: [offsetcoordinates.lng, offsetcoordinates.lat],
});
Hirundine answered 23/11, 2020 at 15:50 Comment(1)
You're making this too hard for yourself. An offset CAN be applied to flyTo() via AnimationOptionsIrremovable

© 2022 - 2024 — McMap. All rights reserved.