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 _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 # NOTE(LB): We use 'exterior' here in case the `polygon2d` has # interiors (holes) defined. In our use cases, we don't care about # polygon interiors, so we simply discard these exteriors. xx, yy = numpy.transpose(polygon2d.exterior.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_proj_enclosing_polygon(self): """ See :meth:`Mesh._get_proj_enclosing_polygon`. :class:`RectangularMesh` contains an information about relative positions of points, so it allows to define the minimum polygon, containing the projection of the mesh, which doesn't necessarily have to be convex (in contrast to :class:`Mesh` implementation). :returns: Same structure as :meth:`Mesh._get_proj_convex_hull`. """ if self.lons.size < 4: # the mesh doesn't contain even a single cell, use :class:`Mesh` # method implementation (which would dilate the point or the line) return super(RectangularMesh, self)._get_proj_enclosing_polygon() proj = geo_utils.get_orthographic_projection( *geo_utils.get_spherical_bounding_box(self.lons, self.lats) ) mesh2d = numpy.array(proj(self.lons.transpose(), self.lats.transpose())).transpose() lines = iter(mesh2d) # we iterate over horizontal stripes, keeping the "previous" # line of points. we keep it reversed, such that together # with the current line they define the sequence of points # around the stripe. prev_line = lines.next()[::-1] polygons = [] for i, line in enumerate(lines): coords = numpy.concatenate((prev_line, line, prev_line[0:1])) # create the shapely polygon object from the stripe # coordinates and simplify it (remove redundant points, # if there are any lying on the straight line). stripe = shapely.geometry.LineString(coords) \ .simplify(self.DIST_TOLERANCE) \ .buffer(self.DIST_TOLERANCE, 2) polygons.append(shapely.geometry.Polygon(stripe.exterior)) prev_line = line[::-1] # create a final polygon as the union of all the stripe ones polygon = shapely.ops.cascaded_union(polygons) \ .simplify(self.DIST_TOLERANCE) return proj, polygon
def _get_proj_enclosing_polygon(self): """ See :meth:`Mesh._get_proj_enclosing_polygon`. :class:`RectangularMesh` contains an information about relative positions of points, so it allows to define the minimum polygon, containing the projection of the mesh, which doesn't necessarily have to be convex (in contrast to :class:`Mesh` implementation). :returns: Same structure as :meth:`Mesh._get_proj_convex_hull`. """ if self.lons.size < 4: # the mesh doesn't contain even a single cell, use :class:`Mesh` # method implementation (which would dilate the point or the line) return super(RectangularMesh, self)._get_proj_enclosing_polygon() proj = geo_utils.get_orthographic_projection( *geo_utils.get_spherical_bounding_box(self.lons, self.lats)) mesh2d = numpy.array(proj(self.lons.transpose(), self.lats.transpose())).transpose() lines = iter(mesh2d) # we iterate over horizontal stripes, keeping the "previous" # line of points. we keep it reversed, such that together # with the current line they define the sequence of points # around the stripe. prev_line = lines.next()[::-1] polygons = [] for i, line in enumerate(lines): coords = numpy.concatenate((prev_line, line, prev_line[0:1])) # create the shapely polygon object from the stripe # coordinates and simplify it (remove redundant points, # if there are any lying on the straight line). stripe = shapely.geometry.LineString(coords) \ .simplify(self.DIST_TOLERANCE) \ .buffer(self.DIST_TOLERANCE, 2) polygons.append(shapely.geometry.Polygon(stripe.exterior)) prev_line = line[::-1] # create a final polygon as the union of all the stripe ones polygon = shapely.ops.cascaded_union(polygons) \ .simplify(self.DIST_TOLERANCE) return proj, polygon
def _define_bins(bins_data, mag_bin_width, dist_bin_width, coord_bin_width, truncation_level, n_epsilons): """ Define bin edges for disaggregation histograms. Given bins data as provided by :func:`_collect_bins_data`, this function finds edges of histograms, taking into account maximum and minimum values of magnitude, distance and coordinates as well as requested sizes/numbers of bins. """ mags, dists, lons, lats, _joint_probs, tect_reg_types, trt_bins = bins_data mag_bins = mag_bin_width * numpy.arange( int(numpy.floor(mags.min() / mag_bin_width)), int(numpy.ceil(mags.max() / mag_bin_width) + 1) ) dist_bins = dist_bin_width * numpy.arange( int(numpy.floor(dists.min() / dist_bin_width)), int(numpy.ceil(dists.max() / dist_bin_width) + 1) ) west, east, north, south = get_spherical_bounding_box(lons, lats) west = numpy.floor(west / coord_bin_width) * coord_bin_width east = numpy.ceil(east / coord_bin_width) * coord_bin_width lon_extent = get_longitudinal_extent(west, east) lon_bins, _, _ = npoints_between( west, 0, 0, east, 0, 0, numpy.round(lon_extent / coord_bin_width) + 1 ) lat_bins = coord_bin_width * numpy.arange( int(numpy.floor(south / coord_bin_width)), int(numpy.ceil(north / coord_bin_width) + 1) ) eps_bins = numpy.linspace(-truncation_level, truncation_level, n_epsilons + 1) return mag_bins, dist_bins, lon_bins, lat_bins, eps_bins, trt_bins
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))
def _get_proj_convex_hull(self): """ Create a projection centered in the center of this mesh and define a convex polygon in that projection, enveloping all the points of the mesh. :returns: Tuple of two items: projection function and shapely 2d polygon. Note that the result geometry can be line or point depending on number of points in the mesh and their arrangement. """ # 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.flatten(), self.lats.flatten())).copy() multipoint = shapely.geometry.MultiPoint(coords) # create a 2d polygon from a convex hull around that multipoint. polygon2d = multipoint.convex_hull return proj, polygon2d
def _get_proj_convex_hull(self): """ Create a projection centered in the center of this mesh and define a convex polygon in that projection, enveloping all the points of the mesh. :returns: Tuple of two items: projection function and shapely 2d polygon. Note that the result geometry can be line or point depending on number of points in the mesh and their arrangement. """ # 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.flatten(), self.lats.flatten())).copy() multipoint = shapely.geometry.MultiPoint(coords) # create a 2d polygon from a convex hull around that multipoint. polygon2d = multipoint.convex_hull return proj, polygon2d
def _define_bins(bins_data, mag_bin_width, dist_bin_width, coord_bin_width, truncation_level, n_epsilons): """ Define bin edges for disaggregation histograms. Given bins data as provided by :func:`_collect_bins_data`, this function finds edges of histograms, taking into account maximum and minimum values of magnitude, distance and coordinates as well as requested sizes/numbers of bins. """ mags, dists, lons, lats, _joint_probs, tect_reg_types, trt_bins = bins_data mag_bins = mag_bin_width * numpy.arange( int(numpy.floor(mags.min() / mag_bin_width)), int(numpy.ceil(mags.max() / mag_bin_width) + 1)) dist_bins = dist_bin_width * numpy.arange( int(numpy.floor(dists.min() / dist_bin_width)), int(numpy.ceil(dists.max() / dist_bin_width) + 1)) west, east, north, south = get_spherical_bounding_box(lons, lats) west = numpy.floor(west / coord_bin_width) * coord_bin_width east = numpy.ceil(east / coord_bin_width) * coord_bin_width lon_extent = get_longitudinal_extent(west, east) lon_bins, _, _ = npoints_between( west, 0, 0, east, 0, 0, numpy.round(lon_extent / coord_bin_width) + 1) lat_bins = coord_bin_width * numpy.arange( int(numpy.floor(south / coord_bin_width)), int(numpy.ceil(north / coord_bin_width) + 1)) eps_bins = numpy.linspace(-truncation_level, truncation_level, n_epsilons + 1) return mag_bins, dist_bins, lon_bins, lat_bins, eps_bins, trt_bins