Beispiel #1
0
 def test_LAX_to_JFK(self):
     dist = geodetic.geodetic_distance(*(LAX + JFK))
     self.assertAlmostEqual(dist,
                            0.623585 * geodetic.EARTH_RADIUS,
                            delta=0.1)
     dist2 = geodetic.geodetic_distance(*(JFK + LAX))
     self.assertAlmostEqual(dist, dist2)
Beispiel #2
0
 def test_one_to_many(self):
     dist = geodetic.geodetic_distance(0, 0, [-1, 1], [0, 0])
     self.assertTrue(numpy.allclose(dist, [111.195, 111.195]), str(dist))
     dist = geodetic.geodetic_distance(0, 0, [[-1, 0], [1, 0]],
                                       [[0, 0], [0, 0]])
     self.assertTrue(numpy.allclose(dist, [[111.195, 0], [111.195, 0]]),
                     str(dist))
Beispiel #3
0
 def test_one_to_many(self):
     dist = geodetic.geodetic_distance(0, 0, [-1, 1], [0, 0])
     self.assertTrue(numpy.allclose(dist, [111.195, 111.195]), str(dist))
     dist = geodetic.geodetic_distance(0, 0, [[-1, 0], [1, 0]],
                                             [[0, 0], [0, 0]])
     self.assertTrue(numpy.allclose(dist, [[111.195, 0], [111.195, 0]]),
                     str(dist))
Beispiel #4
0
    def get_distance_matrix(self):
        """
        Compute and return distances between each pairs of points in the mesh.

        This method requires that all the points lie on Earth surface (have
        zero depth) and coordinate arrays are one-dimensional.

        .. warning::
            Because of its quadratic space and time complexity this method
            is safe to use for meshes of up to several thousand points. For
            mesh of 10k points it needs ~800 Mb for just the resulting matrix
            and four times that much for intermediate storage.

        :returns:
            Two-dimensional numpy array, square matrix of distances. The matrix
            has zeros on main diagonal and positive distances in kilometers
            on all other cells. That is, value in cell (3, 5) is the distance
            between mesh's points 3 and 5 in km, and it is equal to value
            in cell (5, 3).

        Uses :func:`nhlib.geo.geodetic.geodetic_distance`.
        """
        assert self.lons.ndim == 1
        assert self.depths is None or (self.depths == 0).all()
        distances = geodetic.geodetic_distance(
            self.lons.reshape(self.lons.shape + (1, )),
            self.lats.reshape(self.lats.shape + (1, )), self.lons, self.lats)
        return numpy.matrix(distances, copy=False)
Beispiel #5
0
    def distance_to_mesh(self, mesh, with_depths=True):
        """
        Compute distance (in km) between this point and each point of ``mesh``.

        :param mesh:
            :class:`~nhlib.geo.mesh.Mesh` of points to calculate distance to.
        :param with_depths:
            If ``True`` (by default), distance is calculated between actual
            point and the mesh, geodetic distance of projections is combined
            with vertical distance (difference of depths). If this is set
            to ``False``, only geodetic distance between projections
            is calculated.
        :returns:
            Numpy array of floats of the same shape as ``mesh`` with distance
            values in km in respective indices.
        """
        if with_depths:
            if mesh.depths is None:
                mesh_depths = numpy.zeros_like(mesh.lons)
            else:
                mesh_depths = mesh.depths
            return geodetic.distance(self.longitude, self.latitude, self.depth,
                                     mesh.lons, mesh.lats, mesh_depths)
        else:
            return geodetic.geodetic_distance(self.longitude, self.latitude,
                                              mesh.lons, mesh.lats)
Beispiel #6
0
    def average_azimuth(self):
        """
        Calculate and return weighted average azimuth of all line's segments
        in decimal degrees.

        Uses formula from
        http://en.wikipedia.org/wiki/Mean_of_circular_quantities

        >>> from nhlib.geo.point import Point as P
        >>> str(Line([P(0, 0), P(1e-5, 1e-5)]).average_azimuth())
        '45.0'
        >>> str(Line([P(0, 0), P(0, 1e-5), P(1e-5, 1e-5)]).average_azimuth())
        '45.0'
        >>> line = Line([P(0, 0), P(-2e-5, 0), P(-2e-5, 1.154e-5)])
        >>> '%.1f' % line.average_azimuth()
        '300.0'
        """
        if len(self.points) == 2:
            return self.points[0].azimuth(self.points[1])
        lons = numpy.array([point.longitude for point in self.points])
        lats = numpy.array([point.latitude for point in self.points])
        azimuths = geodetic.azimuth(lons[:-1], lats[:-1], lons[1:], lats[1:])
        distances = geodetic.geodetic_distance(lons[:-1], lats[:-1],
                                               lons[1:], lats[1:])
        azimuths = numpy.radians(azimuths)
        # convert polar coordinates to Cartesian ones and calculate
        # the average coordinate of each component
        avg_x = numpy.mean(distances * numpy.sin(azimuths))
        avg_y = numpy.mean(distances * numpy.cos(azimuths))
        # find the mean azimuth from that mean vector
        azimuth = numpy.degrees(numpy.arctan2(avg_x, avg_y))
        if azimuth < 0:
            azimuth += 360
        return azimuth
