def get_ry0_distance(self, mesh): """ :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Ry0-distance to. :returns: Numpy array of distances in km. See also :meth:`superclass method <.base.BaseSurface.get_ry0_distance>` for spec of input and result values. This is version specific to the planar surface doesn't make use of the mesh """ dst1 = geodetic.distance_to_arc(self.top_left.longitude, self.top_left.latitude, (self.strike + 90.) % 360, mesh.lons, mesh.lats) dst2 = geodetic.distance_to_arc(self.top_right.longitude, self.top_right.latitude, (self.strike + 90.) % 360, mesh.lons, mesh.lats) # Find the points on the rupture # Get the shortest distance from the two lines idx = numpy.sign(dst1) == numpy.sign(dst2) dst = numpy.zeros_like(dst1) dst[idx] = numpy.fmin(numpy.abs(dst1[idx]), numpy.abs(dst2[idx])) return dst
def get_ry0_distance(self, mesh): """ Compute the minimum distance between each point of a mesh and the great circle arcs perpendicular to the average strike direction of the fault trace and passing through the end-points of the trace. :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Ry0-distance to. :returns: Numpy array of distances in km. """ # This computes ry0 by using an average strike direction top_edge = self.mesh[0:1] mean_strike = self.get_strike() dst1 = geodetic.distance_to_arc(top_edge.lons[0, 0], top_edge.lats[0, 0], (mean_strike + 90.) % 360, mesh.lons, mesh.lats) dst2 = geodetic.distance_to_arc(top_edge.lons[0, -1], top_edge.lats[0, -1], (mean_strike + 90.) % 360, mesh.lons, mesh.lats) # Find the points on the rupture # Get the shortest distance from the two lines idx = numpy.sign(dst1) == numpy.sign(dst2) dst = numpy.zeros_like(dst1) dst[idx] = numpy.fmin(numpy.abs(dst1[idx]), numpy.abs(dst2[idx])) return dst
def calc_ry0_distance(P0, P1, lat, lon, dep): """Calculate Ry0 distance. Compute the minimum distance between sites (lat, lon, dep) and the great circle arcs perpendicular to the average strike direction of the fault trace and passing through the end-points of the trace. :param P0: Point object, representing the first top-edge vertex of a fault quadrilateral. :param P1: Point object, representing the second top-edge vertex of a fault quadrilateral. :param lat: Numpy array of latitude. :param lon: Numpy array of longitude. :param dep: Numpy array of depths (km). :returns: Array of size lon.shape of distances (in km) from input points to rupture surface. """ # Strike surfaceP0 = point.Point(P0.longitude, P0.latitude, 0.0) surfaceP1 = point.Point(P1.longitude, P1.latitude, 0.0) strike = P0.azimuth(P1) dst1 = geodetic.distance_to_arc(P0.longitude,P0.latitude, (strike + 90.) % 360, lon, lat) dst2 = geodetic.distance_to_arc(P1.longitude,P1.latitude, (strike + 90.) % 360, lon, lat) # Get the shortest distance from the two lines idx = np.sign(dst1) == np.sign(dst2) dst = np.zeros_like(dst1) dst[idx] = np.fmin(np.abs(dst1[idx]), np.abs(dst2[idx])) return dst
def get_ry0_distance(self, mesh): """ :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Ry0-distance to. :returns: Numpy array of distances in km. See also :meth:`superclass method <.base.BaseSurface.get_ry0_distance>` for spec of input and result values. This method uses an average strike direction to compute ry0. """ # This computes ry0 by using an average strike direction top_edge = self.get_mesh()[0:1] mean_strike = self.get_strike() dst1 = geodetic.distance_to_arc(top_edge.lons[0, 0], top_edge.lats[0, 0], (mean_strike + 90.) % 360, mesh.lons, mesh.lats) dst2 = geodetic.distance_to_arc(top_edge.lons[0, -1], top_edge.lats[0, -1], (mean_strike + 90.) % 360, mesh.lons, mesh.lats) # Find the points on the rupture # Get the shortest distance from the two lines idx = numpy.sign(dst1) == numpy.sign(dst2) dst = numpy.zeros_like(dst1) dst[idx] = numpy.fmin(numpy.abs(dst1[idx]), numpy.abs(dst2[idx])) return dst
def calc_ry0_distance(P0, P1, lat, lon, dep): """Calculate Ry0 distance. Compute the minimum distance between sites (lat, lon, dep) and the great circle arcs perpendicular to the average strike direction of the fault trace and passing through the end-points of the trace. :param P0: Point object, representing the first top-edge vertex of a fault quadrilateral. :param P1: Point object, representing the second top-edge vertex of a fault quadrilateral. :param lat: Numpy array of latitude. :param lon: Numpy array of longitude. :param dep: Numpy array of depths (km). :returns: Array of size lon.shape of distances (in km) from input points to rupture surface. """ # Strike surfaceP0 = point.Point(P0.longitude, P0.latitude, 0.0) surfaceP1 = point.Point(P1.longitude, P1.latitude, 0.0) strike = P0.azimuth(P1) dst1 = geodetic.distance_to_arc(P0.longitude, P0.latitude, (strike + 90.) % 360, lon, lat) dst2 = geodetic.distance_to_arc(P1.longitude, P1.latitude, (strike + 90.) % 360, lon, lat) # Get the shortest distance from the two lines idx = np.sign(dst1) == np.sign(dst2) dst = np.zeros_like(dst1) dst[idx] = np.fmin(np.abs(dst1[idx]), np.abs(dst2[idx])) return dst
def test_one_point(self): dist = geodetic.distance_to_arc(12.3, 44.5, 39.4, plons=13.4, plats=46.9) self.assertAlmostEqual(dist, -105.12464364) dist = geodetic.distance_to_arc(12.3, 44.5, 39.4, plons=13.4, plats=44.9) self.assertAlmostEqual(dist, 38.34459954)
def get_ry0_distance(self, mesh): """ Compute the minimum distance between each point of a mesh and the great circle arcs perpendicular to the average strike direction of the fault trace and passing through the end-points of the trace. :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Ry0-distance to. :returns: Numpy array of distances in km. """ # This computes ry0 by using an average strike direction top_edge = self.mesh[0:1] mean_strike = self.get_strike() # Manage the case of kite fault surfaces ia = 0 ib = -1 if (self.__class__.__name__ == 'KiteSurface'): idx = numpy.nonzero(numpy.isfinite(top_edge.lons[0, :]))[0] ia = min(idx) ib = max(idx) # Computing the distances between the sites and the two lines # perpendicular to the strike passing trough the two extremes # of the top of the rupture dst1 = geodetic.distance_to_arc(top_edge.lons[0, ia], top_edge.lats[0, ia], (mean_strike + 90.) % 360, mesh.lons, mesh.lats) dst2 = geodetic.distance_to_arc(top_edge.lons[0, ib], top_edge.lats[0, ib], (mean_strike + 90.) % 360, mesh.lons, mesh.lats) # Get the shortest distance from the two lines idx = numpy.sign(dst1) == numpy.sign(dst2) dst = numpy.zeros_like(dst1) dst[idx] = numpy.fmin(numpy.abs(dst1[idx]), numpy.abs(dst2[idx])) if numpy.any(numpy.isnan(dst)): raise ValueError('NaN in Ry0') return dst
def get_rx_distance(self, mesh): """ Compute distance between each point of mesh and surface's great circle arc. Distance is measured perpendicular to the rupture strike, from the surface projection of the updip edge of the rupture, with the down dip direction being positive (this distance is usually called ``Rx``). In other words, is the horizontal distance to top edge of rupture measured perpendicular to the strike. Values on the hanging wall are positive, values on the footwall are negative. Base class calls :func:`openquake.hazardlib.geo.geodetic.distance_to_arc`. :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Rx-distance to. :returns: Numpy array of distances in km. """ top_edge_centroid = self._get_top_edge_centroid() return geodetic.distance_to_arc( top_edge_centroid.longitude, top_edge_centroid.latitude, self.get_strike(), mesh.lons, mesh.lats )
def get_rx_distance(self, mesh): """ See :meth:`superclass method <.base.BaseSurface.get_rx_distance>` for spec of input and result values. The method extracts the top edge of the surface. For each point in mesh, it then computes the Rx distance to each segment the top edge is made of. The calculation is done by calling the function :func:`openquake.hazardlib.geo.geodetic.distance_to_arc`. The final Rx distance matrix is then constructed by taking, for each point in mesh, the minimum Rx distance value computed. """ top_edge = self.get_mesh()[0:1] dists = [] for i in range(top_edge.lons.shape[1] - 1): p1 = Point( top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i] ) p2 = Point( top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1] ) azimuth = p1.azimuth(p2) dists.append( geodetic.distance_to_arc( p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats ) ) dists = numpy.array(dists) return numpy.min(dists, axis=0)
def test_one_point(self): dist = float( geodetic.distance_to_semi_arc(12.3, 44.5, 39.4, plons=13.4, plats=46.9)) self.assertAlmostEqual(dist, -105.12464364) dist = float( geodetic.distance_to_arc(12.3, 44.5, 219.4, plons=13.4, plats=46.9)) self.assertAlmostEqual(dist, +105.12464364) dist = float( geodetic.distance_to_semi_arc(12.3, 44.5, 39.4, plons=13.4, plats=44.9)) self.assertAlmostEqual(dist, 38.34459954) # This tests the distance to a point in the y-negative halfspace in a # reference system which uses as the origin the reference point # (i.e. (12.3; 44.5)) and the y direction as the direction with # azimuth = 39.4) dist = float( geodetic.distance_to_semi_arc(12.3, 44.5, 39.4, plons=11.3, plats=44.5)) self.assertAlmostEqual(dist, -79.3093368)
def get_rx_distance(self, mesh): """ See :meth:`superclass method <.base.BaseQuadrilateralSurface.get_rx_distance>` for spec of input and result values. This is an optimized version specific to planar surface that doesn't make use of the mesh. """ return geodetic.distance_to_arc(self.corner_lons[0], self.corner_lats[0], self.strike, mesh.lons, mesh.lats)
def get_rx_distance(self, mesh): """ See :meth:`superclass method <.base.BaseSurface.get_rx_distance>` for spec of input and result values. This is an optimized version specific to planar surface that doesn't make use of the mesh. """ return geodetic.distance_to_arc(self.corner_lons[0], self.corner_lats[0], self.strike, mesh.lons, mesh.lats)
def get_rx_distance(self, mesh): """ See :meth:`superclass method <.base.BaseSurface.get_rx_distance>` for spec of input and result values. Base class calls :func:`openquake.hazardlib.geo.geodetic.distance_to_arc`. """ top_edge_centroid = self._get_top_edge_centroid() return geodetic.distance_to_arc(top_edge_centroid.longitude, top_edge_centroid.latitude, self.get_strike(), mesh.lons, mesh.lats)
def get_rx_distance(self, mesh): """ See :meth:`superclass method <.base.BaseSurface.get_rx_distance>` for spec of input and result values. Base class calls :func:`openquake.hazardlib.geo.geodetic.distance_to_arc`. """ top_edge_centroid = self._get_top_edge_centroid() return geodetic.distance_to_arc( top_edge_centroid.longitude, top_edge_centroid.latitude, self.get_strike(), mesh.lons, mesh.lats )
def test_one_point(self): dist = float(geodetic.distance_to_semi_arc( 12.3, 44.5, 39.4, plons=13.4, plats=46.9)) self.assertAlmostEqual(dist, -105.12464364) dist = float(geodetic.distance_to_arc( 12.3, 44.5, 219.4, plons=13.4, plats=46.9)) self.assertAlmostEqual(dist, +105.12464364) dist = float(geodetic.distance_to_semi_arc( 12.3, 44.5, 39.4, plons=13.4, plats=44.9)) self.assertAlmostEqual(dist, 38.34459954) # This tests the distance to a point in the y-negative halfspace in a # reference system which uses as the origin the reference point # (i.e. (12.3; 44.5)) and the y direction as the direction with # azimuth = 39.4) dist = float(geodetic.distance_to_semi_arc( 12.3, 44.5, 39.4, plons=11.3, plats=44.5)) self.assertAlmostEqual(dist, -79.3093368)
def get_rx_distance(self, mesh): """ Compute distance between each point of mesh and surface's great circle arc. Distance is measured perpendicular to the rupture strike, from the surface projection of the updip edge of the rupture, with the down dip direction being positive (this distance is usually called ``Rx``). In other words, is the horizontal distance to top edge of rupture measured perpendicular to the strike. Values on the hanging wall are positive, values on the footwall are negative. :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Rx-distance to. :returns: Numpy array of distances in km. """ top_edge = self.mesh[0:1] dists = [] ia = 0 ib = top_edge.lons.shape[1] - 2 if (self.__class__.__name__ == 'KiteSurface'): idxs = numpy.nonzero(numpy.isfinite(top_edge.lons[0, :]))[0] ia = min(idxs) ib = sorted(idxs)[-2] if top_edge.lons.shape[1] < 3: i = 0 if ((self.__class__.__name__ == 'KiteSurface') and (numpy.isnan(top_edge.lons[0, i]) or numpy.isnan(top_edge.lons[0, i + 1]))): msg = 'Rx calculation. Top of rupture has less than two points' raise ValueError(msg) p1 = Point(top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i]) p2 = Point(top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1]) azimuth = p1.azimuth(p2) dists.append( geodetic.distance_to_arc(p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats)) else: for i in range(top_edge.lons.shape[1] - 1): if ((self.__class__.__name__ == 'KiteSurface') and (numpy.isnan(top_edge.lons[0, i]) or numpy.isnan(top_edge.lons[0, i + 1]))): continue p1 = Point(top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i]) p2 = Point(top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1]) # Swapping if i == 0: pt = p1 p1 = p2 p2 = pt # Computing azimuth and distance if i == ia or i == ib: azimuth = p1.azimuth(p2) tmp = geodetic.distance_to_semi_arc( p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats) else: tmp = geodetic.min_distance_to_segment( numpy.array([p1.longitude, p2.longitude]), numpy.array([p1.latitude, p2.latitude]), mesh.lons, mesh.lats) # Correcting the sign of the distance if i == 0: tmp *= -1 dists.append(tmp) # Computing distances dists = numpy.array(dists) iii = abs(dists).argmin(axis=0) dst = dists[iii, list(range(dists.shape[1]))] if numpy.any(numpy.isnan(dst)): raise ValueError('NaN in Rx') return dst
def get_rx_distance(self, mesh): """ Compute distance between each point of mesh and surface's great circle arc. Distance is measured perpendicular to the rupture strike, from the surface projection of the updip edge of the rupture, with the down dip direction being positive (this distance is usually called ``Rx``). In other words, is the horizontal distance to top edge of rupture measured perpendicular to the strike. Values on the hanging wall are positive, values on the footwall are negative. :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Rx-distance to. :returns: Numpy array of distances in km. """ top_edge = self.mesh[0:1] dists = [] if top_edge.lons.shape[1] < 3: i = 0 p1 = Point( top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i] ) p2 = Point( top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1] ) azimuth = p1.azimuth(p2) dists.append( geodetic.distance_to_arc( p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats ) ) else: for i in range(top_edge.lons.shape[1] - 1): p1 = Point( top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i] ) p2 = Point( top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1] ) # Swapping if i == 0: pt = p1 p1 = p2 p2 = pt # Computing azimuth and distance if i == 0 or i == top_edge.lons.shape[1] - 2: azimuth = p1.azimuth(p2) tmp = geodetic.distance_to_semi_arc(p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats) else: tmp = geodetic.min_distance_to_segment( numpy.array([p1.longitude, p2.longitude]), numpy.array([p1.latitude, p2.latitude]), mesh.lons, mesh.lats) # Correcting the sign of the distance if i == 0: tmp *= -1 dists.append(tmp) # Computing distances dists = numpy.array(dists) iii = abs(dists).argmin(axis=0) dst = dists[iii, list(range(dists.shape[1]))] return dst
def get_rx_distance(self, mesh): """ See :meth:`superclass method <.base.BaseSurface.get_rx_distance>` for spec of input and result values. The method extracts the top edge of the surface. For each point in mesh it computes the Rx distance to each segment the top edge is made of. The calculation is done by calling the function :func:`openquake.hazardlib.geo.geodetic.distance_to_arc`. The final Rx distance matrix is then constructed by taking, for each point in mesh, the minimum Rx distance value computed. """ top_edge = self.get_mesh()[0:1] dists = [] if top_edge.lons.shape[1] < 3: i = 0 p1 = Point( top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i] ) p2 = Point( top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1] ) azimuth = p1.azimuth(p2) dists.append( geodetic.distance_to_arc( p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats ) ) else: for i in range(top_edge.lons.shape[1] - 1): p1 = Point( top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i] ) p2 = Point( top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1] ) # Swapping if i == 0: pt = p1 p1 = p2 p2 = pt # Computing azimuth and distance if i == 0 or i == top_edge.lons.shape[1] - 2: azimuth = p1.azimuth(p2) tmp = geodetic.distance_to_semi_arc(p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats) else: tmp = geodetic.min_distance_to_segment([p1.longitude, p2.longitude], [p1.latitude, p2.latitude], mesh.lons, mesh.lats) # Correcting the sign of the distance if i == 0: tmp *= -1 dists.append(tmp) # Computing distances dists = numpy.array(dists) iii = abs(dists).argmin(axis=0) dst = dists[iii, list(range(dists.shape[1]))] return dst
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)
def get_rx_distance(self, mesh): """ Compute distance between each point of mesh and surface's great circle arc. Distance is measured perpendicular to the rupture strike, from the surface projection of the updip edge of the rupture, with the down dip direction being positive (this distance is usually called ``Rx``). In other words, is the horizontal distance to top edge of rupture measured perpendicular to the strike. Values on the hanging wall are positive, values on the footwall are negative. :param mesh: :class:`~openquake.hazardlib.geo.mesh.Mesh` of points to calculate Rx-distance to. :returns: Numpy array of distances in km. """ top_edge = self.mesh[0:1] dists = [] if top_edge.lons.shape[1] < 3: i = 0 p1 = Point(top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i]) p2 = Point(top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1]) azimuth = p1.azimuth(p2) dists.append( geodetic.distance_to_arc(p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats)) else: for i in range(top_edge.lons.shape[1] - 1): p1 = Point(top_edge.lons[0, i], top_edge.lats[0, i], top_edge.depths[0, i]) p2 = Point(top_edge.lons[0, i + 1], top_edge.lats[0, i + 1], top_edge.depths[0, i + 1]) # Swapping if i == 0: pt = p1 p1 = p2 p2 = pt # Computing azimuth and distance if i == 0 or i == top_edge.lons.shape[1] - 2: azimuth = p1.azimuth(p2) tmp = geodetic.distance_to_semi_arc( p1.longitude, p1.latitude, azimuth, mesh.lons, mesh.lats) else: tmp = geodetic.min_distance_to_segment( numpy.array([p1.longitude, p2.longitude]), numpy.array([p1.latitude, p2.latitude]), mesh.lons, mesh.lats) # Correcting the sign of the distance if i == 0: tmp *= -1 dists.append(tmp) # Computing distances dists = numpy.array(dists) iii = abs(dists).argmin(axis=0) dst = dists[iii, list(range(dists.shape[1]))] return dst
def test_several_points(self): plons = numpy.array([3.3, 4.3]) plats = numpy.array([20.3, 15.3]) dists = geodetic.distance_to_arc(4.0, 17.0, -123.0, plons, plats) expected_dists = [347.61490787, -176.03785187] self.assertTrue(numpy.allclose(dists, expected_dists))
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)