I built a simple editor using Mapbox GL JS in order to play and retrieve coordinates. In this example, the map is 1024x1024 pixels wide.
My goal was to have a matrix of 2x2 images as you can see in this following image
Here you can find the code used to generate the 4 images
const theme = poster.theme
// The entire map size
const size = { width: 1024, height: 1024 }
// How many of images do I need to fill my map?
const widthRatio = Math.ceil(size.width / 512)
const heightRatio = Math.ceil(size.height / 512)
// The bounds retrieved from the Editor
const bounds = poster.bounds
// DeltaX: longitude of each image
// DeltaY: latitude of each image
const deltaX = (bounds['_ne'].lng - bounds['_sw'].lng) / widthRatio
const deltaY = (bounds['_ne'].lat - bounds['_sw'].lat) / heightRatio
const images = []
let coord = [0, 0]
for (let i=1; i<=(widthRatio*heightRatio); i++) {
const image = {
_ne: {
lat: bounds['_ne'].lat - (coord[1] * deltaY),
lng: bounds['_sw'].lng + ((coord[0] + 1) * deltaX),
},
_sw: {
lat: bounds['_ne'].lat - ((coord[1] + 1) * deltaY),
lng: bounds['_sw'].lng + (coord[0] * deltaX),
}
}
// For each image we have to compute the viewport in order to find the
// center and the zoom level
const imageViewport = geoViewport.viewport([
image['_sw'].lng,
image['_sw'].lat,
image['_ne'].lng,
image['_ne'].lat
], [512, 512], undefined, undefined, 512, true)
coord[1] = coord[0] === (widthRatio - 1) ? coord[1] + 1 : coord[1]
coord[0] = coord[0] === (widthRatio - 1) ? 0 : coord[0] + 1
images.push(imageViewport)
}
return <div style={{
width: size.width + 'px',
height: size.height + 'px',
overflow: 'hidden'
}}>
<div className='flex flex-wrap' style={{ width: 512*widthRatio + 'px', height: 512*heightRatio + 'px' }}>
{images.map((image, key) => {
const src = new URI('https://api.mapbox.com/styles/v1')
src.segment(theme)
src.segment('static')
src.segment(`${image.center[0]},${image.center[1]},${image.zoom},0,0`)
src.segment(`512x512@2x`)
src.query({
access_token: MAPBOX_ACCESS_TOKEN,
attribution: false,
logo: false
})
return <img
key={key}
width='512px'
height='512px'
alt={`tile-${key}`}
src={src.toString()}
/>
})}
</div>
</div>
I divided the corrdinates by 4 to find the center of each image using geoViewport.viewport()
. The centers are correct. The weird thing is that the 2 images at the bottom have a zoom level of 5
, which is the same as the main map. The 2 images at the top have a computed zoom level of 4.901634454858067
. This lead to a bad result as you can see in this image. Both verticaly and horizontaly, the images are not aligned.
I first thought that something went wrong with the zoom level of the top images. I told myself that my entire image has a zoom level of 5
so I'll ask the Static Image API the 4 images using 5
as zoom level and not the computed one of each image anymore. The result has become far more as expected as you can see in this image.
Then I noticed that the vertical alignment is not perfect as you can see in this part of the image.
Then I understand something: The algorithm of the geoViewport
takes care of the Earth curvature!. This is why at first hand the computation gave me a zoom level of 4.901634454858067
for the top images. By setting the zoom level to 5
, I fixed the horizontal alignment, but there is still a small error for the vertical alignment.
The lower the zoom level is, the higher the error shows up. When using a zoom level of 6
, the error disapears! You can see the result in these images: the editor the result
-
Hopefuly for my business, most of my maps will have 6 or a higher zoom level! This is a great news for us. Nonetheless, I want to know if there is something to do to fix this behavior?
-
As the earth is a ball, I told myself that this alignment issue shouldn't apply only on vertical alignment. If I want to have a very wide map, the horizontal alignment will have the same issue. But I tried to generate this image, which is 2560x512px wide using a zoom level of
4
. The result is perfect, every images are aligned horizontaly => the result. So I don't understand why I'm able to align images horizontaly but not verticaly at a zoom level of 5 or lower.