Пример #1
0
    def _fix_right_hand(self):
        # This method fixes the mesh used to represent the grid surface so
        # that it complies with the right hand rule.
        found = False
        irow = 0
        icol = 0
        while not found:
            if np.all(np.isfinite(self.mesh.lons[irow:irow+2, icol:icol+2])):
                found = True
            else:
                icol += 1
                if (icol+1) >= self.mesh.lons.shape[1]:
                    irow += 1
                    icol = 1
                    if (irow+1) >= self.mesh.lons.shape[0]:
                        break
        if found:
            azi_strike = azimuth(self.mesh.lons[irow, icol],
                                 self.mesh.lats[irow, icol],
                                 self.mesh.lons[irow+1, icol],
                                 self.mesh.lats[irow+1, icol])
            azi_dip = azimuth(self.mesh.lons[irow, icol],
                              self.mesh.lats[irow, icol],
                              self.mesh.lons[irow, icol+1],
                              self.mesh.lats[irow, icol+1])

            if abs((azi_strike + 90) % 360 - azi_dip) < 10:
                tlo = np.fliplr(self.mesh.lons)
                tla = np.fliplr(self.mesh.lats)
                tde = np.fliplr(self.mesh.depths)
                mesh = RectangularMesh(tlo, tla, tde)
                self.mesh = mesh
        else:
            msg = 'Could not find a valid quadrilateral for strike calculation'
            raise ValueError(msg)
Пример #2
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 openquake.hazardlib.geo.point import Point as P
        >>> '%.1f' % Line([P(0, 0), P(1e-5, 1e-5)]).average_azimuth()
        '45.0'
        >>> '%.1f' % 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
Пример #3
0
def _find_turning_points(mesh, tol=1.0):
    """
    Identifies the turning points in a rectangular mesh based on the
    deviation in the azimuth between successive points on the upper edge.
    A turning point is flagged if the change in azimuth change is greater than
    the specified tolerance (in degrees)

    :param mesh:
        Mesh for downsampling as instance of :class:
        openquake.hazardlib.geo.mesh.RectangularMesh

    :param float tol:
        Maximum difference in azimuth (decimal degrees) between successive
        points to identify a turning point

    :returns:
        Column indices of turning points (as numpy array)
    """
    assert isinstance(mesh, RectangularMesh)
    azimuths = geodetic.azimuth(mesh.lons[0, :-1], mesh.lats[0, :-1],
                                mesh.lons[0, 1:], mesh.lats[0, 1:])
    naz = len(azimuths)
    azim = azimuths[0]
    # Retain initial point
    idx = [0]
    for i in range(1, naz):
        if numpy.fabs(azimuths[i] - azim) > tol:
            idx.append(i)
            azim = azimuths[i]
    # Add on last point - if not already in the set
    if not idx[-1] == (mesh.lons.shape[1] - 1):
        idx.append(mesh.lons.shape[1] - 1)
    return numpy.array(idx)
Пример #4
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:`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
Пример #5
0
def get_xyz_from_ll(projected, reference):
    """
    This method computes the x, y and z coordinates of a set of points
    provided a reference point

    :param projected:
        :class:`~openquake.hazardlib.geo.point.Point` object
        representing the coordinates of target point to be projected
    :param reference:
        :class:`~openquake.hazardlib.geo.point.Point` object
        representing the coordinates of the reference point.

    :returns:
            x
            y
            z
    """

    azims = geod.azimuth(reference.longitude, reference.latitude,
                         projected.longitude, projected.latitude)
    depths = np.subtract(reference.depth, projected.depth)
    dists = geod.geodetic_distance(reference.longitude,
                                   reference.latitude,
                                   projected.longitude,
                                   projected.latitude)
    return (dists * math.sin(math.radians(azims)),
            dists * math.cos(math.radians(azims)),
            depths)
Пример #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 openquake.hazardlib.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
Пример #7
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:`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
Пример #8
0
def _find_turning_points(mesh, tol=1.0):
    """
    Identifies the turning points in a rectangular mesh based on the
    deviation in the azimuth between successive points on the upper edge.
    A turning point is flagged if the change in azimuth change is greater than
    the specified tolerance (in degrees)

    :param mesh:
        Mesh for downsampling as instance of :class:
        openquake.hazardlib.geo.mesh.RectangularMesh

    :param float tol:
        Maximum difference in azimuth (decimal degrees) between successive
        points to identify a turning point

    :returns:
        Column indices of turning points (as numpy array)
    """
    assert isinstance(mesh, RectangularMesh)
    azimuths = geodetic.azimuth(mesh.lons[0, :-1], mesh.lats[0, :-1],
                                mesh.lons[0, 1:], mesh.lats[0, 1:])
    naz = len(azimuths)
    azim = azimuths[0]
    # Retain initial point
    idx = [0]
    for i in range(1, naz):
        if numpy.fabs(azimuths[i] - azim) > tol:
            idx.append(i)
            azim = azimuths[i]
    # Add on last point - if not already in the set
    if idx[-1] != mesh.lons.shape[1] - 1:
        idx.append(mesh.lons.shape[1] - 1)
    return numpy.array(idx)
Пример #9
0
def test_map_rupture(interactive=False):
    xp0 = np.array([-90.898000])
    xp1 = np.array([-91.308000])
    yp0 = np.array([12.584000])
    yp1 = np.array([12.832000])
    zp = [0.0]
    strike = azimuth(yp0[0], xp0[0], yp1[0], xp1[0])
    origin = Origin({
        'lat': 0.0,
        'lon': 0.0,
        'depth': 0.0,
        'mag': 5.5,
        'eventsourcecode': 'abcd'
    })
    interface_width = MAX_DEPTH / np.sin(np.radians(DIP))
    widths = np.ones(xp0.shape) * interface_width
    dips = np.ones(xp0.shape) * DIP
    strike = [strike]
    rupture = QuadRupture.fromTrace(xp0,
                                    yp0,
                                    xp1,
                                    yp1,
                                    zp,
                                    widths,
                                    dips,
                                    origin,
                                    strike=strike)
    map_rupture(rupture)
    if interactive:
        fname = os.path.join(os.path.expanduser('~'), 'rupture_map.png')
        plt.savefig(fname)
        print('Rupture map plot saved to %s.  Delete this file if you wish.' %
              fname)
