예제 #1
0
    def test_projection_across_international_date_line(self):
        # tests that given a polygon crossing the internation date line,
        # projecting its coordinates from spherical to cartesian and then back
        # to spherical gives the original values
        west = 179.7800
        east = -179.8650
        north = 51.4320
        south = 50.8410
        proj = utils.OrthographicProjection(west, east, north, south)

        lons = numpy.array([179.8960, 179.9500, -179.9930, -179.9120,
                            -179.8650, -179.9380, 179.9130, 179.7800])
        lats = numpy.array([50.8410, 50.8430, 50.8490, 50.8540, 51.4160,
                            51.4150, 51.4270, 51.4320])
        xx, yy = proj(lons, lats)
        comp_lons, comp_lats = proj(xx, yy, reverse=True)
        numpy.testing.assert_allclose(lons, comp_lons)
        numpy.testing.assert_allclose(lats, comp_lats)

        west = 179.
        east = -179.
        north = 1
        south = -1
        proj = utils.OrthographicProjection(west, east, north, south)
        lons = numpy.array([179.0, -179.0])
        lats = numpy.array([-1, 1])
        xx, yy = proj(lons, lats)
        comp_lons, comp_lats = proj(xx, yy, reverse=True)
        numpy.testing.assert_allclose(lons, comp_lons)
        numpy.testing.assert_allclose(lats, comp_lats)
예제 #2
0
    def _get_cartesian_edge_set(self):
        """
        For the GC2 calculations a set of cartesian representations of the
        fault edges are needed. In this present case we use a common cartesian
        framework for all edges, as opposed to defining a separate orthographic
        projection per edge
        """
        # Get projection space for cartesian projection
        edge_sets = numpy.vstack(self.edge_set)
        west, east, north, south = utils.get_spherical_bounding_box(
            edge_sets[:, 0],
            edge_sets[:, 1])
        self.proj = utils.OrthographicProjection(west, east, north, south)

        for edges in self.edge_set:
            # Project edges into cartesian space
            px, py = self.proj(edges[:, 0], edges[:, 1])
            # Store the two end-points of the trace
            self.cartesian_endpoints.append(
                numpy.array([[px[0], py[0], edges[0, 2]],
                             [px[-1], py[-1], edges[-1, 2]]]))
            self.cartesian_edges.append(numpy.column_stack([px, py,
                                                            edges[:, 2]]))
            # Get surface length vector for the trace - easier in cartesian
            lengths = numpy.sqrt((px[:-1] - px[1:]) ** 2. +
                                 (py[:-1] - py[1:]) ** 2.)
            self.length_set.append(lengths)
            # Get cumulative surface length vector
            self.cum_length_set.append(
                numpy.hstack([0., numpy.cumsum(lengths)]))
        return edge_sets
예제 #3
0
    def to_polygon(self, radius):
        """
        Create a circular polygon with specified radius centered in the point.

        :param radius:
            Required radius of a new polygon, in km.
        :returns:
            Instance of :class:`~openquake.hazardlib.geo.polygon.Polygon` that
            approximates a circle around the point with specified radius.
        """
        assert radius > 0
        # avoid circular imports
        from openquake.hazardlib.geo.polygon import Polygon

        # get a projection that is centered in the point
        proj = geo_utils.OrthographicProjection(self.longitude, self.longitude,
                                                self.latitude, self.latitude)

        # create a shapely object from a projected point coordinates,
        # which are supposedly (0, 0)
        point = shapely.geometry.Point(*proj(self.longitude, self.latitude))

        # extend the point to a shapely polygon using buffer()
        # and create openquake.hazardlib.geo.polygon.Polygon object from it
        return Polygon._from_2d(point.buffer(radius), proj)
예제 #4
0
 def test_points_too_far(self):
     proj = utils.OrthographicProjection(180, 180, 45, 45)
     with self.assertRaises(ValueError) as ar:
         proj(90, -45)
     self.assertEqual(str(ar.exception),
                      'some points are too far from the projection '
                      'center lon=180.0 lat=45.0')
예제 #5
0
 def test_projection(self):
     # values verified against pyproj's implementation
     proj = utils.OrthographicProjection(10, 16, -2, 30)
     lons = numpy.array([10., 20., 30., 40.])
     lats = numpy.array([-1., -2., -3., -4.])
     xx, yy = proj(lons, lats)
     exx = [-309.89151465, 800.52541443, 1885.04014687, 2909.78079661]
     eyy = [-1650.93260348, -1747.79256663, -1797.62444771, -1802.28117183]
     aac(xx, exx, atol=0.01, rtol=0.005)
     aac(yy, eyy, atol=0.01, rtol=0.005)
예제 #6
0
 def test_projecting_back_and_forth(self):
     lon0, lat0 = -10.4, 20.3
     proj = utils.OrthographicProjection(lon0, lat0, lon0, lat0)
     lons = lon0 + (numpy.random.random((20, 10)) * 50 - 25)
     lats = lat0 + (numpy.random.random((20, 10)) * 50 - 25)
     xx, yy = proj(lons, lats, reverse=False)
     self.assertEqual(xx.shape, (20, 10))
     self.assertEqual(yy.shape, (20, 10))
     blons, blats = proj(xx, yy, reverse=True)
     aac(blons, lons)
     aac(blats, lats)
