Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions opendm/cutline.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from skimage.draw import line
from skimage.graph import route_through_array
import shapely
from shapely.geometry import LineString, mapping, shape
from shapely.geometry import LineString, MultiPolygon, mapping, shape
from shapely.ops import polygonize, unary_union

def write_raster(data, file):
Expand All @@ -31,6 +31,19 @@ def write_raster(data, file):
with rasterio.open(file, 'w', BIGTIFF="IF_SAFER", **profile) as wout:
wout.write(data, 1)

def largest_polygon(polygons):
cutline_union = unary_union(polygons)
if isinstance(cutline_union, MultiPolygon):
parts = list(cutline_union.geoms)
else:
parts = [cutline_union]

largest = parts[0]
for p in parts[1:]:
if p.area > largest.area:
largest = p
return largest

def compute_cutline(orthophoto_file, crop_area_file, destination, max_concurrency=1, scale=1):
if io.file_exists(orthophoto_file) and io.file_exists(crop_area_file):
log.ODM_INFO("Computing cutline")
Expand Down Expand Up @@ -140,16 +153,8 @@ def compute_linestrings(direction):
return

log.ODM_INFO("Merging polygons")
cutline_polygons = unary_union(polygons)
if not hasattr(cutline_polygons, '__getitem__'):
cutline_polygons = [cutline_polygons]

largest_cutline = cutline_polygons[0]
largest_cutline = largest_polygon(polygons)
max_area = largest_cutline.area
for p in cutline_polygons:
if p.area > max_area:
max_area = p.area
largest_cutline = p

log.ODM_INFO("Largest cutline found: %s m^2" % max_area)

Expand All @@ -158,7 +163,7 @@ def compute_linestrings(direction):
'driver': 'GPKG',
'schema': {
'properties': {},
'geometry': 'MultiPolygon'
'geometry': 'Polygon'
}
}

Expand Down
58 changes: 58 additions & 0 deletions tests/test_cutline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import os
import tempfile
import unittest

import fiona
from shapely.geometry import Polygon, mapping

from opendm.cutline import largest_polygon


class TestCutline(unittest.TestCase):
def test_largest_polygon_from_single_polygon(self):
""" Tests that the largest polygon from a single polygon is the polygon itself """

p = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]) # 1x1 square
largest = largest_polygon([p])

self.assertEqual(largest.geom_type, "Polygon")
self.assertAlmostEqual(largest.area, 1.0)

def test_largest_polygon_from_multipolygon_union(self):
""" Tests that the largest polygon from a multipolygon union is the largest polygon """

small = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]) # 1x1 square (area = 1)
large = Polygon([(0, 0), (3, 0), (3, 2), (0, 2)]) # 3x2 rectangle (area = 6)
largest = largest_polygon([small, large])
self.assertEqual(largest.geom_type, "Polygon")
self.assertAlmostEqual(largest.area, 6.0) # matches large polygon

def test_polygon_writes_to_gpkg_with_polygon_schema(self):
""" Tests that fiona can write our polygon to a GPKG file"""

geom = largest_polygon([
Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
Polygon([(2, 0), (5, 0), (5, 2), (2, 2)]),
])
self.assertEqual(mapping(geom)["type"], "Polygon")

# Write out the polygon to quickly verify that Fiona can still process
# our largest polygon.
with tempfile.TemporaryDirectory() as tmp:
path = os.path.join(tmp, "cutline.gpkg")
with fiona.open(
path,
"w",
driver="GPKG",
crs="EPSG:4326",
schema={"geometry": "Polygon", "properties": {}},
) as sink:
sink.write({"geometry": mapping(geom), "properties": {}})

with fiona.open(path) as src:
feature = next(iter(src))
self.assertEqual(feature["geometry"]["type"], "Polygon")


if __name__ == "__main__":
unittest.main()
Loading