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)
|
|
|
|
|
2016-12-10 14:58:53 +01:00
|
|
|
def intersects_path(self, path, filled=False):
|
2016-12-08 16:58:08 +01:00
|
|
|
for polygon in self.polygons:
|
2016-12-10 14:58:53 +01:00
|
|
|
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, )
|
|
|
|
|
2016-12-10 14:58:53 +01:00
|
|
|
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
|
|
|
|
2016-12-10 14:58:53 +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
|
2016-12-10 14:58:53 +01:00
|
|
|
|
|
|
|
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)
|