示例#1
0
def prefilter_ruptures(hdf5, ridx, idx_set, sites, integration_distance):
    """
    Determines if a rupture is likely to be inside the integration distance
    by considering the set of fault plane centroids.

    :param hdf5:
        Source of UCERF file as h5py.File object
    :param list ridx:
        List of indices composing the rupture sections
    :param dict idx_set:
        Set of indices for the branch
    :param sites:
        Sites for consideration (can be None!)
    :param float integration_distance:
        Maximum distance from rupture to site for consideration
    """
    # Generate array of sites
    if not sites:
        return True
    centroids = numpy.array([[0., 0., 0.]], dtype="f")
    for idx in ridx:
        trace_idx = "{:s}/{:s}".format(idx_set["sec_idx"], str(idx))
        centroids = numpy.vstack(
            [centroids, hdf5[trace_idx + "/Centroids"][:].astype("float64")])
    centroids = centroids[1:, :]
    distance = min_geodetic_distance(centroids[:, 0], centroids[:, 1],
                                     sites.lons, sites.lats)
    return numpy.any(distance <= integration_distance)
示例#2
0
def prefilter_ruptures(hdf5, ridx, idx_set, sites, integration_distance):
    """
    Determines if a rupture is likely to be inside the integration distance
    by considering the set of fault plane centroids.

    :param hdf5:
        Source of UCERF file as h5py.File object
    :param list ridx:
        List of indices composing the rupture sections
    :param dict idx_set:
        Set of indices for the branch
    :param sites:
        Sites for consideration (can be None!)
    :param float integration_distance:
        Maximum distance from rupture to site for consideration
    """
    # Generate array of sites
    if not sites:
        return True
    centroids = numpy.array([[0., 0., 0.]], dtype="f")
    for idx in ridx:
        trace_idx = "{:s}/{:s}".format(idx_set["sec_idx"], str(idx))
        centroids = numpy.vstack([
            centroids,
            hdf5[trace_idx + "/Centroids"][:].astype("float64")])
    centroids = centroids[1:, :]
    distance = min_geodetic_distance(centroids[:, 0], centroids[:, 1],
                                     sites.lons, sites.lats)
    #logging.info("%s  -  %s", str(ridx), str(numpy.min(distance)))
    return numpy.any(distance <= integration_distance)
示例#3
0
    def get_rupture_sites(self, hdf5, ridx, src_filter, mag):
        """
        Determines if a rupture is likely to be inside the integration distance
        by considering the set of fault plane centroids and returns the
        affected sites if any.

        :param hdf5:
            Source of UCERF file as h5py.File object
        :param ridx:
            List of indices composing the rupture sections
        :param src_filter:
            SourceFilter instance
        :param mag:
            Magnitude of the rupture for consideration
        :returns:
            The sites affected by the rupture (or None)
        """
        centroids = []
        for idx in ridx:
            trace_idx = "{:s}/{:s}".format(self.idx_set["sec_idx"], str(idx))
            centroids.append(hdf5[trace_idx + "/Centroids"].value)
        centroids = numpy.concatenate(centroids)
        lons, lats = src_filter.sitecol.lons, src_filter.sitecol.lats
        distance = min_geodetic_distance(centroids[:, 0], centroids[:, 1],
                                         lons, lats)
        idist = src_filter.integration_distance(DEFAULT_TRT, mag)
        return src_filter.sitecol.filter(distance <= idist)
示例#4
0
    def filter_sites_by_distance_from_rupture_set(
            self, rupset_idx, sites, max_dist):
        """
        Filter sites by distances from a set of ruptures
        """
        with h5py.File(self.source_file, "r") as hdf5:
            rup_index_key = "/".join([self.idx_set["geol_idx"],
                                      "RuptureIndex"])

            # Find the combination of rupture sections used in this model
            rupture_set = set()
            # Determine which of the rupture sections used in this set
            # of indices
            for i in rupset_idx:
                rupture_set.update(hdf5[rup_index_key][i])
            centroids = np.empty([1, 3])
            # For each of the identified rupture sections, retreive the
            # centroids
            for ridx in rupture_set:
                trace_idx = "{:s}/{:s}".format(self.idx_set["sec_idx"],
                                               str(ridx))
                centroids = np.vstack([
                    centroids,
                    hdf5[trace_idx + "/Centroids"][:].astype("float64")])
            distance = min_geodetic_distance(centroids[1:, 0],
                                             centroids[1:, 1],
                                             sites.lons, sites.lats)
            idx = distance <= max_dist
            if np.any(idx):
                return rupset_idx, sites.filter(idx)
            else:
                return [], []
示例#5
0
    def _test(self, mlons, mlats, mdepths, slons, slats, sdepths,
              expected_mpoint_indices):
        mlons, mlats, mdepths = [numpy.array(arr, float)
                                 for arr in (mlons, mlats, mdepths)]
        slons, slats, sdepths = [numpy.array(arr, float)
                                 for arr in (slons, slats, sdepths)]
        actual_indices = geodetic.min_distance(mlons, mlats, mdepths,
                                               slons, slats, sdepths,
                                               indices=True)
        numpy.testing.assert_equal(actual_indices, expected_mpoint_indices)
        dists = geodetic.min_distance(mlons, mlats, mdepths,
                                      slons, slats, sdepths)
        expected_closest_mlons = mlons.flat[expected_mpoint_indices]
        expected_closest_mlats = mlats.flat[expected_mpoint_indices]
        expected_closest_mdepths = mdepths.flat[expected_mpoint_indices]
        expected_distances = geodetic.distance(
            expected_closest_mlons, expected_closest_mlats,
            expected_closest_mdepths,
            slons, slats, sdepths
        )
        self.assertTrue((dists == expected_distances).all())

        # testing min_geodetic_distance with the same lons and lats
        min_geod_distance = geodetic.min_geodetic_distance(mlons, mlats,
                                                           slons, slats)
        min_geo_distance2 = geodetic.min_distance(mlons, mlats, mdepths * 0,
                                                  slons, slats, sdepths * 0)
        numpy.testing.assert_almost_equal(min_geod_distance, min_geo_distance2)
示例#6
0
文件: smooth.py 项目: gem/oq-mbt
	def gaussian(self, radius, sigma):
		# Values
		values = numpy.zeros((len(self.mesh)))
		# Compute the number of expected nodes
		numpnts = consts.pi*radius**2/(self.cellsize**2)
		# Smoothing the catalogue
		for lon, lat, mag in zip(self.catalogue.data['longitude'],
								 self.catalogue.data['latitude'],
								 self.catalogue.data['magnitude']):
			# set the bounding box
			minlon, minlat = point_at(lon, lat, 225, radius*2**0.5)
			maxlon, maxlat = point_at(lon, lat, 45, radius*2**0.5) 
			# find nodes within the bounding box
			idxs = list(self.rtree.intersection((minlon, 
					                             minlat, 
			                                     maxlon, 
			                                     maxlat)))
			# get distances
			dsts = min_geodetic_distance(lon, lat, 
			                             self.mesh.lons[idxs],
			                             self.mesh.lats[idxs])   
			jjj = numpy.nonzero(dsts < 50)[0]
			idxs = numpy.array(idxs)
			iii = idxs[jjj]
			# set values
			tmpvalues= numpy.exp(-dsts[jjj]/sigma**2)
			normfact = sum(tmpvalues)
			values[iii] += tmpvalues/normfact
		return values
示例#7
0
 def get_background_sids(self):
     """
     We can apply the filtering of the background sites as a pre-processing
     step - this is done here rather than in the sampling of the ruptures
     themselves
     """
     branch_key = self.ukey["grid_key"]
     with h5py.File(self.source_file, 'r') as hdf5:
         bg_locations = hdf5["Grid/Locations"][()]
         if hasattr(self, 'src_filter'):
             # in event based
             idist = self.src_filter.integration_distance[DEFAULT_TRT][-1][
                 1]
         else:
             # in classical
             return numpy.arange(len(bg_locations))
         distances = min_geodetic_distance(
             self.src_filter.sitecol.xyz,
             (bg_locations[:, 0], bg_locations[:, 1]))
         # Add buffer equal to half of length of median area from Mmax
         mmax_areas = self.msr.get_median_area(
             hdf5["/".join(["Grid", branch_key, "MMax"])][()], 0.0)
         # for instance hdf5['Grid/FM0_0_MEANFS_MEANMSR/MMax']
         mmax_lengths = numpy.sqrt(mmax_areas / self.aspect)
         ok = distances <= (0.5 * mmax_lengths + idist)
         # get list of indices from array of booleans
         return numpy.where(ok)[0].tolist()
