Last active
August 3, 2021 18:25
-
-
Save JoaoCarabetta/8a5df60ac0012e56219a5b2dffcb20e3 to your computer and use it in GitHub Desktop.
Katana Algorithm Minimal Working Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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