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
def test(self): lon, lat = geodetic.point_at(10.0, 20.0, 30.0, 50.0) self.assertAlmostEqual(lon, 10.239856504796101, places=6) self.assertAlmostEqual(lat, 20.38925590463351, places=6) lon, lat = geodetic.point_at(-13.5, 22.4, -140.0, 120.0) self.assertAlmostEqual(lon, -14.245910669126582, places=6) self.assertAlmostEqual(lat, 21.57159463157223, places=6)
def sample_aftershock_coords(mainshock, n_aftershocks, min_width=5, min_depth=4, max_depth=20.): main_midpt_lat = mainshock.surface.get_middle_point().latitude main_midpt_lon = mainshock.surface.get_middle_point().longitude width = mainshock.surface.get_width() length = mainshock.surface.get_area() / width surface_width = width * np.sin(np.radians(mainshock.surface.get_dip())) if surface_width < min_width: surface_width = min_width along_strike_distance = length * inverse_transform_sample( aft_axis_distance, aft_distance_probs, n_aftershocks) strike_perp_distance = width * inverse_transform_sample( aft_axis_distance, aft_distance_probs, n_aftershocks) aftershock_dists = np.sqrt(along_strike_distance**2 + strike_perp_distance**2) aftershock_angle_from_strike = np.degrees( np.arctan2(strike_perp_distance, along_strike_distance)) aftershock_az = (aftershock_angle_from_strike + mainshock.surface.get_strike()) aftershock_lons, aftershock_lats = point_at(main_midpt_lon, main_midpt_lat, aftershock_az, aftershock_dists) aftershock_depths = np.random.uniform(min_depth, max_depth, n_aftershocks) return aftershock_lons, aftershock_lats, aftershock_depths
def point_at(self, horizontal_distance, vertical_increment, azimuth): """ Compute the point with given horizontal, vertical distances and azimuth from this point. :param horizontal_distance: Horizontal distance, in km. :type horizontal_distance: float :param vertical_increment: Vertical increment, in km. When positive, the new point has a greater depth. When negative, the new point has a smaller depth. :type vertical_increment: float :type azimuth: Azimuth, in decimal degrees. :type azimuth: float :returns: The point at the given distances. :rtype: Instance of :class:`Point` """ lon, lat = geodetic.point_at(self.longitude, self.latitude, azimuth, horizontal_distance) return Point(lon, lat, self.depth + vertical_increment)
def _set_vertexes(self): self.plo.append(self.olo) self.pla.append(self.ola) for lngh, strk in zip(self.length, self.strike): tlo, tla = point_at(self.plo[-1], self.pla[-1], strk, lngh) self.plo.append(tlo) self.pla.append(tla)
def translate(self, p1, p2): """ Translate the surface for a specific distance along a specific azimuth direction. Parameters are two points (instances of :class:`openquake.hazardlib.geo.point.Point`) representing the direction and an azimuth for translation. The resulting surface corner points will be that far along that azimuth from respective corner points of this surface as ``p2`` is located with respect to ``p1``. :returns: A new :class:`PlanarSurface` object with the same mesh spacing, dip, strike, width, length and depth but with corners longitudes and latitudes translated. """ azimuth = geodetic.azimuth(p1.longitude, p1.latitude, p2.longitude, p2.latitude) distance = geodetic.geodetic_distance(p1.longitude, p1.latitude, p2.longitude, p2.latitude) # avoid calling PlanarSurface's constructor nsurf = object.__new__(PlanarSurface) # but do call BaseQuadrilateralSurface's one BaseQuadrilateralSurface.__init__(nsurf) nsurf.mesh_spacing = self.mesh_spacing nsurf.dip = self.dip nsurf.strike = self.strike nsurf.corner_lons, nsurf.corner_lats = geodetic.point_at( self.corner_lons, self.corner_lats, azimuth, distance ) nsurf.corner_depths = self.corner_depths.copy() nsurf._init_plane() nsurf.width = self.width nsurf.length = self.length return nsurf
def translate(self, p1, p2): """ Translate the surface for a specific distance along a specific azimuth direction. Parameters are two points (instances of :class:`openquake.hazardlib.geo.point.Point`) representing the direction and an azimuth for translation. The resulting surface corner points will be that far along that azimuth from respective corner points of this surface as ``p2`` is located with respect to ``p1``. :returns: A new :class:`PlanarSurface` object with the same mesh spacing, dip, strike, width, length and depth but with corners longitudes and latitudes translated. """ azimuth = geodetic.azimuth(p1.longitude, p1.latitude, p2.longitude, p2.latitude) distance = geodetic.geodetic_distance(p1.longitude, p1.latitude, p2.longitude, p2.latitude) # avoid calling PlanarSurface's constructor nsurf = object.__new__(PlanarSurface) nsurf.dip = self.dip nsurf.strike = self.strike nsurf.corner_lons, nsurf.corner_lats = geodetic.point_at( self.corner_lons, self.corner_lats, azimuth, distance) nsurf.corner_depths = self.corner_depths.copy() nsurf._init_plane() nsurf.width = self.width nsurf.length = self.length return nsurf
def _create_rupture(distance, magnitude, tectonic_region_type=TRT.ACTIVE_SHALLOW_CRUST): # Return a rupture with a fixed geometry located at a given r_jb distance # from a site located at (0.0, 0.0). # parameter float distance: # Joyner and Boore rupture-site distance # parameter float magnitude: # Rupture magnitude # Find the point at a given distance lonp, latp = point_at(0.0, 0.0, 90., distance) mag = magnitude rake = 0.0 tectonic_region_type = tectonic_region_type hypocenter = Point(lonp, latp, 2.5) surface = PlanarSurface.from_corner_points( Point(lonp, -1, 0.), Point(lonp, +1, 0.), Point(lonp, +1, 5.), Point(lonp, -1, 5.)) surface = SimpleFaultSurface.from_fault_data( fault_trace=Line([Point(lonp, -1), Point(lonp, 1)]), upper_seismogenic_depth=0.0, lower_seismogenic_depth=5.0, dip=90.0, mesh_spacing=1.0) # check effective rupture-site distance from openquake.hazardlib.geo.mesh import Mesh mesh = Mesh(numpy.array([0.0]), numpy.array([0.0])) assert abs(surface.get_joyner_boore_distance(mesh)-distance) < 1e-2 return BaseRupture(mag, rake, tectonic_region_type, hypocenter, surface, NonParametricSeismicSource)
def discretize(self, mesh_spacing): """ Get a mesh of uniformly spaced points inside the polygon area with distance of ``mesh_spacing`` km between. :returns: An instance of :class:`~openquake.hazardlib.geo.mesh.Mesh` that holds the points data. Mesh is created with no depth information (all the points are on the Earth surface). """ self._init_polygon2d() west, east, north, south = self._bbox lons = [] lats = [] # we cover the bounding box (in spherical coordinates) from highest # to lowest latitude and from left to right by longitude. we step # by mesh spacing distance (linear measure). we check each point # if it is inside the polygon and yield the point object, if so. # this way we produce an uniformly-spaced mesh regardless of the # latitude. latitude = north while latitude > south: longitude = west while utils.get_longitudinal_extent(longitude, east) > 0: # we use Cartesian space just for checking if a point # is inside of the polygon. x, y = self._projection(longitude, latitude) if self._polygon2d.contains(shapely.geometry.Point(x, y)): lons.append(longitude) lats.append(latitude) # move by mesh spacing along parallel... longitude, _, = geodetic.point_at(longitude, latitude, 90, mesh_spacing) # ... and by the same distance along meridian in outer one _, latitude = geodetic.point_at(west, latitude, 180, mesh_spacing) lons = numpy.array(lons) lats = numpy.array(lats) return Mesh(lons, lats, depths=None)
def get_middle_point(lon1, lat1, lon2, lat2): """ Given two points return the point exactly in the middle lying on the same great circle arc. Parameters are point coordinates in degrees. :returns: Tuple of longitude and latitude of the point in the middle. """ if lon1 == lon2 and lat1 == lat2: return lon1, lat1 dist = geodetic.geodetic_distance(lon1, lat1, lon2, lat2) azimuth = geodetic.azimuth(lon1, lat1, lon2, lat2) return geodetic.point_at(lon1, lat1, azimuth, dist / 2.0)
def __call__(self, event): if isinstance(event, KeyEvent): if event.key is 'd': print('----------------------') self.xs = [] self.ys = [] self.xp = [] self.yp = [] self.line.set_data(self.xp, self.yp) self.point.set_data(self.xs, self.ys) self.line.figure.canvas.draw() self.point.figure.canvas.draw() self.data = [] elif event.key is 'f': dat = numpy.array(self.data) fname = './cs_%s.csv' % (self.csec.ids) numpy.savetxt(fname, dat) print('Section data saved to: %s' % (fname)) else: pass else: olo = self.csec.olo ola = self.csec.ola assert len(self.csec.strike) == 1 if event.xdata is not None: strike = self.csec.strike[0] nlo, nla = point_at(olo, ola, strike, event.xdata) cnt = len(self.xs)+1 print('%03d, %+7.4f, %+7.4f, %6.2f' % (cnt, nlo, nla, event.ydata)) if event.inaxes != self.line.axes: return self.xp.append(event.xdata) self.yp.append(event.ydata) self.xs.append(event.xdata) self.ys.append(event.ydata) self.data.append([nlo, nla, event.ydata]) self.point.set_data(self.xs, self.ys) self.line.set_data(self.xs, self.ys) self.line.figure.canvas.draw() self.point.figure.canvas.draw()
def from_hypocenter(cls, hypoc, msr, mag, aratio, strike, dip, rake): """ Create and return a planar surface given the hypocenter location and other rupture properties. :param hypoc: An instance of :class: `openquake.hazardlib.geo.point.Point` :param msr: The magnitude scaling relationship e.g. an instance of :class: `openquake.hazardlib.scalerel.WC1994` :param mag: The magnitude :param aratio: The rupture aspect ratio :param strike: The rupture strike :param dip: The rupture dip :param rake: The rupture rake """ lon = hypoc.longitude lat = hypoc.latitude depth = hypoc.depth area = msr.get_median_area(mag, rake) width = (area / aratio)**0.5 length = width * aratio height = width * numpy.sin(numpy.radians(dip)) hdist = width * numpy.cos(numpy.radians(dip)) # Move hor. 1/2 hdist in direction -90 mid_top = point_at(lon, lat, strike - 90, hdist / 2) # Move hor. 1/2 hdist in direction +90 mid_bot = point_at(lon, lat, strike + 90, hdist / 2) # compute corner points at the surface top_right = point_at(mid_top[0], mid_top[1], strike, length / 2) top_left = point_at(mid_top[0], mid_top[1], strike + 180, length / 2) bot_right = point_at(mid_bot[0], mid_bot[1], strike, length / 2) bot_left = point_at(mid_bot[0], mid_bot[1], strike + 180, length / 2) # compute corner points in 3D pbl = Point(bot_left[0], bot_left[1], depth + height / 2) pbr = Point(bot_right[0], bot_right[1], depth + height / 2) hei = depth - height / 2 ptl = Point(top_left[0], top_left[1], hei) ptr = Point(top_right[0], top_right[1], hei) self = cls(strike, dip, ptl, ptr, pbr, pbl) return self
def get_polygon_from_simple_fault(flt): """ """ xtrace = [] ytrace = [] if isinstance(flt, SimpleFaultSource): trc = flt.fault_trace elif isinstance(flt, OQtSource): trc = flt.trace for pnt in trc: xtrace.append(pnt.longitude) ytrace.append(pnt.latitude) # # Get strike direction azim = azimuth(xtrace[0], ytrace[0], xtrace[-1], ytrace[-1]) # # Compute the dip direction dip = flt.dip dip_dir = (azim + 90) % 360 seism_thickness = flt.lower_seismogenic_depth - flt.upper_seismogenic_depth # # Horizontal distance h_dist = seism_thickness / scipy.tan(scipy.radians(dip)) # # Compute the bottom trace xb = xtrace yb = ytrace for x, y in zip(xtrace[::-1], ytrace[::-1]): nx, ny = point_at(x, y, dip_dir, h_dist) xb.append(nx) yb.append(ny) # Create the polygon geometry pnt_list = [] for x, y in zip(xb, yb): pnt_list.append((x, y)) return pnt_list
def test01(self): tmp_fname = 'trash' cs_length = 400 cs_depth = 100 intd = 100 handle, tmp_fname = tempfile.mkstemp() print(tmp_fname) get_cs(self.trench, 'tmp.txt', cs_length, cs_depth, intd, 0, tmp_fname) if PLOT: _ = plt.figure() columns = ['lon', 'lat', 'dep', 'len', 'azim', 'id', 'fname'] df = pd.read_csv(tmp_fname, names=columns, delimiter=' ') for i, row in df.iterrows(): ex, ey = point_at(row.lon, row.lat, row.azim, row.len) plt.plot([row.lon], [row.lat], 'o') plt.text(row.lon, row.lat, '{:d}'.format(row.id)) plt.plot([row.lon, ex], [row.lat, ey], '-') plt.show() expected = os.path.join(BASE_PATH, 'data', 'traces', 'expected.txt') msg = 'The two files do not match' self.assertTrue(filecmp.cmp(tmp_fname, expected), msg)
def make_aftershock_surface(aft_d, scale_relationship=WC1994, mesh_spacing=2.): rupture_area = scale_relationship().get_median_area( aft_d['Mw'], aft_d['rake']) length = (rupture_area * aft_d['aspect_ratio'])**0.5 width = length / aft_d['aspect_ratio'] mid_upper_lon, mid_upper_lat = point_at( aft_d['lon'], aft_d['lat'], aft_d['strike'] - 90, width * np.sin(np.radians(aft_d['dip']))) mid_lower_lon, mid_lower_lat = point_at( aft_d['lon'], aft_d['lat'], aft_d['strike'] + 90, width * np.sin(np.radians(aft_d['dip']))) ul_lon, ul_lat = point_at(mid_upper_lon, mid_upper_lat, aft_d['strike'] - 180, length / 2) ur_lon, ur_lat = point_at(mid_upper_lon, mid_upper_lat, aft_d['strike'], length / 2) ll_lon, ll_lat = point_at(mid_upper_lon, mid_upper_lat, aft_d['strike'] - 180, length / 2) lr_lon, lr_lat = point_at(mid_upper_lon, mid_upper_lat, aft_d['strike'], length / 2) upper_surface_depth = (aft_d['depth'] - width * np.cos(np.radians(aft_d['dip']))) if upper_surface_depth < 0: upper_surface_depth = 0. lower_surface_depth = (aft_d['depth'] + width * np.cos(np.radians(aft_d['dip']))) ul_corner = Point(ul_lon, ul_lat, upper_surface_depth) ll_corner = Point(ll_lon, ll_lat, lower_surface_depth) ur_corner = Point(ur_lon, ur_lat, upper_surface_depth) lr_corner = Point(lr_lon, lr_lat, lower_surface_depth) return PlanarSurface.from_corner_points(mesh_spacing, ul_corner, ur_corner, lr_corner, ll_corner)
def createGeoJSON(self): """ Create the GeoJSON for the segment grid cells and earthquake point. Returns: dictionary: GeoJSON formatted dictionary. """ segment_cells = [] slips = np.asarray([]) for segment in self.segments: slips = np.append(slips, segment['slip'].flatten()) max_slip = np.ceil(np.max(slips)) COLORS.vmax = max_slip for num in range(self.getNumSegments()): # Get segment segment = self.getSegment(num) arr_size = len(segment['lat'].flatten()) dx = [self.event['dx'] / 2] * arr_size dy = [self.event['dz'] / 2] * arr_size length = [self.event['dx']] * arr_size width = [self.event['dz']] * arr_size strike = [segment['strike']] * arr_size dip = [segment['dip']] * arr_size optional_properties = copy.deepcopy(segment) for key in [ 'dip', 'strike', 'lon', 'depth', 'slip', 'lat', 'length', 'width' ]: del optional_properties[key] for key in optional_properties: optional_properties[key] = optional_properties[key].flatten() px = segment['lon'].flatten() py = segment['lat'].flatten() pz = segment['depth'].flatten() slips = segment['slip'].flatten() # Verify that all are numpy arrays px = np.array(px, dtype='d') py = np.array(py, dtype='d') # depth should be in meters not in km pz = np.array(pz, dtype='d') * 1000 dx = np.array(dx, dtype='d') dy = np.array(dy, dtype='d') length = np.array(length, dtype='d') width = np.array(width, dtype='d') strike = np.array(strike, dtype='d') dip = np.array(dip, dtype='d') # Get P1 and P2 (top horizontal points) theta = np.rad2deg(np.arctan((dy * np.cos(np.deg2rad(dip))) / dx)) P1_direction = strike + 180 + theta P1_distance = np.sqrt(dx**2 + (dy * np.cos(np.deg2rad(dip)))**2) P2_direction = strike P2_distance = length P1_lon = np.asarray([]) P1_lat = np.asarray([]) P2_lon = np.asarray([]) P2_lat = np.asarray([]) for idx, value in enumerate(px): P1_points = point_at(px[idx], py[idx], P1_direction[idx], P1_distance[idx]) P1_lon = np.append(P1_lon, P1_points[0]) P1_lat = np.append(P1_lat, P1_points[1]) P2_points = point_at(P1_points[0], P1_points[1], P2_direction[idx], P2_distance[idx]) P2_lon = np.append(P2_lon, P2_points[0]) P2_lat = np.append(P2_lat, P2_points[1]) # Get top depth top_horizontal_depth = pz - 1000 * np.abs( dy * np.sin(np.deg2rad(dip))) group_index = np.array(range(len(P1_lon))) # Convert dip to radians dip = np.radians(dip) # Get a projection object west = np.min((P1_lon.min(), P2_lon.min())) east = np.max((P1_lon.max(), P2_lon.max())) south = np.min((P1_lat.min(), P2_lat.min())) north = np.max((P1_lat.max(), P2_lat.max())) # Projected coordinates are in km proj = OrthographicProjection(west, east, north, south) xp2 = np.zeros_like(P1_lon) xp3 = np.zeros_like(P1_lon) yp2 = np.zeros_like(P1_lon) yp3 = np.zeros_like(P1_lon) zpdown = np.zeros_like(top_horizontal_depth) for i, p1lon in enumerate(P1_lon): # Project the top edge coordinates p0x, p0y = proj(p1lon, P1_lat[i]) p1x, p1y = proj(P2_lon[i], P2_lat[i]) # Get the rotation angle defined by these two points if strike is None: dx = p1x - p0x dy = p1y - p0y theta = np.arctan2(dx, dy) # theta is angle from north elif len(strike) == 1: theta = np.radians(strike[0]) else: theta = np.radians(strike[i]) R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) # Rotate the top edge points into a new coordinate system (vertical # line) p0 = np.array([p0x, p0y]) p1 = np.array([p1x, p1y]) p0p = np.dot(R, p0) p1p = np.dot(R, p1) # Get right side coordinates in project, rotated system dz = np.sin(dip[i]) * width[i] * 1000 dx = np.cos(dip[i]) * width[i] p3xp = p0p[0] + dx p3yp = p0p[1] p2xp = p1p[0] + dx p2yp = p1p[1] # Get right side coordinates in un-rotated projected system p3p = np.array([p3xp, p3yp]) p2p = np.array([p2xp, p2yp]) Rback = np.array([[np.cos(-theta), -np.sin(-theta)], [np.sin(-theta), np.cos(-theta)]]) p3 = np.dot(Rback, p3p) p2 = np.dot(Rback, p2p) p3x = np.array([p3[0]]) p3y = np.array([p3[1]]) p2x = np.array([p2[0]]) p2y = np.array([p2[1]]) # project lower edge points back to lat/lon coordinates lon3, lat3 = proj(p3x, p3y, reverse=True) lon2, lat2 = proj(p2x, p2y, reverse=True) xp2[i] = lon2 xp3[i] = lon3 yp2[i] = lat2 yp3[i] = lat3 zpdown[i] = top_horizontal_depth[i] + dz # --------------------------------------------------------------------- # Create GeoJSON object # --------------------------------------------------------------------- u_groups = np.unique(group_index) n_groups = len(u_groups) polygons = [] for i in range(n_groups): ind = np.where(u_groups[i] == group_index)[0] lons = np.concatenate([ P1_lon[ind[0]].reshape((1, )), P2_lon[ind], xp2[ind][::-1], xp3[ind][::-1][-1].reshape((1, )), P1_lon[ind[0]].reshape( (1, )) ]) lats = np.concatenate([ P1_lat[ind[0]].reshape((1, )), P2_lat[ind], yp2[ind][::-1], yp3[ind][::-1][-1].reshape((1, )), P1_lat[ind[0]].reshape( (1, )) ]) deps = np.concatenate([ top_horizontal_depth[ind[0]].reshape( (1, )), top_horizontal_depth[ind], zpdown[ind][::-1], zpdown[ind][::-1][-1].reshape( (1, )), top_horizontal_depth[ind[0]].reshape((1, )) ]) poly = [] for lon, lat, dep in zip(lons, lats, deps): lon = np.around(lon, decimals=4) lat = np.around(lat, decimals=4) deps = np.around(deps, decimals=4) coordinates = np.around(np.asarray([lon, lat, dep]), decimals=5) poly.append(coordinates.tolist()) properties = {} for property in optional_properties: properties[property] = optional_properties[property][i] h = COLORS.getDataColor(slips[i], color_format='hex') properties["slip"] = slips[i] properties["fill"] = h properties["stroke-width"] = 1.5 properties["fill-opacity"] = 1 d = { "type": "Feature", "properties": properties, "geometry": { "type": "Polygon", "coordinates": [poly] } } polygons += [d] segment_cells += polygons features = { "type": "FeatureCollection", "metadata": { 'epicenter': { 'location': self.event['location'], 'date': self.event['date'].strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'depth': self.event['depth'], 'moment': self.event['moment'], 'mag': self.event['mag'], 'lon': self.event['lon'], 'lat': self.event['lat'] } }, "features": segment_cells } self.corners = features
def fromOrientation(cls, px, py, pz, dx, dy, length, width, strike, dip, origin): """ Create a QuadRupture instance from a known point, shape, and orientation. A point is defined as a set of latitude, longitude, and depth, which is located in the corner between the tail of the vector pointing in the strike direction and the dip direction (nearest to the surface). The shape is defined by length, width, dx, and dy. The length is the measurement of the quadrilateral in the direction of strike, and width is the measurement of quadrilateral in the direction of dip. Dx is the measurement on the plane in the strike direction between the known point and the corner between the tail of the vector pointing in the strike direction and the dip direction (nearest to the surface). Dy is the measurement on the plane in the dip direction between the known point and the corner between the tail of the vector pointing in the strike direction and the dip direction (nearest to the surface). The orientation is defined by azimuth and angle from horizontal, strike and dip respectively. For example in plane view: :: strike direction p1*------------------->>p2 * | dy | dip |--------o | direction | dx known point | Width V | V | p4----------------------p3 Length Args: px (array): Array or list of longitudes (floats) of the known point. py (array): Array or list of latitudes (floats) of the known point. pz (array): Array or list of depths (floats) of the known point. dx (array): Array or list of distances (floats), in the strike direction, between the known point and P1. dx must be less than length. dy (array): Array or list of distances (floats), in the dip direction, between the known point and P1. dy must be less than width. length (array): Array or list of widths (floats) of the plane in the strike direction. width (array): Array or list of widths (floats) of the plane in the dip direction. strike (array): Array or list of strike angles (floats). dip (array): Array or list of dip angles (floats). origin (Origin): Reference to a ShakeMap Origin object. Returns: QuadRupture instance. Raises: ShakeLibException: if the lengths of the points arrays are not all equal. """ # Verify that arrays are of equal length if len(px) == len(py) == len(pz) == len(dx) == len(dy) == len( length) == len(width) == len(strike) == len(dip): pass else: raise ShakeLibException( 'Number of px, py, pz, dx, dy, length, width, ' 'strike, dip points must be ' 'equal.') # Verify that all are numpy arrays px = np.array(px, dtype='d') py = np.array(py, dtype='d') pz = np.array(pz, dtype='d') dx = np.array(dx, dtype='d') dy = np.array(dy, dtype='d') length = np.array(length, dtype='d') width = np.array(width, dtype='d') strike = np.array(strike, dtype='d') dip = np.array(dip, dtype='d') # Get P1 and P2 (top horizontal points) theta = np.rad2deg(np.arctan2(dy * np.cos(np.deg2rad(dip)), dx)) P1_direction = strike + 180 + theta P1_distance = np.sqrt(dx**2 + (dy * np.cos(np.deg2rad(dip)))**2) P2_direction = strike P2_distance = length P1_lon = [] P1_lat = [] P2_lon = [] P2_lat = [] for idx, value in enumerate(px): P1_points = point_at(px[idx], py[idx], P1_direction[idx], P1_distance[idx]) P1_lon += [P1_points[0]] P1_lat += [P1_points[1]] P2_points = point_at(P1_points[0], P1_points[1], P2_direction[idx], P2_distance[idx]) P2_lon += [P2_points[0]] P2_lat += [P2_points[1]] # Get top depth top_horizontal_depth = pz - np.abs(dy * np.sin(np.deg2rad(dip))) # Get QuadRupture object quad = QuadRupture.fromTrace(P1_lon, P1_lat, P2_lon, P2_lat, top_horizontal_depth, width, dip, origin, strike=strike) return quad
def test_zero_distance(self): lon, lat = geodetic.point_at(1.3, -5.6, -35.0, 0) self.assertAlmostEqual(lon, 1.3) self.assertAlmostEqual(lat, -5.6)