def get_convex_hull(self): """ Get a convex polygon object that contains projections of all the points of the mesh. :returns: Instance of :class:`nhlib.geo.polygon.Polygon` that is a convex hull around all the points in this mesh. If the original mesh had only one point, the resulting polygon has a square shape with a side length of 10 meters. If there were only two points, resulting polygon is a stripe 10 meters wide. """ # avoid circular imports from nhlib.geo.polygon import Polygon # create a projection centered in the center of points collection proj = geo_utils.get_orthographic_projection( *geo_utils.get_spherical_bounding_box(self.lons, self.lats) ) # project all the points and create a shapely multipoint object. # need to copy an array because otherwise shapely misinterprets it coords = numpy.transpose(proj(self.lons, self.lats)).copy() multipoint = shapely.geometry.MultiPoint(coords) # create a 2d polygon from a convex hull around that multipoint polygon2d = multipoint.convex_hull # if mesh had only one point, the convex hull is a point. if there # were two, it is a line string. we need to return a convex polygon # object, so extend that area-less geometries by some arbitrarily # small distance, like five meters. if isinstance(polygon2d, (shapely.geometry.LineString, shapely.geometry.Point)): polygon2d = polygon2d.buffer(0.005, 1) return Polygon._from_2d(polygon2d, proj)
def _from_2d(cls, polygon2d, proj): """ Create a polygon object from a 2d polygon and a projection. :param polygon2d: Instance of ``shapely.geometry.Polygon``. :param proj: Projection object created by :func:`~nhlib.geo._utils.get_orthographic_projection` that was used to project ``polygon2d``. That projection will be used for projecting it back to get spherical coordinates from Cartesian ones. :returns: New :class:`Polygon` object. Note that spherical coordinates of that polygon do not get upsampled even for longer edges. """ # avoid calling class' constructor polygon = object.__new__(cls) # project polygon2d back on the sphere xx, yy = numpy.transpose(polygon2d.boundary.coords) # need to cut off the last point -- it repeats the first one polygon.lons, polygon.lats = proj(xx[:-1], yy[:-1], reverse=True) # initialize the instance (as constructor would do) polygon._bbox = utils.get_spherical_bounding_box(polygon.lons, polygon.lats) polygon._polygon2d = polygon2d polygon._projection = proj return polygon
def get_polygon_area(polygon): """ Compute polygon area in squared kilometers. """ lons = [lon for lon, lat in polygon] lats = [lat for lon, lat in polygon] west, east, north, south = _utils.get_spherical_bounding_box(lons, lats) proj = _utils.get_orthographic_projection(west, east, north, south) xx, yy = proj(lons, lats) polygon2d = shapely.geometry.Polygon(zip(xx, yy)) return polygon2d.area
def get_polygon_area(polygon): """ Compute polygon area in squared kilometers. """ lons = [lon for lon,lat in polygon] lats = [lat for lon,lat in polygon] west, east, north, south = _utils.get_spherical_bounding_box(lons, lats) proj = _utils.get_orthographic_projection(west, east, north, south) xx, yy = proj(lons, lats) polygon2d = shapely.geometry.Polygon(zip(xx, yy)) return polygon2d.area
def get_joyner_boore_distance(self, mesh): """ Compute and return Joyner-Boore distance to each point of ``mesh``. Point's depth is ignored. See :meth:`nhlib.geo.surface.BaseSurface.get_joyner_boore_distance` for definition of this distance. :returns: numpy array of distances in km of the same shape as ``mesh``. Distance value is considered to be zero if a point lies inside the polygon enveloping the projection of the mesh or on one of its edges. """ bounding_mesh = self._get_bounding_mesh(with_depths=False) assert bounding_mesh.depths is None lons, lats = bounding_mesh.lons, bounding_mesh.lats depths = numpy.zeros_like(lons) proj = geo_utils.get_orthographic_projection( *geo_utils.get_spherical_bounding_box(lons, lats) ) xx, yy = proj(lons, lats) mesh_2d = numpy.array([xx, yy], dtype=float).transpose().copy() if len(xx) == 2: mesh_2d = shapely.geometry.LineString(mesh_2d) elif len(xx) == 1: mesh_2d = shapely.geometry.Point(*mesh_2d) elif len(xx) > 2: mesh_2d = shapely.geometry.Polygon(mesh_2d) mesh_lons, mesh_lats = mesh.lons.flatten(), mesh.lats.flatten() mesh_xx, mesh_yy = proj(mesh_lons, mesh_lats) distances = [] for i in xrange(len(mesh_lons)): point_2d = shapely.geometry.Point(mesh_xx[i], mesh_yy[i]) dist = mesh_2d.distance(point_2d) if dist < 500: # if the distance is below threshold of 500 kilometers, # consider the distance measured on the projection accurate # enough (an error doesn't exceed half km). distances.append(dist) else: # ... otherwise get the precise distance between bounding mesh # projection and the point projection using pure numerical way distances.append(geodetic.min_distance( lons, lats, depths, mesh_lons[i], mesh_lats[i], 0 )) return numpy.array(distances).reshape(mesh.shape)
def _init_polygon2d(self): """ Spherical bounding box, projection, and Cartesian polygon are all cached to prevent redundant computations. If any of them are `None`, recalculate all of them. """ if (self._polygon2d is None or self._projection is None or self._bbox is None): # resample polygon line segments: lons, lats = get_resampled_coordinates(self.lons, self.lats) # find the bounding box of a polygon in spherical coordinates: self._bbox = utils.get_spherical_bounding_box(lons, lats) # create a projection that is centered in a polygon center: self._projection = \ utils.get_orthographic_projection(*self._bbox) # project polygon vertices to the Cartesian space and create # a shapely polygon object: xx, yy = self._projection(lons, lats) self._polygon2d = shapely.geometry.Polygon(zip(xx, yy))