示例#8
0
 def get_indices(self, src, ridx, mag):
     """
     :param src: an UCERF source
     :param ridx: a set of rupture indices
     :param mag: magnitude to use to compute the integration distance
     :returns: array with the IDs of the sites close to the ruptures
     """
     centroids = src.get_centroids(ridx)
     mindistance = min_geodetic_distance(
         (centroids[:, 0], centroids[:, 1]), self.sitecol.xyz)
     idist = self.integration_distance(DEFAULT_TRT, mag)
     indices, = (mindistance <= idist).nonzero()
     return indices
示例#9
0
 def get_indices(self, src, ridx, mag):
     """
     :param src: an UCERF source
     :param ridx: a set of rupture indices
     :param mag: magnitude to use to compute the integration distance
     :returns: array with the IDs of the sites close to the ruptures
     """
     centroids = src.get_centroids(ridx)
     mindistance = min_geodetic_distance((centroids[:, 0], centroids[:, 1]),
                                         self.sitecol.xyz)
     idist = self.integration_distance(DEFAULT_TRT, mag)
     indices, = (mindistance <= idist).nonzero()
     return indices
示例#10
0
    def get_joyner_boore_distance(self, mesh):

        # Get indexes of the finite points composing the edges
        iupp = np.nonzero(np.isfinite(self.mesh.lons[0, :]))[0]
        ilow = np.flipud(np.nonzero(np.isfinite(self.mesh.lons[-1, :]))[0])
        irig = np.nonzero(np.isfinite(self.mesh.lons[:, -1]))[0]
        ilef = np.flipud(np.nonzero(np.isfinite(self.mesh.lons[:, 0]))[0])

        # Building the polygon
        pnts = []
        for corner in [(0, iupp), (irig, -1), (-1, ilow), (ilef, 0)]:
            pnts.extend(
                zip(self.mesh.lons[corner], self.mesh.lats[corner],
                    self.mesh.depths[corner]))
        perimeter = np.array(pnts)

        distances = geodetic.min_geodetic_distance(
            (perimeter[:, 0], perimeter[:, 1]), (mesh.lons, mesh.lats))

        idxs = (distances < 40).nonzero()[0]  # indices on the first dimension
        if not len(idxs):
            # no point is close enough, return distances as they are
            return distances

        # Get the projection
        proj = geo_utils.OrthographicProjection(
            *geo_utils.get_spherical_bounding_box(perimeter[:,
                                                            0], perimeter[:,
                                                                          1]))

        # Mesh projected coordinates
        mesh_xx, mesh_yy = proj(mesh.lons[idxs], mesh.lats[idxs])

        # Create the shapely Polygon using projected coordinates
        xp, yp = proj(perimeter[:, 0], perimeter[:, 1])
        polygon = Polygon([[x, y] for x, y in zip(xp, yp)])

        # Calculate the distances
        distances[idxs] = geo_utils.point_to_polygon_distance(
            polygon, mesh_xx, mesh_yy)

        return distances
示例#11
0
    def get_rupture_sites(self, ridx, src_filter, mag):
        """
        Determines if a rupture is likely to be inside the integration distance
        by considering the set of fault plane centroids and returns the
        affected sites if any.

        :param ridx:
            List of indices composing the rupture sections
        :param src_filter:
            SourceFilter instance
        :param mag:
            Magnitude of the rupture for consideration
        :returns:
            The sites affected by the rupture (or None)
        """
        centroids = self.get_centroids(ridx)
        distance = min_geodetic_distance((centroids[:, 0], centroids[:, 1]),
                                         src_filter.sitecol.xyz)
        idist = src_filter.integration_distance(DEFAULT_TRT, mag)
        return src_filter.sitecol.filter(distance <= idist)
