Skip to content

Instantly share code, notes, and snippets.

@JoaoCarabetta
Last active August 3, 2021 18:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoaoCarabetta/8a5df60ac0012e56219a5b2dffcb20e3 to your computer and use it in GitHub Desktop.
Save JoaoCarabetta/8a5df60ac0012e56219a5b2dffcb20e3 to your computer and use it in GitHub Desktop.
Katana Algorithm Minimal Working Example
from shapely.geometry import box, Polygon, MultiPolygon, GeometryCollection
from shapely.wkt import loads
def threshold_func(geometry, threshold_value):
"""Compares the threshold values with the polygon area"""
return geometry.area < threshold_value
def katana(geometry, threshold_func, threshold_value, number_tiles=0, max_number_tiles=250):
"""Splits a geometry in tiles forming a grid given a threshold function and
a maximum number of tiles.
Parameters
----------
geometry: Polygon or MultiPolygon
Initial geometry
threshold_func: function
Calculete how many segments or km or data points exists in geometry
Should return True or False given a threshold_value.
Let's say you want to stop dividing polygons when you reach 10k unique segments,
then, you should return True when the geometry has < 10k segmnets.
threshold_value: number
Whatever value you set as the max of the quantity you want in each tile
number_tiles: int
Number of tiles, defaults to 0.
max_number_tiles: int
Maximum number of tiles
Return
------
geometry: MultiPolygon
Initial geometry divided in tiles
KUDOS https://snorfalorpagus.net/blog/2016/03/13/splitting-large-polygons-for-faster-intersections/
"""
bounds = geometry.bounds
width = bounds[2] - bounds[0]
height = bounds[3] - bounds[1]
if threshold_func(geometry, threshold_value) or number_tiles == max_number_tiles:
# either the polygon is smaller than the threshold, or the maximum
# number of recursions has been reached
return [geometry]
if height >= width:
# split left to right
a = box(bounds[0], bounds[1], bounds[2], bounds[1] + height / 2)
b = box(bounds[0], bounds[1] + height / 2, bounds[2], bounds[3])
else:
# split top to bottom
a = box(bounds[0], bounds[1], bounds[0] + width / 2, bounds[3])
b = box(bounds[0] + width / 2, bounds[1], bounds[2], bounds[3])
result = []
for d in (
a,
b,
):
c = geometry.intersection(d)
if not isinstance(c, GeometryCollection):
c = [c]
for e in c:
if isinstance(e, (Polygon, MultiPolygon)):
result.extend(katana(e, threshold_func, threshold_value, number_tiles + 1))
if count > 0:
return result
# convert multipart into singlepart
final_result = []
for g in result:
if isinstance(g, MultiPolygon):
final_result.extend(g)
else:
final_result.append(g)
return final_result
wkt = 'POLYGON((2.0117187499999822 44.38657313925715,-19.433593750000018 19.207272119703983,19.414062499999982 6.904449621538131,64.94140624999999 -3.096801256840523,81.46484374999999 37.21269961002643,45.78124999999998 24.106495997107682,53.69140624999998 51.22054369437158,3.7695312499999822 37.07257833232809,2.0117187499999822 44.38657313925715))'
geometry = loads(wkt)
MultiPolygon(katana(geometry, threshold_func, 50, 100))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment