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)
asked Dec 20, 2020 at 10:05

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

answered Dec 22, 2020 at 11:13

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.