예제 #7
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
예제 #8
0
    def test(self):
        polygon2d = shapely.geometry.Polygon([(-12, 0), (0, 14.5), (17.1, 3),
                                              (18, 0), (16.5, -3), (0, -10)])
        proj = geo_utils.OrthographicProjection(0, 0, 0, 0)
        poly = polygon.Polygon._from_2d(polygon2d, proj)
        elons = [-0.10791866, 0., 0.1537842, 0.1618781, 0.14838825, 0.]
        elats = [0., 0.13040175, 0.02697965, 0., -0.02697965, -0.0899322]
        ebbox = [-0.10791866, 0.1618781, 0.13040175, -0.0899322]
        numpy.testing.assert_allclose(poly.lons, elons)
        numpy.testing.assert_allclose(poly.lats, elats)
        numpy.testing.assert_allclose(poly._bbox, ebbox)
        self.assertIs(poly._polygon2d, polygon2d)
        self.assertIs(poly._projection, proj)

        poly = polygon.Polygon._from_2d(poly._polygon2d, poly._projection)
        numpy.testing.assert_allclose(poly.lons, elons)
        numpy.testing.assert_allclose(poly.lats, elats)
        numpy.testing.assert_allclose(poly._bbox, ebbox)
        self.assertIs(poly._polygon2d, polygon2d)
        self.assertIs(poly._projection, proj)
예제 #9
0
    def _get_proj_convex_hull(self):
        """
        Create a projection centered in the center of this mesh and define
        a convex polygon in that projection, enveloping all the points
        of the mesh.

        :returns:
            Tuple of two items: projection function and shapely 2d polygon.
            Note that the result geometry can be line or point depending
            on number of points in the mesh and their arrangement.
        """
        # create a projection centered in the center of points collection
        proj = geo_utils.OrthographicProjection(
            *geo_utils.get_spherical_bounding_box(self.lons, self.lats))

        # project all the points and create a shapely multipoint object.
        # need to copy an array because otherwise shapely misinterprets it
        coords = numpy.transpose(proj(self.lons.flat, self.lats.flat)).copy()
        multipoint = shapely.geometry.MultiPoint(coords)
        # create a 2d polygon from a convex hull around that multipoint
        return proj, multipoint.convex_hull
예제 #10
0
    def _init_polygon2d(self):
        """
        Spherical bounding box, projection, and Cartesian polygon are all
        cached to prevent redundant computations.

        If any of them are `None`, recalculate all of them.
        """
        if (self._polygon2d is None or self._projection is None
                or self._bbox is None):
            # resample polygon line segments:
            lons, lats = get_resampled_coordinates(self.lons, self.lats)

            # find the bounding box of a polygon in spherical coordinates:
            self._bbox = utils.get_spherical_bounding_box(lons, lats)

            # create a projection that is centered in a polygon center:
            self._projection = utils.OrthographicProjection(*self._bbox)

            # project polygon vertices to the Cartesian space and create
            # a shapely polygon object:
            xx, yy = self._projection(lons, lats)
            self._polygon2d = shapely.geometry.Polygon(list(zip(xx, yy)))
예제 #11
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
예제 #12
0
    def _get_proj_enclosing_polygon(self):
        """
        See :meth:`Mesh._get_proj_enclosing_polygon`.

        :class:`RectangularMesh` contains an information about relative
        positions of points, so it allows to define the minimum polygon,
        containing the projection of the mesh, which doesn't necessarily
        have to be convex (in contrast to :class:`Mesh` implementation).

        :returns:
            Same structure as :meth:`Mesh._get_proj_convex_hull`.
        """
        if self.lons.size < 4:
            # the mesh doesn't contain even a single cell
            return self._get_proj_convex_hull()

        proj = geo_utils.OrthographicProjection(
            *geo_utils.get_spherical_bounding_box(self.lons, self.lats))
        if len(self.lons.shape) == 1:  # 1D mesh
            lons = self.lons.reshape(len(self.lons), 1)
            lats = self.lats.reshape(len(self.lats), 1)
        else:  # 2D mesh
            lons = self.lons.T
            lats = self.lats.T
        mesh2d = numpy.array(proj(lons, lats)).T
        lines = iter(mesh2d)
        # we iterate over horizontal stripes, keeping the "previous"
        # line of points. we keep it reversed, such that together
        # with the current line they define the sequence of points
        # around the stripe.
        prev_line = next(lines)[::-1]
        polygons = []
        for i, line in enumerate(lines):
            coords = numpy.concatenate((prev_line, line, prev_line[0:1]))
            # create the shapely polygon object from the stripe
            # coordinates and simplify it (remove redundant points,
            # if there are any lying on the straight line).
            stripe = shapely.geometry.LineString(coords) \
                                     .simplify(self.DIST_TOLERANCE) \
                                     .buffer(self.DIST_TOLERANCE, 2)
            polygons.append(shapely.geometry.Polygon(stripe.exterior))
            prev_line = line[::-1]
        try:
            # create a final polygon as the union of all the stripe ones
            polygon = shapely.ops.cascaded_union(polygons) \
                                 .simplify(self.DIST_TOLERANCE)
        except ValueError:
            # NOTE(larsbutler): In some rare cases, we've observed ValueErrors
            # ("No Shapely geometry can be created from null value") with very
            # specific sets of polygons such that there are two unique
            # and many duplicates of one.
            # This bug is very difficult to reproduce consistently (except on
            # specific platforms) so the work around here is to remove the
            # duplicate polygons. In fact, we only observed this error on our
            # CI/build machine. None of our dev environments or production
            # machines has encountered this error, at least consistently. >:(
            polygons = [
                shapely.wkt.loads(x)
                for x in list(set(p.wkt for p in polygons))
            ]
            polygon = shapely.ops.cascaded_union(polygons) \
                                 .simplify(self.DIST_TOLERANCE)
        return proj, polygon