示例#12
0
 def get_background_sids(self, src_filter):
     """
     We can apply the filtering of the background sites as a pre-processing
     step - this is done here rather than in the sampling of the ruptures
     themselves
     """
     branch_key = self.idx_set["grid_key"]
     idist = src_filter.integration_distance(DEFAULT_TRT)
     lons, lats = src_filter.sitecol.lons, src_filter.sitecol.lats
     with h5py.File(self.source_file, 'r') as hdf5:
         bg_locations = hdf5["Grid/Locations"].value
         distances = min_geodetic_distance(
             lons, lats, bg_locations[:, 0], bg_locations[:, 1])
         # Add buffer equal to half of length of median area from Mmax
         mmax_areas = self.msr.get_median_area(
             hdf5["/".join(["Grid", branch_key, "MMax"])].value, 0.0)
         # for instance hdf5['Grid/FM0_0_MEANFS_MEANMSR/MMax']
         mmax_lengths = numpy.sqrt(mmax_areas / self.aspect)
         ok = distances <= (0.5 * mmax_lengths + idist)
         # get list of indices from array of booleans
         return numpy.where(ok)[0].tolist()
示例#13
0
 def get_background_sids(self, src_filter):
     """
     We can apply the filtering of the background sites as a pre-processing
     step - this is done here rather than in the sampling of the ruptures
     themselves
     """
     branch_key = self.idx_set["grid_key"]
     idist = src_filter.integration_distance(DEFAULT_TRT)
     with h5py.File(self.source_file, 'r') as hdf5:
         bg_locations = hdf5["Grid/Locations"].value
         distances = min_geodetic_distance(
             src_filter.sitecol.xyz,
             (bg_locations[:, 0], bg_locations[:, 1]))
         # Add buffer equal to half of length of median area from Mmax
         mmax_areas = self.msr.get_median_area(
             hdf5["/".join(["Grid", branch_key, "MMax"])].value, 0.0)
         # for instance hdf5['Grid/FM0_0_MEANFS_MEANMSR/MMax']
         mmax_lengths = numpy.sqrt(mmax_areas / self.aspect)
         ok = distances <= (0.5 * mmax_lengths + idist)
         # get list of indices from array of booleans
         return numpy.where(ok)[0].tolist()
示例#14
0
    def get_joyner_boore_distance(self, mesh) -> np.ndarray:
        """
        Computes the Rjb distance between the rupture and the points included
        in the mesh provided.

        :param mesh:
            An instance of :class:`openquake.hazardlib.geo.mesh.Mesh`
        :returns:
            A :class:`numpy.ndarray` instance with the Rjb values
        """

        blo, bla = self._get_external_boundary()
        distances = geodetic.min_geodetic_distance((blo, bla),
                                                   (mesh.lons, mesh.lats))

        idxs = (distances < 40).nonzero()[0]  # indices on the first dimension
        if len(idxs) < 1:
            # no point is close enough, return distances as they are
            return distances

        # Get the projection
        proj = geo_utils.OrthographicProjection(
            *geo_utils.get_spherical_bounding_box(blo, bla))

        # Mesh projected coordinates
        mesh_xx, mesh_yy = proj(mesh.lons[idxs], mesh.lats[idxs])

        # Create the shapely Polygon using projected coordinates
        xp, yp = proj(blo, bla)
        polygon = Polygon([[x, y] for x, y in zip(xp, yp)])

        # Calculate the distances
        distances[idxs] = geo_utils.point_to_polygon_distance(
            polygon, mesh_xx, mesh_yy)

        return distances