Beispiel #7
0
    def distance_to_mesh(self, mesh, with_depths=True):
        """
        Compute distance (in km) between this point and each point of ``mesh``.

        :param mesh:
            :class:`~nhlib.geo.mesh.Mesh` of points to calculate distance to.
        :param with_depths:
            If ``True`` (by default), distance is calculated between actual
            point and the mesh, geodetic distance of projections is combined
            with vertical distance (difference of depths). If this is set
            to ``False``, only geodetic distance between projections
            is calculated.
        :returns:
            Numpy array of floats of the same shape as ``mesh`` with distance
            values in km in respective indices.
        """
        if with_depths:
            if mesh.depths is None:
                mesh_depths = numpy.zeros_like(mesh.lons)
            else:
                mesh_depths = mesh.depths
            return geodetic.distance(self.longitude, self.latitude, self.depth,
                                     mesh.lons, mesh.lats, mesh_depths)
        else:
            return geodetic.geodetic_distance(self.longitude, self.latitude,
                                              mesh.lons, mesh.lats)
Beispiel #8
0
    def translate(self, p1, p2):
        """
        Translate the surface for a specific distance along a specific azimuth
        direction.

        Parameters are two points (instances of :class:`nhlib.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 BaseSurface's one
        BaseSurface.__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
Beispiel #9
0
    def average_azimuth(self):
        """
        Calculate and return weighted average azimuth of all line's segments
        in decimal degrees.

        Uses formula from
        http://en.wikipedia.org/wiki/Mean_of_circular_quantities

        >>> from nhlib.geo.point import Point as P
        >>> str(Line([P(0, 0), P(1e-5, 1e-5)]).average_azimuth())
        '45.0'
        >>> str(Line([P(0, 0), P(0, 1e-5), P(1e-5, 1e-5)]).average_azimuth())
        '45.0'
        >>> line = Line([P(0, 0), P(-2e-5, 0), P(-2e-5, 1.154e-5)])
        >>> '%.1f' % line.average_azimuth()
        '300.0'
        """
        if len(self.points) == 2:
            return self.points[0].azimuth(self.points[1])
        lons = numpy.array([point.longitude for point in self.points])
        lats = numpy.array([point.latitude for point in self.points])
        azimuths = geodetic.azimuth(lons[:-1], lats[:-1], lons[1:], lats[1:])
        distances = geodetic.geodetic_distance(lons[:-1], lats[:-1],
                                               lons[1:], lats[1:])
        azimuths = numpy.radians(azimuths)
        # convert polar coordinates to Cartesian ones and calculate
        # the average coordinate of each component
        avg_x = numpy.mean(distances * numpy.sin(azimuths))
        avg_y = numpy.mean(distances * numpy.cos(azimuths))
        # find the mean azimuth from that mean vector
        azimuth = numpy.degrees(numpy.arctan2(avg_x, avg_y))
        if azimuth < 0:
            azimuth += 360
        return azimuth
Beispiel #10
0
    def get_distance_matrix(self):
        """
        Compute and return distances between each pairs of points in the mesh.

        This method requires that all the points lie on Earth surface (have
        zero depth) and coordinate arrays are one-dimensional.

        .. warning::
            Because of its quadratic space and time complexity this method
            is safe to use for meshes of up to several thousand points. For
            mesh of 10k points it needs ~800 Mb for just the resulting matrix
            and four times that much for intermediate storage.

        :returns:
            Two-dimensional numpy array, square matrix of distances. The matrix
            has zeros on main diagonal and positive distances in kilometers
            on all other cells. That is, value in cell (3, 5) is the distance
            between mesh's points 3 and 5 in km, and it is equal to value
            in cell (5, 3).

        Uses :func:`nhlib.geo.geodetic.geodetic_distance`.
        """
        assert self.lons.ndim == 1
        assert self.depths is None or (self.depths == 0).all()
        distances = geodetic.geodetic_distance(
            self.lons.reshape(self.lons.shape + (1, )),
            self.lats.reshape(self.lats.shape + (1, )),
            self.lons,
            self.lats
        )
        return numpy.matrix(distances, copy=False)
Beispiel #11
0
    def translate(self, p1, p2):
        """
        Translate the surface for a specific distance along a specific azimuth
        direction.

        Parameters are two points (instances of :class:`nhlib.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 BaseSurface's one
        BaseSurface.__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
Beispiel #12
0
 def test_arrays(self):
     lons1 = numpy.array([[-50.03824533, -153.97808192],
                          [-106.36068694, 47.06944014]])
     lats1 = numpy.array([[52.20211151, 17.018482],
                          [-26.06421527, -20.30383723]])
     lons2 = numpy.array([[49.97448794, 52.3126795],
                          [-165.13925579, 162.16421979]])
     lats2 = numpy.array([[-27.66510775, -73.66591257],
                          [-60.24498761, -9.92908014]])
     edist = numpy.array([[13061.87935023, 13506.24000062],
                          [5807.34519649, 12163.45759805]])
     dist = geodetic.geodetic_distance(lons1, lats1, lons2, lats2)
     self.assertEqual(dist.shape, edist.shape)
     self.assertTrue(numpy.allclose(dist, edist))
Beispiel #13
0
 def test_arrays(self):
     lons1 = numpy.array([[-50.03824533, -153.97808192],
                          [-106.36068694, 47.06944014]])
     lats1 = numpy.array([[52.20211151, 17.018482],
                          [-26.06421527, -20.30383723]])
     lons2 = numpy.array([[49.97448794, 52.3126795],
                          [-165.13925579, 162.16421979]])
     lats2 = numpy.array([[-27.66510775, -73.66591257],
                          [-60.24498761,  -9.92908014]])
     edist = numpy.array([[13061.87935023, 13506.24000062],
                          [5807.34519649, 12163.45759805]])
     dist = geodetic.geodetic_distance(lons1, lats1, lons2, lats2)
     self.assertEqual(dist.shape, edist.shape)
     self.assertTrue(numpy.allclose(dist, edist))
Beispiel #14
0
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)
Beispiel #15
0
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)
Beispiel #16
0
def get_resampled_coordinates(lons, lats):
    """
    Resample polygon line segments and return the coordinates of the new
    vertices. This limits distortions when projecting a polygon onto a
    spherical surface.

    Parameters define longitudes and latitudes of a point collection in the
    form of lists or numpy arrays.

    :return:
        A tuple of two numpy arrays: longitudes and latitudes
        of resampled vertices.
    """
    num_coords = len(lons)
    assert num_coords == len(lats)

    lons1 = numpy.array(lons)
    lats1 = numpy.array(lats)
    lons2 = numpy.concatenate((lons1[1:], lons1[0:1]))
    lats2 = numpy.concatenate((lats1[1:], lats1[0:1]))
    distances = geodetic.geodetic_distance(lons1, lats1, lons2, lats2)

    resampled_lons = [lons[0]]
    resampled_lats = [lats[0]]
    for i in xrange(num_coords):
        next_point = (i + 1) % num_coords
        lon1, lat1 = lons[i], lats[i]
        lon2, lat2 = lons[next_point], lats[next_point]

        distance = distances[i]
        num_points = int(distance / UPSAMPLING_STEP_KM) + 1
        if num_points >= 2:
            # We need to increase the resolution of this arc by adding new
            # points.
            new_lons, new_lats, _ = geodetic.npoints_between(
                lon1, lat1, 0, lon2, lat2, 0, num_points
            )
            resampled_lons.extend(new_lons[1:])
            resampled_lats.extend(new_lats[1:])
        else:
            resampled_lons.append(lon2)
            resampled_lats.append(lat2)
    # we cut off the last point because it repeats the first one.
    return numpy.array(resampled_lons[:-1]), numpy.array(resampled_lats[:-1])
Beispiel #17
0
def get_resampled_coordinates(lons, lats):
    """
    Resample polygon line segments and return the coordinates of the new
    vertices. This limits distortions when projecting a polygon onto a
    spherical surface.

    Parameters define longitudes and latitudes of a point collection in the
    form of lists or numpy arrays.

    :return:
        A tuple of two numpy arrays: longitudes and latitudes
        of resampled vertices.
    """
    num_coords = len(lons)
    assert num_coords == len(lats)

    lons1 = numpy.array(lons)
    lats1 = numpy.array(lats)
    lons2 = numpy.concatenate((lons1[1:], lons1[0:1]))
    lats2 = numpy.concatenate((lats1[1:], lats1[0:1]))
    distances = geodetic.geodetic_distance(lons1, lats1, lons2, lats2)

    resampled_lons = [lons[0]]
    resampled_lats = [lats[0]]
    for i in xrange(num_coords):
        next_point = (i + 1) % num_coords
        lon1, lat1 = lons[i], lats[i]
        lon2, lat2 = lons[next_point], lats[next_point]

        distance = distances[i]
        num_points = int(distance / UPSAMPLING_STEP_KM) + 1
        if num_points >= 2:
            # We need to increase the resolution of this arc by adding new
            # points.
            new_lons, new_lats, _ = geodetic.npoints_between(
                lon1, lat1, 0, lon2, lat2, 0, num_points)
            resampled_lons.extend(new_lons[1:])
            resampled_lats.extend(new_lats[1:])
        else:
            resampled_lons.append(lon2)
            resampled_lats.append(lat2)
    # we cut off the last point because it repeats the first one.
    return numpy.array(resampled_lons[:-1]), numpy.array(resampled_lats[:-1])
Beispiel #18
0
 def test_one_point_on_pole(self):
     dist = geodetic.geodetic_distance(0, 90, 0, 88)
     self.assertAlmostEqual(dist, 222.3898533)
Beispiel #19
0
 def test_opposite_points(self):
     dist = geodetic.geodetic_distance(110, -10, -70, 10)
     self.assertAlmostEqual(dist, geodetic.EARTH_RADIUS * numpy.pi,
                            places=3)
Beispiel #20
0
 def test_on_equator(self):
     dist = geodetic.geodetic_distance(0, 0, 1, 0)
     self.assertAlmostEqual(dist, 111.1949266)
     [dist2] = geodetic.geodetic_distance([-5], [0], [-6], [0])
     self.assertAlmostEqual(dist, dist2)
Beispiel #21
0
 def test_LAX_to_JFK(self):
     dist = geodetic.geodetic_distance(*(LAX + JFK))
     self.assertAlmostEqual(dist, 0.623585 * geodetic.EARTH_RADIUS,
                            delta=0.1)
     dist2 = geodetic.geodetic_distance(*(JFK + LAX))
     self.assertAlmostEqual(dist, dist2)
Beispiel #22
0
 def test_on_equator(self):
     dist = geodetic.geodetic_distance(0, 0, 1, 0)
     self.assertAlmostEqual(dist, 111.1949266)
     [dist2] = geodetic.geodetic_distance([-5], [0], [-6], [0])
     self.assertAlmostEqual(dist, dist2)
Beispiel #23
0
    def get_joyner_boore_distance(self, mesh):
        """
        See :meth:`superclass' method
        <nhlib.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.geodetic_distance(
            self.corner_lons, self.corner_lats, mesh_lons, mesh_lats
        ).min(axis=-1)

        # 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)

        return 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
        )