Пример #10
0
def get_xyz_from_ll(projected, reference):
    """
    This method computes the x, y and z coordinates of a set of points
    provided a reference point

    :param projected:
        :class:`~openquake.hazardlib.geo.point.Point` object
        representing the coordinates of target point to be projected
    :param reference:
        :class:`~openquake.hazardlib.geo.point.Point` object
        representing the coordinates of the reference point.

    :returns:
            x
            y
            z
    """

    azims = geod.azimuth(reference.longitude, reference.latitude,
                         projected.longitude, projected.latitude)
    depths = np.subtract(reference.depth, projected.depth)
    dists = geod.geodetic_distance(reference.longitude, reference.latitude,
                                   projected.longitude, projected.latitude)
    return (dists * math.sin(math.radians(azims)),
            dists * math.cos(math.radians(azims)), depths)
Пример #11
0
 def test_arrays(self):
     lons1 = numpy.array([[156.49676849, 150.03697145],
                          [-77.96053914, -109.36694411]])
     lats1 = numpy.array([[-79.78522764, -89.15044328],
                          [-32.28244296, -25.11092309]])
     lons2 = numpy.array([[-84.6732372, 140.08382287],
                          [82.69227935, -18.9919318]])
     lats2 = numpy.array([[82.16896786, 26.16081412],
                          [41.21501474, -2.88241099]])
     eazimuths = numpy.array([[47.07336955, 350.11740733],
                              [54.46959147, 92.76923701]])
     az = geodetic.azimuth(lons1, lats1, lons2, lats2)
     self.assertTrue(numpy.allclose(az, eazimuths), str(az))
Пример #12
0
    def get_azimuth_of_closest_point(self, mesh):
        """
        Compute the azimuth between point in `mesh` and the corresponding
        closest point on the rupture surface.

        :param mesh:
            An instance of  :class:`openquake.hazardlib.geo.mesh`
        :return:
            An :class:`numpy.ndarray` instance with the azimuth values.
        """
        mesh_closest = self.get_closest_points(mesh)
        return geodetic.azimuth(mesh.lons, mesh.lats, mesh_closest.lons,
                                mesh_closest.lats)
Пример #13
0
    def get_strike(self) -> float:
        """
        Return the fault strike as the average strike along the top of the
        fault surface.

        :returns:
            The average strike, in decimal degrees.
        """
        if self.strike is None:
            idx = np.isfinite(self.mesh.lons)
            azi = azimuth(self.mesh.lons[:-1, :], self.mesh.lats[:-1, :],
                          self.mesh.lons[1:, :], self.mesh.lats[1:, :])
            self.strike = np.mean(((azi[idx[:-1, :]] + 0.001) % 360))
        return self.strike
Пример #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)
Пример #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)
Пример #16
0
    def azimuth(self, point):
        """
        Compute the azimuth (in decimal degrees) between this point
        and the given point.

        :param point:
            Destination point.
        :type point:
            Instance of :class:`Point`
        :returns:
            The azimuth, value in a range ``[0, 360)``.
        :rtype:
            float
        """
        return geodetic.azimuth(self.longitude, self.latitude,
                                point.longitude, point.latitude)
Пример #17
0
    def azimuth(self, point):
        """
        Compute the azimuth (in decimal degrees) between this point
        and the given point.

        :param point:
            Destination point.
        :type point:
            Instance of :class:`Point`
        :returns:
            The azimuth, value in a range ``[0, 360)``.
        :rtype:
            float
        """
        return geodetic.azimuth(self.longitude, self.latitude, point.longitude,
                                point.latitude)
Пример #18
0
def test_plot_rupture(interactive=False):
    xp0 = np.array([-90.898000])
    xp1 = np.array([-91.308000])
    yp0 = np.array([12.584000])
    yp1 = np.array([12.832000])
    zp = [0.0]
    strike = azimuth(yp0[0], xp0[0], yp1[0], xp1[0])
    origin = Origin({
        'lat': 0.0,
        'lon': 0.0,
        'depth': 0.0,
        'mag': 5.5,
        'id': '',
        'netid': 'abcd',
        'network': '',
        'locstring': '',
        'time': HistoricTime.utcfromtimestamp(time.time())
    })
    interface_width = MAX_DEPTH / np.sin(np.radians(DIP))
    widths = np.ones(xp0.shape) * interface_width
    dips = np.ones(xp0.shape) * DIP
    strike = [strike]
    rupture = QuadRupture.fromTrace(xp0,
                                    yp0,
                                    xp1,
                                    yp1,
                                    zp,
                                    widths,
                                    dips,
                                    origin,
                                    strike=strike)
    plot_rupture_wire3d(rupture)
    if interactive:
        fname = os.path.join(os.path.expanduser('~'), 'rupture_wire_plot.png')
        plt.savefig(fname)
        print('Wire 3D plot saved to %s.  Delete this file if you wish.' %
              fname)

    # Need to get tests to check exception for if an axis is handed off
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    plot_rupture_wire3d(rupture, ax)

    # And raise the exception if it is not a 3d axis
    with pytest.raises(TypeError):
        ax = fig.add_subplot(111)
        plot_rupture_wire3d(rupture, ax)
Пример #19
0
    def resample(self, distance):
        """
        This resamples the trench axis given a certain distance and computes
        the strike at each node.

        :parameter distance:
            The sampling distance [in km
        """
        naxis = rsmpl(self.axis[:, 0], self.axis[:, 1], distance)
        if len(self.axis) < 3:
            raise ValueError('Small array')
        #
        # compute azimuths
        az = numpy.zeros_like(self.axis[:, 0])
        az[1:-1] = azimuth(self.axis[:-2, 0], self.axis[:-2, 1],
                           self.axis[2:, 0], self.axis[2:, 1])
        az[0] = az[1]
        az[-1] = az[-2]
        return Trench(naxis, az)
Пример #20
0
def get_compass_dir(lat1, lon1, lat2, lon2, format='short'):
    """Get the nearest string compass direction between two points.
    
    :param lat1: Latitude of first point.
    :param lon1: Longitude of first point.
    :param lat2: Latitude of second point.
    :param lon2: Longitude of second point.
    :param format: String used to determine the type of output. ('short','long').
    :returns: String compass direction, in the form of 'North','Northeast',... if format is 'long', 
             or 'N','NE',... if format is 'short'.
    """
    if format != 'short':
        points = ['North', 'Northeast', 'East', 'Southeast', 'South', 'Southwest', 'West', 'Northwest']
    else:
        points = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
    az = geodetic.azimuth(lon1, lat1, lon2, lat2)
    angles = np.arange(0, 360, 45)
    adiff = abs(az - angles)
    i = adiff.argmin()
    return points[i]
Пример #21
0
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
Пример #22
0
    def get_azimuth(self, mesh):
        """
        This method computes the azimuth of a set of points in a
        :class:`openquake.hazardlib.geo.mesh` instance. The reference used for
        the calculation of azimuth is the middle point and the strike of the
        rupture. The value of azimuth computed corresponds to the angle
        measured in a clockwise direction from the strike of the rupture.

        :parameter mesh:
            An instance of  :class:`openquake.hazardlib.geo.mesh`
        :return:
            An instance of `numpy.ndarray`
        """
        # Get info about the rupture
        strike = self.get_strike()
        hypocenter = self.get_middle_point()
        # This is the azimuth from the north of each point Vs. the middle of
        # the rupture
        azim = geodetic.azimuth(hypocenter.longitude, hypocenter.latitude,
                                mesh.lons, mesh.lats)
        # Compute the azimuth from the fault strike
        rel_azi = (azim - strike) % 360
        return rel_azi
Пример #23
0
    def get_azimuth(self, mesh):
        """
        This method computes the azimuth of a set of points in a
        :class:`openquake.hazardlib.geo.mesh` instance. The reference used for
        the calculation of azimuth is the middle point and the strike of the
        rupture. The value of azimuth computed corresponds to the angle
        measured in a clockwise direction from the strike of the rupture.

        :parameter mesh:
            An instance of  :class:`openquake.hazardlib.geo.mesh`
        :return:
            An instance of `numpy.ndarray`
        """
        # Get info about the rupture
        strike = self.get_strike()
        hypocenter = self.get_middle_point()
        # This is the azimuth from the north of each point Vs. the middle of
        # the rupture
        azim = geodetic.azimuth(hypocenter.longitude, hypocenter.latitude,
                                mesh.lons, mesh.lats)
        # Compute the azimuth from the fault strike
        rel_azi = (azim - strike) % 360
        return rel_azi
Пример #24
0
def test_plot_rupture(interactive=False):
    xp0 = np.array([-90.898000])
    xp1 = np.array([-91.308000])
    yp0 = np.array([12.584000])
    yp1 = np.array([12.832000])
    zp = [0.0]
    strike = azimuth(yp0[0], xp0[0], yp1[0], xp1[0])
    origin = Origin({
        'lat': 0.0,
        'lon': 0.0,
        'depth': 0.0,
        'mag': 5.5,
        'id': '',
        'netid': 'abcd',
        'network': '',
        'locstring': '',
        'time': HistoricTime.utcfromtimestamp(time.time())
    })
    interface_width = MAX_DEPTH / np.sin(np.radians(DIP))
    widths = np.ones(xp0.shape) * interface_width
    dips = np.ones(xp0.shape) * DIP
    strike = [strike]
    rupture = QuadRupture.fromTrace(xp0,
                                    yp0,
                                    xp1,
                                    yp1,
                                    zp,
                                    widths,
                                    dips,
                                    origin,
                                    strike=strike)
    plot_rupture_wire3d(rupture)
    if interactive:
        fname = os.path.join(os.path.expanduser('~'), 'rupture_wire_plot.png')
        plt.savefig(fname)
        print('Wire 3D plot saved to %s.  Delete this file if you wish.' %
              fname)
Пример #25
0
def test_map_rupture(interactive=False):
    xp0 = np.array([-90.898000])
    xp1 = np.array([-91.308000])
    yp0 = np.array([12.584000])
    yp1 = np.array([12.832000])
    zp = [0.0]
    strike = azimuth(yp0[0], xp0[0], yp1[0], xp1[0])
    origin = Origin({'lat': 0.0,
                     'lon': 0.0,
                     'depth': 0.0,
                     'mag': 5.5,
                     'eventsourcecode': 'abcd'})
    interface_width = MAX_DEPTH / np.sin(np.radians(DIP))
    widths = np.ones(xp0.shape) * interface_width
    dips = np.ones(xp0.shape) * DIP
    strike = [strike]
    rupture = QuadRupture.fromTrace(
        xp0, yp0, xp1, yp1, zp, widths, dips, origin, strike=strike)
    map_rupture(rupture)
    if interactive:
        fname = os.path.join(os.path.expanduser('~'), 'rupture_map.png')
        plt.savefig(fname)
        print('Rupture map plot saved to %s.  Delete this file if you wish.'
              % fname)
Пример #26
0
def _get_cell_area(rlo, rla, coo, nnidx):
    """
    :parameter rlo:
    :parameter rla:
    :parameter coo:
    :parameter nnidx:

    :return:

    """
    alo = [coo[idx][0] for idx in nnidx]
    ala = [coo[idx][1] for idx in nnidx]
    #
    # Computing azimuths and distances
    azis = azimuth(rlo, rla, alo, ala)
    dsts = geodetic_distance(rlo, rla, alo, ala)
    #
    # Processing the selected nodes
    delta = 5.0
    colocated = 0
    nearest_nodes = {}
    for azi, dst, idx in zip(azis, dsts, nnidx):
        if dst < 0.5:
            if (abs(rlo - coo[idx][0]) < 0.005
                    and abs(rla - coo[idx][1]) < 0.005):
                colocated += 1
                continue
        # East
        if abs(azi - 90) < delta:
            if 90 in nearest_nodes:
                if dst < nearest_nodes[90][0]:
                    nearest_nodes[90] = (dst, idx)
            else:
                nearest_nodes[90] = (dst, idx)
        # South
        elif abs(azi - 180) < delta:
            if 180 in nearest_nodes:
                if dst < nearest_nodes[180][0]:
                    nearest_nodes[180] = (dst, idx)
            else:
                nearest_nodes[180] = (dst, idx)
        # West
        elif abs(azi - 270) < delta:
            if 270 in nearest_nodes:
                if dst < nearest_nodes[270][0]:
                    nearest_nodes[270] = (dst, idx)
            else:
                nearest_nodes[270] = (dst, idx)
        # North
        elif abs(azi - 360) < delta or azi < delta:
            if 0 in nearest_nodes:
                if dst < nearest_nodes[0][0]:
                    nearest_nodes[0] = (dst, idx)
            else:
                nearest_nodes[0] = (dst, idx)
        else:
            pass
    #
    # fix missing information
    out = np.nan
    try:
        fdsts = _get_final_dsts(nearest_nodes)
        out = (fdsts[0] + fdsts[2]) / 2 * (fdsts[1] + fdsts[3]) / 2
    except:
        pass
        logging.debug('Node:', rlo, rla)
        logging.debug('Nearest nodes:', nearest_nodes)
        logging.debug('Queried nodes:')
        for idx in nnidx:
            logging.debug('  ', coo[idx][0], coo[idx][1], coo[idx][2])
    return out, colocated
Пример #27
0
def test_rupture_depth(interactive=False):
    DIP = 17.0
    WIDTH = 20.0
    GRIDRES = 0.1

    names = ['single', 'double', 'triple',
             'concave', 'concave_simple', 'ANrvSA']
    means = [3.1554422780092461, 2.9224454569459781,
             3.0381968625073563, 2.0522694624400271,
             2.4805390352818755, 2.8740121776209673]
    stds = [2.1895293825074575, 2.0506459673526174,
            2.0244588429154402, 2.0112565876976416,
            2.1599789955270019, 1.6156220309120068]
    xp0list = [np.array([118.3]),
               np.array([10.1, 10.1]),
               np.array([10.1, 10.1, 10.3]),
               np.array([10.9, 10.5, 10.9]),
               np.array([10.9, 10.6]),
               np.array([-76.483, -76.626, -76.757, -76.99, -77.024, -76.925,
                         -76.65, -76.321, -75.997, -75.958])]
    xp1list = [np.array([118.3]),
               np.array([10.1, 10.3]),
               np.array([10.1, 10.3, 10.1]),
               np.array([10.5, 10.9, 11.3]),
               np.array([10.6, 10.9]),
               np.array([-76.626, -76.757, -76.99, -77.024, -76.925, -76.65,
                         -76.321, -75.997, -75.958, -76.006])]
    yp0list = [np.array([34.2]),
               np.array([34.2, 34.5]),
               np.array([34.2, 34.5, 34.8]),
               np.array([34.2, 34.5, 34.8]),
               np.array([35.1, 35.2]),
               np.array([-52.068, -51.377, -50.729, -49.845, -49.192, -48.507,
                         -47.875, -47.478, -47.08, -46.422])]
    yp1list = [np.array([34.5]),
               np.array([34.5, 34.8]),
               np.array([34.5, 34.8, 35.1]),
               np.array([34.5, 34.8, 34.6]),
               np.array([35.2, 35.4]),
               np.array([-51.377, -50.729, -49.845, -49.192, -48.507, -47.875,
                         -47.478, -47.08, -46.422, -45.659])]

    for i in range(0, len(xp0list)):
        xp0 = xp0list[i]
        xp1 = xp1list[i]
        yp0 = yp0list[i]
        yp1 = yp1list[i]
        name = names[i]
        mean_value = means[i]
        std_value = stds[i]

        zp = np.zeros(xp0.shape)
        strike = azimuth(xp0[0], yp0[0], xp1[-1], yp1[-1])
        widths = np.ones(xp0.shape) * WIDTH
        dips = np.ones(xp0.shape) * DIP
        strike = [strike]

    origin = Origin({'id': 'test',
                     'lon': 0, 'lat': 0,
                     'depth': 5.0, 'mag': 7.0, 'netid': 'us',
                     'network': '', 'locstring': '',
                     'time': HistoricTime.utcfromtimestamp(time.time())})

    rupture = QuadRupture.fromTrace(
        xp0, yp0, xp1, yp1, zp, widths, dips, origin, strike=strike)

    # make a grid of points over both quads, ask for depths
    ymin = np.nanmin(rupture.lats)
    ymax = np.nanmax(rupture.lats)
    xmin = np.nanmin(rupture.lons)
    xmax = np.nanmax(rupture.lons)

    xmin = np.floor(xmin * (1 / GRIDRES)) / (1 / GRIDRES)
    xmax = np.ceil(xmax * (1 / GRIDRES)) / (1 / GRIDRES)
    ymin = np.floor(ymin * (1 / GRIDRES)) / (1 / GRIDRES)
    ymax = np.ceil(ymax * (1 / GRIDRES)) / (1 / GRIDRES)
    geodict = GeoDict.createDictFromBox(
        xmin, xmax, ymin, ymax, GRIDRES, GRIDRES)
    nx = geodict.nx
    ny = geodict.ny
    depths = np.zeros((ny, nx))
    for row in range(0, ny):
        for col in range(0, nx):
            lat, lon = geodict.getLatLon(row, col)
            depth = rupture.getDepthAtPoint(lat, lon)
            depths[row, col] = depth

    np.testing.assert_almost_equal(np.nanmean(depths), mean_value)
    np.testing.assert_almost_equal(np.nanstd(depths), std_value)

    if interactive:
        fig, axes = plt.subplots(nrows=2, ncols=1)
        ax1, ax2 = axes
        xdata = np.append(xp0, xp1[-1])
        ydata = np.append(yp0, yp1[-1])
        plt.sca(ax1)
        plt.plot(xdata, ydata, 'b')
        plt.sca(ax2)
        im = plt.imshow(depths, cmap='viridis_r')  # noqa
        ch = plt.colorbar()  # noqa
        fname = os.path.join(os.path.expanduser('~'),
                             'quad_%s_test.png' % name)
        print('Saving image for %s quad test... %s' % (name, fname))
        plt.savefig(fname)
        plt.close()
Пример #28
0
def _resample_profile(line, sampling_dist):
    """
    :parameter line:
        An instance of :class:`openquake.hazardlib.geo.line.Line`
    :parameter sampling_dist:
        A scalar definining the distance used to sample the profile
    :returns:
        An instance of :class:`openquake.hazardlib.geo.line.Line`
    """
    lo = [pnt.longitude for pnt in line.points]
    la = [pnt.latitude for pnt in line.points]
    de = [pnt.depth for pnt in line.points]
    #
    # initialise the cumulated distance
    cdist = 0.
    #
    # get the azimuth of the profile
    azim = azimuth(lo[0], la[0], lo[-1], la[-1])
    #
    # initialise the list with the resampled nodes
    idx = 0
    resampled_cs = [Point(lo[idx], la[idx], de[idx])]
    #
    # set the starting point
    slo = lo[idx]
    sla = la[idx]
    sde = de[idx]
    #
    # resampling
    while 1:
        #
        # check loop exit condition
        if idx > len(lo) - 2:
            break
        #
        # compute the distance between the starting point and the next point
        # on the profile
        segment_len = distance(slo, sla, sde, lo[idx + 1], la[idx + 1],
                               de[idx + 1])
        #
        # search for the point
        if cdist + segment_len > sampling_dist:
            #
            # this is the lenght of the last segment-fraction needed to
            # obtain the sampling distance
            delta = sampling_dist - cdist
            #
            # compute the slope of the last segment and its horizontal length.
            # We need to manage the case of a vertical segment TODO
            segment_hlen = distance(slo, sla, 0., lo[idx + 1], la[idx + 1], 0.)
            segment_slope = np.arctan((de[idx + 1] - sde) / segment_hlen)
            #
            # horizontal and vertical lenght of delta
            delta_v = delta * np.sin(segment_slope)
            delta_h = delta * np.cos(segment_slope)
            #
            # add a new point to the cross section
            pnts = npoints_towards(slo, sla, sde, azim, delta_h, delta_v, 2)
            #
            # update the starting point
            slo = pnts[0][-1]
            sla = pnts[1][-1]
            sde = pnts[2][-1]
            resampled_cs.append(Point(slo, sla, sde))
            #
            # reset the cumulative distance
            cdist = 0.

        else:
            cdist += segment_len
            idx += 1
            slo = lo[idx]
            sla = la[idx]
            sde = de[idx]

    line = Line(resampled_cs)
    return line
Пример #29
0
def store(filename, model, info=None):
    """
    This creates a pickle file containing the list of earthquake sources
    representing an earthquake source model.

    :parameter filename:
        The name of the file where the to store the model
    :parameter model:
        A list of OpenQuake hazardlib source instances
    """
    # Preparing output filenames
    dname = os.path.dirname(filename)
    slist = re.split('\\.', os.path.basename(filename))
    # SIDx
    p = index.Property()
    p.dimension = 3
    sidx = index.Rtree(os.path.join(dname, slist[0]), properties=p)
    #
    cpnt = 0
    l_other = []
    l_points = []
    for src in model:
        if isinstance(src, (AreaSource, SimpleFaultSource, ComplexFaultSource,
                      CharacteristicFaultSource, NonParametricSeismicSource)):
            l_other.append(src)
        else:

            if len(src.hypocenter_distribution.data) == 1:
                srcs = [src]
            else:
                srcs = _split_point_source(src)

            for src in srcs:
                l_points.append(src)
                # calculate distances
                dst = distance(l_points[0].location.longitude,
                               l_points[0].location.latitude,
                               0.,
                               src.location.longitude,
                               src.location.latitude,
                               0.)
                azi = azimuth(l_points[0].location.longitude,
                              l_points[0].location.latitude,
                              src.location.longitude,
                              src.location.latitude)
                x = numpy.cos(numpy.radians(azi)) * dst
                y = numpy.sin(numpy.radians(azi)) * dst
                # update the spatial index
                z = src.hypocenter_distribution.data[0][1]
                sidx.insert(cpnt, (x, y, z, x, y, z))
                cpnt += 1

    # All the other sources
    fou = open(filename, 'wb')
    pickle.dump(l_other, fou)
    fou.close()
    # Load info
    if info is None:
        info = _get_model_info(model)
    # Points
    fou = open(os.path.join(dname, slist[0]) + '_points.pkl', 'wb')
    pickle.dump(l_points, fou)
    fou.close()
    # Info
    fou = open(os.path.join(dname, slist[0]) + '_info.pkl', 'wb')
    pickle.dump(info, fou)
    fou.close()
Пример #30
0
 def test_quadrants(self):
     az = geodetic.azimuth(0, 0, [0.01, 0.01, -0.01, -0.01],
                           [0.01, -0.01, -0.01, 0.01])
     assert_aeq(az, [45, 135, 225, 315], decimal=5)
Пример #31
0
 def test_quadrants(self):
     az = geodetic.azimuth(0, 0, [0.01, 0.01, -0.01, -0.01],
                                 [0.01, -0.01, -0.01, 0.01])
     assert_aeq(az, [45, 135, 225, 315], decimal=5)
Пример #32
0
 def test_meridians(self):
     az = geodetic.azimuth(0, 0, 0, 1)
     self.assertEqual(az, 0)
     az = geodetic.azimuth(0, 2, 0, 1)
     self.assertEqual(az, 180)
Пример #33
0
 def test_LAX_to_JFK(self):
     az = geodetic.azimuth(*(LAX + JFK))
     self.assertAlmostEqual(az, 360 - 65.8922, places=4)
Пример #34
0
def _resample_edge_with_direction(edge,
                                  sampling_dist,
                                  reference_idx,
                                  direct=+1):
    """
    :param edge:
    :param sampling_dist:
    :param reference_idx:
    :param direct:
    """
    #
    # checking that the increment is either 1 or -1
    assert abs(direct) == 1
    #
    # create three lists: one with longitude, one with latitude and one with
    # depth
    lo = [pnt.longitude for pnt in edge.points]
    la = [pnt.latitude for pnt in edge.points]
    de = [pnt.depth for pnt in edge.points]
    #
    # initialise the variable used to store the cumulated distance
    cdist = 0.
    #
    # initialise the list with the resampled nodes
    idx = reference_idx
    resampled_cs = [Point(lo[idx], la[idx], de[idx])]
    #
    # set the starting point
    slo = lo[idx]
    sla = la[idx]
    sde = de[idx]
    #
    # get the azimuth of the first segment on the edge in the given direction
    azim = azimuth(lo[idx], la[idx], lo[idx + direct], la[idx + direct])
    #
    # resampling
    old_dst = 1.e10
    while 1:
        #
        # this is a sanity check
        assert idx <= len(lo) - 1
        #
        # check loop exit condition
        if direct > 0 and idx > len(lo) - 1:
            break
        if direct < 0 and idx < 1:
            break
        #
        # compute the distance between the starting point and the next point
        # on the profile
        segment_len = distance(slo, sla, sde, lo[idx + direct],
                               la[idx + direct], de[idx + direct])
        #
        # search for the point
        if cdist + segment_len > sampling_dist:
            #
            # check
            if segment_len > old_dst:
                print(segment_len, '>', old_dst)
                raise ValueError('The segment length is increasing')
            else:
                old_dst = segment_len
            #
            # this is the lenght of the last segment-fraction needed to
            # obtain the sampling distance
            delta = sampling_dist - cdist
            #
            # compute the slope of the last segment and its horizontal length.
            # we need to manage the case of a vertical segment TODO
            segment_hlen = distance(slo, sla, 0., lo[idx + direct],
                                    la[idx + direct], 0.)
            segment_slope = np.arctan((de[idx + direct] - sde) / segment_hlen)
            #
            # horizontal and vertical lenght of delta
            delta_v = delta * np.sin(segment_slope)
            delta_h = delta * np.cos(segment_slope)
            #
            # add a new point to the cross section
            pnts = npoints_towards(slo, sla, sde, azim, delta_h, delta_v, 2)
            #
            # update the starting point
            slo = pnts[0][-1]
            sla = pnts[1][-1]
            sde = pnts[2][-1]
            #
            # checking distance between the reference point and latest point
            # included in the resampled section
            pnt = resampled_cs[-1]
            checkd = distance(slo, sla, sde, pnt.longitude, pnt.latitude,
                              pnt.depth)
            # >>> TOLERANCE
            if (cdist < 1e-2
                    and abs(checkd - sampling_dist) > 0.05 * sampling_dist):
                print(checkd, sampling_dist)
                msg = 'Segment distance different than sampling dst'
                raise ValueError(msg)
            #
            # updating the resample cross-section
            resampled_cs.append(Point(slo, sla, sde))
            #
            #
            tot = distance(lo[idx], la[idx], de[idx], lo[idx + direct],
                           la[idx + direct], de[idx + direct])
            downd = distance(slo, sla, sde, lo[idx], la[idx], de[idx])
            upd = distance(slo, sla, sde, lo[idx + direct], la[idx + direct],
                           de[idx + direct])
            #
            # >>> TOLERANCE
            if abs(tot - (downd + upd)) > tot * 0.05:
                print('     upd, downd, tot', upd, downd, tot)
                print(abs(tot - (downd + upd)))
                raise ValueError('Distances are not matching')
            #
            # reset the cumulative distance
            cdist = 0.
        else:
            # print('aa', cdist, segment_len, sampling_dist)
            # print('  ', idx, len(lo)-1, direct)
            #
            #
            old_dst = 1.e10

            cdist += segment_len
            idx += direct
            slo = lo[idx]
            sla = la[idx]
            sde = de[idx]
            #
            # get the azimuth of the profile
            if idx < len(lo) - 1:
                azim = azimuth(lo[idx], la[idx], lo[idx + direct],
                               la[idx + direct])
            else:
                break
    #
    #
    return resampled_cs
Пример #35
0
 def test_quadrants(self):
     az = geodetic.azimuth(0, 0, [0.01, 0.01, -0.01, -0.01],
                                 [0.01, -0.01, -0.01, 0.01])
     self.assertTrue(numpy.allclose(az, [45, 135, 225, 315]), str(az))
Пример #36
0
 def test_equator(self):
     az = geodetic.azimuth(0, 0, 1, 0)
     self.assertEqual(az, 90)
     az = geodetic.azimuth(1, 0, 0, 0)
     self.assertEqual(az, 270)
Пример #37
0
def test_rupture_depth(interactive=False):
    DIP = 17.0
    WIDTH = 20.0
    GRIDRES = 0.1

    names = ['single', 'double', 'triple',
             'concave', 'concave_simple', 'ANrvSA']
    means = [3.1554422780092461, 2.9224454569459781,
             3.0381968625073563, 2.0522694624400271,
             2.4805390352818755, 2.8740121776209673]
    stds = [2.1895293825074575, 2.0506459673526174,
            2.0244588429154402, 2.0112565876976416,
            2.1599789955270019, 1.6156220309120068]
    xp0list = [np.array([118.3]),
               np.array([10.1, 10.1]),
               np.array([10.1, 10.1, 10.3]),
               np.array([10.9, 10.5, 10.9]),
               np.array([10.9, 10.6]),
               np.array([-76.483, -76.626, -76.757, -76.99, -77.024, -76.925,
                         -76.65, -76.321, -75.997, -75.958])]
    xp1list = [np.array([118.3]),
               np.array([10.1, 10.3]),
               np.array([10.1, 10.3, 10.1]),
               np.array([10.5, 10.9, 11.3]),
               np.array([10.6, 10.9]),
               np.array([-76.626, -76.757, -76.99, -77.024, -76.925, -76.65,
                         -76.321, -75.997, -75.958, -76.006])]
    yp0list = [np.array([34.2]),
               np.array([34.2, 34.5]),
               np.array([34.2, 34.5, 34.8]),
               np.array([34.2, 34.5, 34.8]),
               np.array([35.1, 35.2]),
               np.array([-52.068, -51.377, -50.729, -49.845, -49.192, -48.507,
                         -47.875, -47.478, -47.08, -46.422])]
    yp1list = [np.array([34.5]),
               np.array([34.5, 34.8]),
               np.array([34.5, 34.8, 35.1]),
               np.array([34.5, 34.8, 34.6]),
               np.array([35.2, 35.4]),
               np.array([-51.377, -50.729, -49.845, -49.192, -48.507, -47.875,
                         -47.478, -47.08, -46.422, -45.659])]

    for i in range(0, len(xp0list)):
        xp0 = xp0list[i]
        xp1 = xp1list[i]
        yp0 = yp0list[i]
        yp1 = yp1list[i]
        name = names[i]
        mean_value = means[i]
        std_value = stds[i]

        zp = np.zeros(xp0.shape)
        strike = azimuth(xp0[0], yp0[0], xp1[-1], yp1[-1])
        widths = np.ones(xp0.shape) * WIDTH
        dips = np.ones(xp0.shape) * DIP
        strike = [strike]
        origin = Origin({'eventsourcecode': 'test', 'lat': 0, 'lon': 0,
                         'depth': 5.0, 'mag': 7.0})
        rupture = QuadRupture.fromTrace(
            xp0, yp0, xp1, yp1, zp, widths, dips, origin, strike=strike)

        # make a grid of points over both quads, ask for depths
        ymin = np.nanmin(rupture.lats)
        ymax = np.nanmax(rupture.lats)
        xmin = np.nanmin(rupture.lons)
        xmax = np.nanmax(rupture.lons)

        xmin = np.floor(xmin * (1 / GRIDRES)) / (1 / GRIDRES)
        xmax = np.ceil(xmax * (1 / GRIDRES)) / (1 / GRIDRES)
        ymin = np.floor(ymin * (1 / GRIDRES)) / (1 / GRIDRES)
        ymax = np.ceil(ymax * (1 / GRIDRES)) / (1 / GRIDRES)
        geodict = GeoDict.createDictFromBox(
            xmin, xmax, ymin, ymax, GRIDRES, GRIDRES)
        nx = geodict.nx
        ny = geodict.ny
        depths = np.zeros((ny, nx))
        for row in range(0, ny):
            for col in range(0, nx):
                lat, lon = geodict.getLatLon(row, col)
                depth = rupture.getDepthAtPoint(lat, lon)
                depths[row, col] = depth

        np.testing.assert_almost_equal(np.nanmean(depths), mean_value)
        np.testing.assert_almost_equal(np.nanstd(depths), std_value)

        if interactive:
            fig, axes = plt.subplots(nrows=2, ncols=1)
            ax1, ax2 = axes
            xdata = np.append(xp0, xp1[-1])
            ydata = np.append(yp0, yp1[-1])
            plt.sca(ax1)
            plt.plot(xdata, ydata, 'b')
            plt.sca(ax2)
            im = plt.imshow(depths, cmap='viridis_r')  # noqa
            ch = plt.colorbar()  # noqa
            fname = os.path.join(os.path.expanduser('~'),
                                 'quad_%s_test.png' % name)
            print('Saving image for %s quad test... %s' % (name, fname))
            plt.savefig(fname)
            plt.close()
