Writing a map library from scratch - Part 2: Zooming
If you haven’t read Part 1 yet, I recommend you do so before continuing.
In the previous article, we created a static map made up by tiles on a specific zoom level. But how do we make it interactive?
Interactivity
In essence, zooming (or panning) on a map is just a manipulation of the map center and the zoom level. If you can figure out what the new zoom level and the new map center will be after an interaction, you just need to re-render the map with the these values.
Zooming
Zooming towards the center of the container is fairly simple. We just need to change the zoom level and re-render the map, as the center does not change. However, zooming towards or away from the mouse cursor is a bit more complicated. We need to calculate the new map center based on the mouse position.
Zooming towards mouse cursor
When you zoom towards the mouse cursor, the new center of the map will be at midpoint between the mouse cursor and the old center.
Zooming away from mouse cursor
When you zoom away from the mouse cursor, it’s the other way around. The old center is located at the midpoint between mouse cursor and the new center.
How do we calculate the new center?
If we zoom in, we need to calculate the midpoint between the mouse cursor and the center. We will calculate the distance in pixels. Let us take the same map from part 1:
- The center is:
(13.4, 52.52)
. - The zoom level is
14
. - One tile is
256
pixels wide and high. - The map is inside a container of
400
pixels width and300
pixels height. - The center of the container is at
(200, 150)
.
Using the Web Mercator Projection, these geo-coordinates (on a globe) can be converted
to (2253273.3155555557, 1375543.6427981234)
in
cartesian coordinates (on a flat surface). Let’s say the mouse
cursor is at (100, 100)
inside the container, and we want to zoom
in by 1
level (to zoom level 15
).
The midpoint between the mouse cursor at (150, 125)
, which
is 50
pixels to the left and 25
pixels
up from the center.
So the cartesian coordinates of the new center are:
x = 2253273.3155555557 - 50;
x = 2253223.3155555557;
y = 1375543.6427981234 - 25;
y = 1375518.6427981234;
Now, we need to use the web mercator projection to convert these cartesian coordinates to geographic coordinates.
axisSize = 4194304 // 256*2^14
lng = 360 * (x / axisSize - 0.5);
lng = 13.395708465576188;
lat = 360 * (Math.atan(Math.exp(Math.PI * (1 - (2 * y) / axisSize))) / Math.PI - 0.25);
lat = 52.52130564660004;
If we plug those values back into the algorithm from Part 1, we found a way to zoom in!
How do we zoom out? Well, we just need to use the old center as the midpoint between the mouse cursor and
the new center. Which means, with the same values, old center at (200, 150)
, mouse
cursor at (100, 100)
, the new center would be at (300, 200)
.
In cartesian coordinates, that would be:
x = 2253273.3155555557 + 100;
x = 2253373.3155555557;
y = 1375543.6427981234 + 50;
y = 1375593.6427981234;
In geographic coordinates:
axisSize = 4194304 // 256*2^14
lng = 360 * (x / axisSize - 0.5);
lng = 13.408583068847673;
lat = 360 * (Math.atan(Math.exp(Math.PI * (1 - (2 * y) / axisSize))) / Math.PI - 0.25);
lat = 52.51738859038791;
And that’s it! We can now zoom in and out on the map! Here is a working example, click/tap to zoom in, double click/double tap to zoom out:
In Part 3, we will look at how panning works.