Skip to content

Instantly share code, notes, and snippets.

@rgazelot
Last active March 10, 2020 14:38
Show Gist options
  • Save rgazelot/622ff59cf5b26b2e5eb2a75413c6e0a8 to your computer and use it in GitHub Desktop.
Save rgazelot/622ff59cf5b26b2e5eb2a75413c6e0a8 to your computer and use it in GitHub Desktop.

The editor

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.

The editor

My goal was to have a matrix of 2x2 images as you can see in this following image

The code

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>

The matrix result

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

The questions

  1. 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?

  2. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment