def test_2d_mesh(self): mesh = Mesh(numpy.array([[0., 1.], [2., 3.]]), numpy.array([[0., 0.], [0., 0.]]), None) target_mesh = Mesh( numpy.array([[3., 4., 5.], [-6., -7., 8.], [9., 10., 11.]]), numpy.array([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]), None) self._test(mesh, target_mesh, expected_distance_indices=[3, 3, 3, 0, 0, 3, 3, 3, 3])
def __init__(self, sites): self.indices = None self.vs30 = numpy.zeros(len(sites)) self.vs30measured = numpy.zeros(len(sites), dtype=bool) self.z1pt0 = self.vs30.copy() self.z2pt5 = self.vs30.copy() lons = self.vs30.copy() lats = self.vs30.copy() for i in xrange(len(sites)): self.vs30[i] = sites[i].vs30 self.vs30measured[i] = sites[i].vs30measured self.z1pt0[i] = sites[i].z1pt0 self.z2pt5[i] = sites[i].z2pt5 lons[i] = sites[i].location.longitude lats[i] = sites[i].location.latitude self.mesh = Mesh(lons, lats, depths=None) # protect arrays from being accidentally changed. it is useful # because we pass these arrays directly to a GMPE through # a SiteContext object and if a GMPE is implemented poorly it could # modify the site values, thereby corrupting site and all the # subsequent calculation. note that this doesn't protect arrays from # being changed by calling itemset() for arr in (self.vs30, self.vs30measured, self.z1pt0, self.z2pt5, self.mesh.lons, self.mesh.lats): arr.flags.writeable = False
def test_many_points(self): lons = numpy.array([0.7, 0.6, 0.4, 0.6, 0.3, 0.9, 0.5, 0.4]) lats = numpy.array([0.8, 0.5, 0.2, 0.7, 0.2, 0.4, 0.9, 0.4]) mesh = Mesh(lons, lats, None) polygon = mesh.get_convex_hull() elons = [0.4, 0.3, 0.5, 0.7, 0.9] elats = [0.2, 0.2, 0.9, 0.8, 0.4] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def test(self): mesh = Mesh(numpy.array([0., 1., 2., 3.]), numpy.zeros(4), None) matrix = mesh.get_distance_matrix() aaae = numpy.testing.assert_array_almost_equal aaae(matrix[0], [[0, 111.2, 222.4, 333.6]], decimal=1) aaae(matrix[1], [[111.2, 0, 111.2, 222.4]], decimal=1) aaae(matrix[2], [[222.4, 111.2, 0, 111.2]], decimal=1) aaae(matrix[3], [[333.6, 222.4, 111.2, 0]], decimal=1) for i in xrange(4): for j in xrange(i, 4): self.assertEqual(matrix[i, j], matrix[j, i])
def filter(self, mask): """ Create a new collection with only a subset of sites from this one. :param mask: Numpy array of boolean values of the same length as this sites collection. ``True`` values should indicate that site with that index should be included into the filtered collection. :returns: A new :class:`SiteCollection` instance, unless all the values in ``mask`` are ``True``, in which case this site collection is returned, or if all the values in ``mask`` are ``False``, in which case method returns ``None``. New collection has data of only those sites that were marked for inclusion in mask. See also :meth:`expand`. """ assert len(mask) == len(self) if mask.all(): # all sites satisfy the filter, return # this collection unchanged return self if not mask.any(): # no sites pass the filter, return None return None col = object.__new__(SiteCollection) # extract indices of Trues from the mask [indices] = mask.nonzero() # take only needed values from this collection # to a new one col.vs30 = self.vs30.take(indices) col.vs30measured = self.vs30measured.take(indices) col.z1pt0 = self.z1pt0.take(indices) col.z2pt5 = self.z2pt5.take(indices) col.mesh = Mesh(self.mesh.lons.take(indices), self.mesh.lats.take(indices), depths=None) if self.indices is not None: # if this collection was already a subset of some other # collection (a result of :meth:`filter` itself) than mask's # indices represent values in a filtered collection, but # we need to keep track of original indices in the whole # (unfiltered) collection. here we save original indices # of sites in this double- (or more times) filtered # collection col.indices = self.indices.take(indices) else: col.indices = indices # do the same as in the constructor for arr in (col.vs30, col.vs30measured, col.z1pt0, col.z2pt5, col.mesh.lons, col.mesh.lats): arr.flags.writeable = False return col
def test_two_points(self): mesh = Mesh(numpy.array([-10., -11.]), numpy.array([-12., -13.]), None) polygon = mesh.get_convex_hull() self.assertIsInstance(polygon, Polygon) elons = [ -10.99996704, -11.0000323, -11.00003296, -10.00003295, -9.99996795, -9.99996705 ] elats = [ -13.00003147, -13.00003212, -12.99996853, -11.99996865, -11.99996776, -12.00003135 ] numpy.testing.assert_allclose(polygon.lons, elons) numpy.testing.assert_allclose(polygon.lats, elats)
def get_closest_points(self, mesh): """ See :meth:`superclass' method <nhlib.geo.surface.base.BaseSurface.get_closest_points>`. This is an optimized version specific to planar surface that doesn't make use of the mesh. """ dists, xx, yy = self._project(mesh.lons, mesh.lats, mesh.depths) mxx = xx.clip(0, self.length) myy = yy.clip(0, self.width) dists.fill(0) lons, lats, depths = self._project_back(dists, mxx, myy) return Mesh(lons, lats, depths)
def test_against_mesh_to_mesh(self): corners = [Point(2.6, 3.7, 20), Point(2.90102155, 3.99961567, 20), Point(3.2, 3.7, 75), Point(2.89905849, 3.40038407, 75)] surface = PlanarSurface(0.5, 45, 70, *corners) lons, lats = numpy.meshgrid(numpy.linspace(2.2, 3.6, 7), numpy.linspace(3.4, 4.2, 7)) sites = Mesh(lons, lats, depths=None) res1 = surface.get_closest_points(sites) res2 = super(PlanarSurface, surface).get_closest_points(sites) aae = numpy.testing.assert_almost_equal # precision up to ~1 km aae(res1.lons, res2.lons, decimal=2) aae(res1.lats, res2.lats, decimal=2) aae(res1.depths, res2.depths, decimal=0)
def surface_projection_from_fault_data(cls, edges): """ Get a surface projection of the complex fault surface. :param edges: A list of horizontal edges of the surface as instances of :class:`nhlib.geo.line.Line`. :returns: Instance of :class:`~nhlib.geo.polygon.Polygon` describing the surface projection of the complex fault. """ # collect lons and lats of all the vertices of all the edges lons, lats = numpy.array([[[point.longitude, point.latitude] for point in edge] for edge in edges], dtype=float).reshape((-1, 2)).transpose() return Mesh(lons, lats, depths=None).get_convex_hull()
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:`~nhlib.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 test1(self): lons, lats, depths = geo_utils.cartesian_to_spherical( numpy.array([[60, -10, -10], [60, -10, 10], [60, 10, 10], [60, 10, -10]], float) ) surface = PlanarSurface(10, 20, 30, *Mesh(lons, lats, depths)) aaae = numpy.testing.assert_array_almost_equal plons, plats, pdepths = geo_utils.cartesian_to_spherical( numpy.array([[60, -10, -10], [59, 0, 0], [70, -11, -10]], float) ) dists, xx, yy = surface._project(plons, plats, pdepths) aaae(xx, [0, 10, 0]) aaae(yy, [0, 10, -1]) aaae(dists, [0, 1, -10]) lons, lats, depths = surface._project_back(dists, xx, yy) aaae(lons, plons) aaae(lats, plats) aaae(depths, pdepths)
def surface_projection_from_fault_data(cls, fault_trace, upper_seismogenic_depth, lower_seismogenic_depth, dip): """ Get a surface projection of the simple fault surface. Parameters are the same as for :meth:`from_fault_data`, excluding mesh spacing. :returns: Instance of :class:`~nhlib.geo.polygon.Polygon` describing the surface projection of the simple fault with specified parameters. """ # similar to :meth:`from_fault_data`, we just don't resample edges dip_tan = math.tan(math.radians(dip)) hdist_top = upper_seismogenic_depth / dip_tan hdist_bottom = lower_seismogenic_depth / dip_tan strike = fault_trace[0].azimuth(fault_trace[-1]) azimuth = (strike + 90.0) % 360 # collect coordinates of vertices in both top and bottom edges lons = [] lats = [] for point in fault_trace.points: top_edge_point = point.point_at(hdist_top, 0, azimuth) bottom_edge_point = point.point_at(hdist_bottom, 0, azimuth) lons.append(top_edge_point.longitude) lats.append(top_edge_point.latitude) lons.append(bottom_edge_point.longitude) lats.append(bottom_edge_point.latitude) lons = numpy.array(lons, float) lats = numpy.array(lats, float) return Mesh(lons, lats, depths=None).get_convex_hull()
def _make_mesh(self, lons, lats, depths=None): mesh = Mesh(lons, lats, depths) self.assertIs(mesh.lons, lons) self.assertIs(mesh.lats, lats) self.assertIs(mesh.depths, depths) return mesh
def test_zeroes(self): mesh = Mesh(numpy.zeros(1000), numpy.zeros(1000), None) matrix = mesh.get_distance_matrix() self.assertIsInstance(matrix, numpy.matrix) self.assertEqual(matrix.shape, (1000, 1000)) self.assertTrue((matrix == 0).all())