2

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)

1 Answer 1

1

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.