Beispiel #24
0
 def test_one_point_on_pole(self):
     dist = geodetic.geodetic_distance(0, 90, 0, 88)
     self.assertAlmostEqual(dist, 222.3898533)
Beispiel #25
0
 def test_small_distance(self):
     dist = geodetic.geodetic_distance(0, 0, 0, 1e-10)
     self.assertAlmostEqual(dist, 0)
     dist = geodetic.geodetic_distance(-1e-12, 0, 0, 0)
     self.assertAlmostEqual(dist, 0)
Beispiel #26
0
 def test_small_distance(self):
     dist = geodetic.geodetic_distance(0, 0, 0, 1e-10)
     self.assertAlmostEqual(dist, 0)
     dist = geodetic.geodetic_distance(-1e-12, 0, 0, 0)
     self.assertAlmostEqual(dist, 0)
Beispiel #27
0
 def test_opposite_points(self):
     dist = geodetic.geodetic_distance(110, -10, -70, 10)
     self.assertAlmostEqual(dist,
                            geodetic.EARTH_RADIUS * numpy.pi,
                            places=3)
Beispiel #28
0
 def test_along_meridian(self):
     coords = map(numpy.array, [(77.5, -150.), (-10., 15.), (77.5, -150.),
                                (-20., 25.)])
     dist = geodetic.geodetic_distance(*coords)
     self.assertTrue(numpy.allclose(dist, [1111.949, 1111.949]), str(dist))
Beispiel #29
0
 def test_along_meridian(self):
     coords = map(numpy.array, [(77.5, -150.), (-10., 15.),
                                (77.5, -150.), (-20., 25.)])
     dist = geodetic.geodetic_distance(*coords)
     self.assertTrue(numpy.allclose(dist, [1111.949, 1111.949]), str(dist))