示例#15
0
def fault_surface_distance(srcs, mesh_spacing, types=None, dmax=40):
    """
    :parameter srcs:
        A list of openquake sources
    :parameter float mesh_spacing:
        The spacing [km] of the grid used to represent the fault surface
    :parameter types:
        The type of sources to be analysed
    :parameter dmax:
    """
    #
    # Fault distance array
    lnkg = np.ones((len(srcs), len(srcs))) * dmax
    #
    # Check input information
    if types is None:
        types = (SimpleFaultSource, CharacteristicFaultSource)
    #
    # Process srcs
    for idxa in range(0, len(srcs)):
        srca = srcs[idxa]
        if isinstance(srca, types):
            #
            # set the mesh for the first fault surface
            if isinstance(srca, SimpleFaultSource):
                meshA, chA = _get_mesh_ch_from_simple_fs(srca, mesh_spacing)
            elif isinstance(srca, CharacteristicFaultSource):
                meshA, chA = _get_mesh_ch_from_char_fs(srca, mesh_spacing)
            else:
                raise ValueError('Unsupported fault type')
            #
            # second loop
            for idxB in range(idxa, len(srcs)):
                srcb = srcs[idxB]
                if isinstance(srcb, types):
                    #
                    # set the mesh for the second fault surface
                    if isinstance(srcb, SimpleFaultSource):
                        meshB, chB = _get_mesh_ch_from_simple_fs(
                            srcb, mesh_spacing)
                    elif isinstance(srcb, CharacteristicFaultSource):
                        meshB, chB = _get_mesh_ch_from_char_fs(
                            srcb, mesh_spacing)
                    else:
                        raise ValueError('Unsupported fault type')
                    #
                    # Calculate the distance between the two bounding boxes
                    la = [(a, b) for a, b in zip(chA.lons, chA.lats)]
                    lb = [(a, b) for a, b in zip(chB.lons, chB.lats)]
                    tmpd = min_geodetic_distance(la, lb)
                    #
                    # Calculate the distance between the two fault surfaces
                    # (if needed)
                    if (np.amin(tmpd) > dmax):
                        mindst = np.amin(tmpd)
                    else:
                        mindst = np.amin(meshA.get_min_distance(meshB))
                    #
                    # Update the array
                    lnkg[idxa, idxB] = mindst
    #
    # check that the size of the linkage matrix corresponds to the size of the
    # initial list of fault sources
    assert len(lnkg) == len(srcs)
    return lnkg
示例#16
0
    def get_joyner_boore_distance(self, mesh):
        """
        See :meth:`superclass' method
        <openquake.hazardlib.geo.surface.base.BaseSurface.get_joyner_boore_distance>`.

        This is an optimized version specific to planar surface that doesn't
        make use of the mesh.
        """
        # we define four great circle arcs that contain four sides
        # of projected planar surface:
        #
        #       ↓     II    ↓
        #    I  ↓           ↓  I
        #       ↓     +     ↓
        #  →→→→→TL→→→→1→→→→TR→→→→→     → azimuth direction →
        #       ↓     -     ↓
        #       ↓           ↓
        # III  -3+   IV    -4+  III             ↓
        #       ↓           ↓            downdip direction
        #       ↓     +     ↓                   ↓
        #  →→→→→BL→→→→2→→→→BR→→→→→
        #       ↓     -     ↓
        #    I  ↓           ↓  I
        #       ↓     II    ↓
        #
        # arcs 1 and 2 are directed from left corners to right ones (the
        # direction has an effect on the sign of the distance to an arc,
        # as it shown on the figure), arcs 3 and 4 are directed from top
        # corners to bottom ones.
        #
        # then we measure distance from each of the points in a mesh
        # to each of those arcs and compare signs of distances in order
        # to find a relative positions of projections of points and
        # projection of a surface.
        #
        # then we consider four special cases (labeled with Roman numerals)
        # and either pick one of distances to arcs or a closest distance
        # to corner.
        #
        # indices 0, 2 and 1 represent corners TL, BL and TR respectively.
        arcs_lons = self.corner_lons.take([0, 2, 0, 1])
        arcs_lats = self.corner_lats.take([0, 2, 0, 1])
        downdip_azimuth = (self.strike + 90) % 360
        arcs_azimuths = [
            self.strike, self.strike, downdip_azimuth, downdip_azimuth
        ]
        mesh_lons = mesh.lons.reshape((-1, 1))
        mesh_lats = mesh.lats.reshape((-1, 1))
        # calculate distances from all the target points to all four arcs
        dists_to_arcs = geodetic.distance_to_arc(arcs_lons, arcs_lats,
                                                 arcs_azimuths, mesh_lons,
                                                 mesh_lats)
        # ... and distances from all the target points to each of surface's
        # corners' projections (we might not need all of those but it's
        # better to do that calculation once for all).
        dists_to_corners = geodetic.min_geodetic_distance(
            (self.corner_lons, self.corner_lats), mesh.xyz)

        # extract from ``dists_to_arcs`` signs (represent relative positions
        # of an arc and a point: +1 means on the left hand side, 0 means
        # on arc and -1 means on the right hand side) and minimum absolute
        # values of distances to each pair of parallel arcs.
        ds1, ds2, ds3, ds4 = numpy.sign(dists_to_arcs).transpose()
        dists_to_arcs = numpy.abs(dists_to_arcs).reshape(-1, 2, 2).min(axis=-1)

        jb_dists = numpy.select(
            # consider four possible relative positions of point and arcs:
            condlist=[
                # signs of distances to both parallel arcs are the same
                # in both pairs, case "I" on a figure above
                (ds1 == ds2) & (ds3 == ds4),
                # sign of distances to two parallels is the same only
                # in one pair, case "II"
                ds1 == ds2,
                # ... or another (case "III")
                ds3 == ds4
                # signs are different in both pairs (this is a "default"),
                # case "IV"
            ],
            choicelist=[
                # case "I": closest distance is the closest distance to corners
                dists_to_corners,
                # case "II": closest distance is distance to arc "1" or "2",
                # whichever is closer
                dists_to_arcs[:, 0],
                # case "III": closest distance is distance to either
                # arc "3" or "4"
                dists_to_arcs[:, 1]
            ],

            # default -- case "IV"
            default=0)

        return jb_dists.reshape(mesh.lons.shape)
示例#17
0
    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:`openquake.hazardlib.geo.surface.base.BaseQuadrilateralSurface.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.
        """
        # we perform a hybrid calculation (geodetic mesh-to-mesh distance
        # and distance on the projection plane for close points). first,
        # we find the closest geodetic distance for each point of target
        # mesh to this one. in general that distance is greater than
        # the exact distance to enclosing polygon of this mesh and it
        # depends on mesh spacing. but the difference can be neglected
        # if calculated geodetic distance is over some threshold.
        distances = geodetic.min_geodetic_distance(self.lons, self.lats,
                                                   mesh.lons.flatten(),
                                                   mesh.lats.flatten())

        # here we find the points for which calculated mesh-to-mesh
        # distance is below a threshold. this threshold is arbitrary:
        # lower values increase the maximum possible error, higher
        # values reduce the efficiency of that filtering. the maximum
        # error is equal to the maximum difference between a distance
        # from site to two adjacent points of the mesh and distance
        # from site to the line connecting them. thus the error is
        # a function of distance threshold and mesh spacing. the error
        # is maximum when the site lies on a perpendicular to the line
        # connecting points of the mesh and that passes the middle
        # point between them. the error then can be calculated as
        # ``err = trsh - d = trsh - \sqrt(trsh^2 - (ms/2)^2)``, where
        # ``trsh`` and ``d`` are distance to mesh points (the one
        # we found on the previous step) and distance to the line
        # connecting them (the actual distance) and ``ms`` is mesh
        # spacing. the threshold of 40 km gives maximum error of 314
        # meters for meshes with spacing of 10 km and 5.36 km for
        # meshes with spacing of 40 km. if mesh spacing is over
        # ``(trsh / \sqrt(2)) * 2`` then points lying in the middle
        # of mesh cells (that is inside the polygon) will be filtered
        # out by the threshold and have positive distance instead of 0.
        # so for threshold of 40 km mesh spacing should not be more
        # than 56 km (typical values are 5 to 10 km).

        [idxs] = (distances < 40).nonzero()
        if not len(idxs):
            # no point is close enough, return distances as they are
            return distances

        # for all the points that are closer than the threshold we need
        # to recalculate the distance and set it to zero, if point falls
        # inside the enclosing polygon of the mesh. for doing that we
        # project both this mesh and the points of the second mesh--selected
        # by distance threshold--to the same Cartesian space, define
        # minimum shapely polygon enclosing the mesh and calculate point
        # to polygon distance, which gives the most accurate value
        # of distance in km (and that value is zero for points inside
        # the polygon).
        proj, polygon = self._get_proj_enclosing_polygon()
        if not isinstance(polygon, shapely.geometry.Polygon):
            # either line or point is our enclosing polygon. draw
            # a square with side of 10 m around in order to have
            # a proper polygon instead.
            polygon = polygon.buffer(self.DIST_TOLERANCE, 1)
        mesh_lons, mesh_lats = mesh.lons.take(idxs), mesh.lats.take(idxs)
        mesh_xx, mesh_yy = proj(mesh_lons, mesh_lats)
        distances_2d = geo_utils.point_to_polygon_distance(
            polygon, mesh_xx, mesh_yy)

        # replace geodetic distance values for points-closer-than-the-threshold
        # by more accurate point-to-polygon distance values.
        distances.put(idxs, distances_2d)
        return distances.reshape(mesh.shape)
示例#18
0
    def get_joyner_boore_distance(self, mesh):
        """
        See :meth:`superclass' method
        <openquake.hazardlib.geo.surface.base.BaseQuadrilateralSurface.get_joyner_boore_distance>`.

        This is an optimized version specific to planar surface that doesn't
        make use of the mesh.
        """
        # we define four great circle arcs that contain four sides
        # of projected planar surface:
        #
        #       ↓     II    ↓
        #    I  ↓           ↓  I
        #       ↓     +     ↓
        #  →→→→→TL→→→→1→→→→TR→→→→→     → azimuth direction →
        #       ↓     -     ↓
        #       ↓           ↓
        # III  -3+   IV    -4+  III             ↓
        #       ↓           ↓            downdip direction
        #       ↓     +     ↓                   ↓
        #  →→→→→BL→→→→2→→→→BR→→→→→
        #       ↓     -     ↓
        #    I  ↓           ↓  I
        #       ↓     II    ↓
        #
        # arcs 1 and 2 are directed from left corners to right ones (the
        # direction has an effect on the sign of the distance to an arc,
        # as it shown on the figure), arcs 3 and 4 are directed from top
        # corners to bottom ones.
        #
        # then we measure distance from each of the points in a mesh
        # to each of those arcs and compare signs of distances in order
        # to find a relative positions of projections of points and
        # projection of a surface.
        #
        # then we consider four special cases (labeled with Roman numerals)
        # and either pick one of distances to arcs or a closest distance
        # to corner.
        #
        # indices 0, 2 and 1 represent corners TL, BL and TR respectively.
        arcs_lons = self.corner_lons.take([0, 2, 0, 1])
        arcs_lats = self.corner_lats.take([0, 2, 0, 1])
        downdip_azimuth = (self.strike + 90) % 360
        arcs_azimuths = [self.strike, self.strike,
                         downdip_azimuth, downdip_azimuth]
        mesh_lons = mesh.lons.reshape((-1, 1))
        mesh_lats = mesh.lats.reshape((-1, 1))
        # calculate distances from all the target points to all four arcs
        dists_to_arcs = geodetic.distance_to_arc(
            arcs_lons, arcs_lats, arcs_azimuths, mesh_lons, mesh_lats
        )
        # ... and distances from all the target points to each of surface's
        # corners' projections (we might not need all of those but it's
        # better to do that calculation once for all).
        dists_to_corners = geodetic.min_geodetic_distance(
            self.corner_lons, self.corner_lats,
            mesh.lons.flatten(), mesh.lats.flatten()
        )

        # extract from ``dists_to_arcs`` signs (represent relative positions
        # of an arc and a point: +1 means on the left hand side, 0 means
        # on arc and -1 means on the right hand side) and minimum absolute
        # values of distances to each pair of parallel arcs.
        ds1, ds2, ds3, ds4 = numpy.sign(dists_to_arcs).transpose()
        dists_to_arcs = numpy.abs(dists_to_arcs).reshape(-1, 2, 2).min(axis=-1)

        jb_dists = numpy.select(
            # consider four possible relative positions of point and arcs:
            condlist=[
                # signs of distances to both parallel arcs are the same
                # in both pairs, case "I" on a figure above
                (ds1 == ds2) & (ds3 == ds4),
                # sign of distances to two parallels is the same only
                # in one pair, case "II"
                ds1 == ds2,
                # ... or another (case "III")
                ds3 == ds4
                # signs are different in both pairs (this is a "default"),
                # case "IV"
            ],

            choicelist=[
                # case "I": closest distance is the closest distance to corners
                dists_to_corners,
                # case "II": closest distance is distance to arc "1" or "2",
                # whichever is closer
                dists_to_arcs[:, 0],
                # case "III": closest distance is distance to either
                # arc "3" or "4"
                dists_to_arcs[:, 1]
            ],

            # default -- case "IV"
            default=0
        )

        return jb_dists.reshape(mesh.lons.shape)
示例#19
0
文件: mesh.py 项目: gem/oq-engine
    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:`openquake.hazardlib.geo.surface.base.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.
        """
        # we perform a hybrid calculation (geodetic mesh-to-mesh distance
        # and distance on the projection plane for close points). first,
        # we find the closest geodetic distance for each point of target
        # mesh to this one. in general that distance is greater than
        # the exact distance to enclosing polygon of this mesh and it
        # depends on mesh spacing. but the difference can be neglected
        # if calculated geodetic distance is over some threshold.
        # get the highest slice from the 3D mesh
        distances = geodetic.min_geodetic_distance(
            (self.lons, self.lats), (mesh.lons, mesh.lats))
        # here we find the points for which calculated mesh-to-mesh
        # distance is below a threshold. this threshold is arbitrary:
        # lower values increase the maximum possible error, higher
        # values reduce the efficiency of that filtering. the maximum
        # error is equal to the maximum difference between a distance
        # from site to two adjacent points of the mesh and distance
        # from site to the line connecting them. thus the error is
        # a function of distance threshold and mesh spacing. the error
        # is maximum when the site lies on a perpendicular to the line
        # connecting points of the mesh and that passes the middle
        # point between them. the error then can be calculated as
        # ``err = trsh - d = trsh - \sqrt(trsh^2 - (ms/2)^2)``, where
        # ``trsh`` and ``d`` are distance to mesh points (the one
        # we found on the previous step) and distance to the line
        # connecting them (the actual distance) and ``ms`` is mesh
        # spacing. the threshold of 40 km gives maximum error of 314
        # meters for meshes with spacing of 10 km and 5.36 km for
        # meshes with spacing of 40 km. if mesh spacing is over
        # ``(trsh / \sqrt(2)) * 2`` then points lying in the middle
        # of mesh cells (that is inside the polygon) will be filtered
        # out by the threshold and have positive distance instead of 0.
        # so for threshold of 40 km mesh spacing should not be more
        # than 56 km (typical values are 5 to 10 km).
        idxs = (distances < 40).nonzero()[0]  # indices on the first dimension
        if not len(idxs):
            # no point is close enough, return distances as they are
            return distances

        # for all the points that are closer than the threshold we need
        # to recalculate the distance and set it to zero, if point falls
        # inside the enclosing polygon of the mesh. for doing that we
        # project both this mesh and the points of the second mesh--selected
        # by distance threshold--to the same Cartesian space, define
        # minimum shapely polygon enclosing the mesh and calculate point
        # to polygon distance, which gives the most accurate value
        # of distance in km (and that value is zero for points inside
        # the polygon).
        proj, polygon = self._get_proj_enclosing_polygon()
        if not isinstance(polygon, shapely.geometry.Polygon):
            # either line or point is our enclosing polygon. draw
            # a square with side of 10 m around in order to have
            # a proper polygon instead.
            polygon = polygon.buffer(self.DIST_TOLERANCE, 1)
        mesh_xx, mesh_yy = proj(mesh.lons[idxs], mesh.lats[idxs])
        # replace geodetic distance values for points-closer-than-the-threshold
        # by more accurate point-to-polygon distance values.
        distances[idxs] = geo_utils.point_to_polygon_distance(
            polygon, mesh_xx, mesh_yy)

        return distances