def _should_unwrap_geom_(self, geom): # Loop through the polygons determining if any coordinates need to be shifted and flag accordingly. ret = False for polygon in iter_exploded_geometries(geom): coords = np.array(polygon.exterior.coords) if np.any(coords[:, 0] < self.center_axis): ret = True break return ret
def unwrap(self, geom): """ :param geom: The target geometry to unwrap (i.e. move to 0 to 360 spatial domain). :type geom: :class:`shapely.geometry.base.BaseGeometry` :rtype: :class:`shapely.geometry.base.BaseGeometry` """ if type(geom) in [MultiPoint, Point]: return self._unwrap_point_(geom) assert type(geom) in [Polygon, MultiPolygon] # Wrap the polygon if requested. if self._should_unwrap_geom_(geom): # Loop through individual polygon components. Doing this operation on multi-polygons causes unexpected # behavior due to potential invalid geometries. processed_geometries = [] for ctr, to_process in enumerate(iter_exploded_geometries(geom)): try: assert to_process.is_valid except AssertionError: # Attempt a simple buffering trick to make the geometry valid. to_process = to_process.buffer(0) assert to_process.is_valid assert isinstance(to_process, Polygon) # Intersection with the two regions. if self._should_unwrap_geom_(to_process): left = to_process.intersection(self.clip1) right = to_process.intersection(self.clip2) # Pull out the right side polygons. right_polygons = [ poly for poly in iter_exploded_geometries(right) ] # Adjust polygons falling the left window. if isinstance(left, Polygon): left_polygons = [self._unwrap_shift_(left)] else: left_polygons = [] for polygon in left: left_polygons.append(self._unwrap_shift_(polygon)) # Merge polygons into single unit. try: processed = MultiPolygon(left_polygons + right_polygons) except TypeError: left = [ x for x in left_polygons if type(x) != LineString ] right = [ x for x in right_polygons if type(x) != LineString ] processed = MultiPolygon(left + right) else: if isinstance(to_process, Polygon): processed = [to_process] else: processed = to_process # Return a single polygon if the output multi-geometry has only one component. Otherwise, explode the # multi-geometry. if len(processed) == 1: processed = [processed[0]] else: processed = [i for i in processed] # Hold for final adjustment. processed_geometries += processed # Convert to a multigeometry if there is more than one output. if len(processed_geometries) > 1: # HACK: Union geometries to avoid some potential issues with multi-geometries. # ret = cascaded_union(processed_geometries) ret = MultiPolygon(processed_geometries) # assert ret.is_valid else: ret = processed_geometries[0] # If polygon does not need adjustment, just return it. else: ret = geom return ret
def wrap(self, geom): """ :param geom: The target geometry to adjust. :type geom: :class:`shapely.geometry.base.BaseGeometry` :rtype: :class:`shapely.geometry.base.BaseGeometry` """ def _shift_(geom): try: coords = np.array(geom.exterior.coords) coords[:, 0] = coords[:, 0] - 360 ret = Polygon(coords) # Likely a MultiPolygon. except AttributeError: polygons = np.empty(len(geom), dtype=object) for ii, polygon in enumerate(geom): coords = np.array(polygon.exterior.coords) coords[:, 0] = coords[:, 0] - 360 polygons[ii] = Polygon(coords) ret = MultiPolygon(list(polygons)) return ret if isinstance(geom, (Polygon, MultiPolygon)): bounds = np.array(geom.bounds) # If the polygon column bounds are both greater than 180 degrees shift the coordinates of the entire # polygon. if np.all([bounds[0] > self.wrap_axis, bounds[2] > self.wrap_axis]): new_geom = _shift_(geom) # If a polygon crosses the 180 axis, then the polygon will need to be split with intersection and # recombined. elif bounds[1] <= self.wrap_axis < bounds[2]: left = [ poly for poly in iter_exploded_geometries( geom.intersection(self.left_clip)) ] right = [ poly for poly in iter_exploded_geometries( _shift_(geom.intersection(self.right_clip))) ] try: new_geom = MultiPolygon(left + right) except TypeError: left = [x for x in left if type(x) != LineString] right = [x for x in right if type(x) != LineString] new_geom = MultiPolygon(left + right) # Otherwise, the polygon coordinates are not affected by wrapping and the geometry may be passed through. else: new_geom = geom # Likely a point type object. else: if isinstance(geom, Point): if geom.x > 180: new_geom = Point(geom.x - 360, geom.y) else: new_geom = geom # Likely a MultiPoint. elif isinstance(geom, MultiPoint): points = [None] * len(geom) for ii, point in enumerate(geom): if point.x > 180: new_point = Point(point.x - 360, point.y) else: new_point = point points[ii] = new_point new_geom = MultiPoint(points) else: raise NotImplementedError(geom) return new_geom