TransWikia.com

Draw GeoJSON polygon over image returned by Mapbox in Python

Geographic Information Systems Asked by dsv on December 23, 2020

I am trying to draw a polygon representing the GeoJSON on top of the image I get from a map returned by Mapbox. If I pass in the GeoJSON in the Mapbox REST API URL, I get the following image.
Image from MapBox.

But I want to draw the polygon manually using PIL as there is a size 8kb restriction for Mapbox API (https://docs.mapbox.com/api/maps/static-images/#overlay-options).

If I just get the map and draw the image using the following code, my polygon seems to be in the correct shape but too small. Can someone point me in the right direction on how to get the polygon size correct?

    def draw(self, mapbox_image, zoom, **kwargs) -> None:
      geometries = get_geometries(kwargs['geojson'])
      left_x = kwargs['left_x']
      top_y = kwargs['top_y']
      actual_x_padding = kwargs['actual_x_padding']
      actual_y_padding = kwargs['actual_y_padding']

      draw = ImageDraw.Draw(mapbox_image)
      for geometry in geometries:
         for coordinates in geometry['coordinates']:
            polygon = [( actual_x_padding+ (longitude_to_x(coordinate[0], zoom ) - left_x),
                       (actual_y_padding +latitude_to_y(coordinate[1], zoom ) - top_y)) for coordinate in
            coordinates]
            draw.polygon(polygon, fill=None, outline="blue")


    ##This is how the parameters are calculated before passing in to the function   
     
    zoom = get_required_zoom(extent_rectangle_lat_long, IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_PADDING)

    left_x = longitude_to_x(extent_rectangle_lat_long[0], zoom)
    right_x = longitude_to_x(extent_rectangle_lat_long[2], zoom)
    top_y = latitude_to_y(extent_rectangle_lat_long[3], zoom)
    bottom_y = latitude_to_y(extent_rectangle_lat_long[1], zoom)

    centre_x = left_x + ((right_x - left_x) / 2)
    centre_y = top_y + ((bottom_y - top_y) / 2)
    centre_longitude = x_to_longitude(centre_x, zoom)
    centre_latitude = y_to_latitude(centre_y, zoom)
    actual_x_padding = 2 * (longitude_to_x(extent_rectangle_lat_long[0], zoom) - image_left_x)
    actual_y_padding = 2 * (latitude_to_y(extent_rectangle_lat_long[3], zoom) - image_top_y)

PIL image

I use the calculations given in MapBox or Google Maps Static API: Extent or bounding box of image? to for the conversion.

def latitude_to_y(latitude_degrees, zoom):
  pi_by_4 = math.pi / 4
  latitude_radians = math.radians(latitude_degrees)
  return (128 / math.pi) * math.pow(2, zoom) * (
        math.pi - math.log(math.tan(pi_by_4 + (latitude_radians / 2)), math.e))

def longitude_to_x(longitude_degrees, zoom):
    longitude_radians = math.radians(longitude_degrees)
    return (128 / math.pi) * math.pow(2, zoom) * (longitude_radians + math.pi)

One Answer

Okay, I managed to reproduce the same result as MapBox by introducing magnify method.. It is working for all scenarios of GeoJSON I used. Not sure why I had to magnify/zoom though.

    def draw(self, mapbox_image, zoom, **kwargs) -> None:
      geometries = get_geometries(kwargs['geojson'])
      left_x = kwargs['left_x']
      top_y = kwargs['top_y']
      actual_x_padding = kwargs['actual_x_padding']
      actual_y_padding = kwargs['actual_y_padding']

      draw = ImageDraw.Draw(mapbox_image)
      for geometry in geometries:
        for coordinates in geometry['coordinates']:
            polygon = [(self.magnify(longitude_to_x(coordinate[0], zoom) - left_x, 100),
                        self.magnify(latitude_to_y(coordinate[1], zoom) - top_y, 100)) for coordinate in
                       coordinates]
            draw.polygon(polygon, fill=None, outline="blue")

   def magnify(self, value, magnification):
     return value + (value * magnification) / 100

Image with magnification applied

Answered by dsv on December 23, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP