WALD's help page for geospatial hackers
by
Sometimes you need to calculate a set of bounding boxes around different parts of the world using a specific coordinate projection system. There are a number of tools that allow drawing boxes interactively in geographical coordinates (lat, lon) using a web page, such as geojson.io. However, I don’t know an easy way of converting or defining bounding boxes in other geographical projections. Probably GIS systems provide the right functionality to do this easily, but this is also about experimenting with interactive Jupyter notebooks and create this functionality.
The following notebook demonstrates how to draw boxes in a map interactively, which can then be converted into any coordinate reference system. This page contains a rendered copy of the notebook but to test this you’ll need to run a Jupyter notebook session. A copy of this notebook can be downloaded from here.
Interactive widgets in Jupyter notebooks allow us to make our code interactive. I’m sure this notebook can be extended and improved to make it more intuitive and interactive. Let us know if you find a way of improving this or something interesting.
from ipyleaflet import Map, basemaps, basemap_to_tiles, DrawControl
import geojson
def bbox(coord_list):
box = []
for i in (0,1):
res = sorted(coord_list, key=lambda x:x[i])
box.append((res[0][i],res[-1][i]))
return box[0][0], box[1][0], box[0][1], box[1][1]
watercolor = basemap_to_tiles(basemaps.Stamen.Watercolor)
m = Map(layers=(watercolor, ), center=(-25, 140), zoom=4)
draw_control = DrawControl(circle={}, circleMarker={}, circlemarker={},
CircleMarker={}, polyline={}, marker={}, polygon={},
rectangle = {"shapeOptions": {"color": "#00005d","fillOpacity": 0.0}})
def handle_draw(self, action, geo_json):
#self.clear()
lon_min.value,lat_min.value,lon_max.value,lat_max.value=bbox(list(geojson.utils.coords(geo_json['geometry'])))
x_min.value,y_min.value=transform(lon_min.value,lat_min.value)
x_max.value,y_max.value=transform(lon_max.value,lat_max.value)
print(f"Width: {x_max.value-x_min.value}")
print(f"Height: {y_max.value-y_min.value}")
draw_control.on_draw(handle_draw)
m.add_control(draw_control)
m
from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
in_crs = widgets.IntText(value=4326,description='Input EPSG:',disabled=True)
display(in_crs)
out_crs = widgets.IntText(value=3577,description='Output EPSG:',disabled=False)
display(out_crs)
lon_min = widgets.BoundedFloatText(value=0.0,min=-380.0,max=380.0,description='Min Longitude:')
display(lon_min)
lat_min = widgets.BoundedFloatText(value=0.0,min=-90.0,max=90.0,description='Min Latitude:')
display(lat_min)
lon_max = widgets.BoundedFloatText(value=0.0,min=-380.0,max=380.0,description='Max Longitude:')
display(lon_max)
lat_max = widgets.BoundedFloatText(value=0.0,min=-90.0,max=90.0,description='Max Latitude:')
display(lat_max)
from pyproj import Proj, transform
from pyproj import Transformer
transformer = Transformer.from_crs(f"EPSG:{in_crs.value}", f"EPSG:{out_crs.value}", always_xy=True)
def transform(x,y):
return transformer.transform(x,y)
x_min = widgets.FloatText(value=0.0,description='Min X:')
display(x_min)
y_min = widgets.FloatText(value=0.0,description='Min Y:')
display(y_min)
x_max = widgets.FloatText(value=0.0,description='Max X:')
display(x_max)
y_max = widgets.FloatText(value=0.0,description='Max Y:')
display(y_max)