team-3/src/c3nav/routing/utils/mpl.py

100 lines
2.8 KiB
Python
Raw Normal View History

2016-12-08 16:58:08 +01:00
from abc import ABC, abstractmethod
2016-12-03 19:09:39 +01:00
from matplotlib.path import Path
2016-12-08 16:58:08 +01:00
from shapely.geometry import MultiPolygon, Polygon
2016-12-03 19:09:39 +01:00
2016-12-07 16:11:33 +01:00
from c3nav.mapdata.utils.geometry import assert_multipolygon
2016-12-03 19:09:39 +01:00
2016-12-08 16:58:08 +01:00
class MplPathProxy(ABC):
@abstractmethod
def intersects_path(self, path):
pass
@abstractmethod
def contains_point(self, point):
pass
class MplMultipolygonPath(MplPathProxy):
def __init__(self, polygon):
self.polygons = [MplPolygonPath(polygon) for polygon in assert_multipolygon(polygon)]
2016-12-19 19:56:04 +01:00
@property
def exteriors(self):
return tuple(polygon.exterior for polygon in self.polygons)
def intersects_path(self, path, filled=False):
2016-12-08 16:58:08 +01:00
for polygon in self.polygons:
if polygon.intersects_path(path, filled=filled):
2016-12-08 16:58:08 +01:00
return True
return False
def contains_point(self, point):
for polygon in self.polygons:
if polygon.contains_point(point):
return True
return False
class MplPolygonPath(MplPathProxy):
def __init__(self, polygon):
self.exterior = linearring_to_mpl_path(polygon.exterior)
self.interiors = [linearring_to_mpl_path(interior) for interior in polygon.interiors]
2016-12-19 19:56:04 +01:00
@property
def exteriors(self):
return (self.exterior, )
def intersects_path(self, path, filled=False):
if filled:
if not self.exterior.intersects_path(path, filled=True):
return False
2016-12-08 16:58:08 +01:00
for interior in self.interiors:
if interior.contains_path(path):
return False
return True
else:
if self.exterior.intersects_path(path, filled=False):
2016-12-08 16:58:08 +01:00
return True
for interior in self.interiors:
if interior.intersects_path(path, filled=False):
return True
return False
2016-12-08 16:58:08 +01:00
def contains_point(self, point):
if not self.exterior.contains_point(point):
return False
for interior in self.interiors:
if interior.contains_point(point):
return False
return True
def shapely_to_mpl(geometry):
2016-12-03 19:09:39 +01:00
"""
convert a shapely Polygon or Multipolygon to a matplotlib Path
:param polygon: shapely Polygon or Multipolygon
2016-12-08 16:58:08 +01:00
:return: MplPathProxy
2016-12-03 19:09:39 +01:00
"""
2016-12-08 16:58:08 +01:00
if isinstance(geometry, Polygon):
return MplPolygonPath(geometry)
2016-12-27 01:49:23 +01:00
elif isinstance(geometry, MultiPolygon) or geometry.is_empty:
2016-12-08 16:58:08 +01:00
return MplMultipolygonPath(geometry)
raise TypeError
2016-12-03 19:09:39 +01:00
def linearring_to_mpl_path(linearring):
vertices = []
codes = []
coords = list(linearring.coords)
vertices.extend(coords)
vertices.append(coords[0])
codes.append(Path.MOVETO)
codes.extend([Path.LINETO] * (len(coords)-1))
codes.append(Path.CLOSEPOLY)
return Path(vertices, codes, readonly=True)