Пример #38
0
def rsmpl_unsure(ix, iy, sampling_dist):
    direct = 1
    idx = 0
    #
    # create three lists: one with longitude, one with latitude and one with
    # depth
    lo = list(ix)
    la = list(iy)
    de = list(numpy.zeros_like(ix))
    #
    # initialise the variable used to store the cumulated distance
    cdist = 0.
    #
    # set the starting point
    slo = lo[idx]
    sla = la[idx]
    sde = de[idx]
    #
    # get the azimuth of the first segment on the edge in the given direction
    azim = azimuth(lo[idx], la[idx], lo[idx + direct], la[idx + direct])
    #
    # initialise the list with the resampled nodes
    resampled_cs = [[lo[idx], la[idx], azim]]
    #
    # resampling
    while 1:
        #
        # this is a sanity check
        assert idx <= len(lo) - 1
        #
        # check loop exit condition
        if direct > 0 and idx > len(lo) - 1:
            break
        #
        # compute the distance between the starting point and the next point
        # on the profile
        segment_len = distance(slo, sla, sde, lo[idx + direct],
                               la[idx + direct], de[idx + direct])
        #
        # search for the point
        if cdist + segment_len > sampling_dist:
            #
            # this is the lenght of the last segment-fraction needed to
            # obtain the sampling distance
            delta = sampling_dist - cdist
            #
            # add a new point to the cross section
            pnts = npoints_towards(slo, sla, sde, azim, delta, 0., 2)
            #
            # update the starting point
            slo = pnts[0][-1]
            sla = pnts[1][-1]
            sde = pnts[2][-1]
            resampled_cs.append([slo, sla, azim])
            #
            # reset the cumulative distance
            cdist = 0.
        else:
            cdist += segment_len
            idx += direct
            slo = lo[idx]
            sla = la[idx]
            sde = de[idx]
            #
            # get the azimuth of the profile
            if idx < len(lo) - 1:
                azim = azimuth(lo[idx], la[idx], lo[idx + direct],
                               la[idx + direct])
            else:
                break
    # code.interact(local=locals())
    return numpy.array(resampled_cs)
Пример #39
0
def _resample_profile(line, sampling_dist):
    # TODO split this function into smaller components.
    """
    :parameter line:
        An instance of :class:`openquake.hazardlib.geo.line.Line`
    :parameter sampling_dist:
        A scalar definining the distance [km] used to sample the profile
    :returns:
        An instance of :class:`openquake.hazardlib.geo.line.Line`
    """
    lo = [pnt.longitude for pnt in line.points]
    la = [pnt.latitude for pnt in line.points]
    de = [pnt.depth for pnt in line.points]

    # Set projection
    g = Geod(ellps='WGS84')

    # Add a tolerance length to the last point of the profile
    # check that final portion of the profile is not vertical
    if abs(lo[-2] - lo[-1]) > 1e-5 and abs(la[-2] - la[-1]) > 1e-5:
        az12, _, odist = g.inv(lo[-2], la[-2], lo[-1], la[-1])
        odist /= 1e3
        slope = np.arctan((de[-1] - de[-2]) / odist)
        hdist = TOL * sampling_dist * np.cos(slope)
        vdist = TOL * sampling_dist * np.sin(slope)
        endlon, endlat, _ = g.fwd(lo[-1], la[-1], az12, hdist * 1e3)
        lo[-1] = endlon
        la[-1] = endlat
        de[-1] = de[-1] + vdist
        az12, _, odist = g.inv(lo[-2], la[-2], lo[-1], la[-1])

        # Checking
        odist /= 1e3
        slopec = np.arctan((de[-1] - de[-2]) / odist)
        assert abs(slope - slopec) < 1e-3
    else:
        de[-1] = de[-1] + TOL * sampling_dist

    # Initialise the cumulated distance
    cdist = 0.

    # Get the azimuth of the profile
    azim = azimuth(lo[0], la[0], lo[-1], la[-1])

    # Initialise the list with the resampled nodes
    idx = 0
    resampled_cs = [Point(lo[idx], la[idx], de[idx])]

    # Set the starting point
    slo = lo[idx]
    sla = la[idx]
    sde = de[idx]

    # Resampling
    while 1:

        # Check loop exit condition
        if idx > len(lo) - 2:
            break

        # Compute the distance between the starting point and the next point
        # on the profile
        segment_len = distance(slo, sla, sde, lo[idx + 1], la[idx + 1],
                               de[idx + 1])

        # Search for the point
        if cdist + segment_len > sampling_dist:

            # This is the lenght of the last segment-fraction needed to
            # obtain the sampling distance
            delta = sampling_dist - cdist

            # Compute the slope of the last segment and its horizontal length.
            # We need to manage the case of a vertical segment TODO
            segment_hlen = distance(slo, sla, 0., lo[idx + 1], la[idx + 1], 0.)
            if segment_hlen > 1e-5:
                segment_slope = np.arctan((de[idx + 1] - sde) / segment_hlen)
            else:
                segment_slope = 90.

            # Horizontal and vertical lenght of delta
            delta_v = delta * np.sin(segment_slope)
            delta_h = delta * np.cos(segment_slope)

            # Add a new point to the cross section
            pnts = npoints_towards(slo, sla, sde, azim, delta_h, delta_v, 2)

            # Update the starting point
            slo = pnts[0][-1]
            sla = pnts[1][-1]
            sde = pnts[2][-1]
            resampled_cs.append(Point(slo, sla, sde))

            # Reset the cumulative distance
            cdist = 0.

        else:
            cdist += segment_len
            idx += 1
            slo = lo[idx]
            sla = la[idx]
            sde = de[idx]

    # Check the distances along the profile
    coo = [[pnt.longitude, pnt.latitude, pnt.depth] for pnt in resampled_cs]
    coo = np.array(coo)
    for i in range(0, coo.shape[0] - 1):
        dst = distance(coo[i, 0], coo[i, 1], coo[i, 2], coo[i + 1, 0],
                       coo[i + 1, 1], coo[i + 1, 2])
        if abs(dst - sampling_dist) > 0.1 * sampling_dist:
            raise ValueError('Wrong distance between points along the profile')

    return Line(resampled_cs)