Ejemplo n.º 1
0
def _Geodesic(points, closed, datum, line, wrap):
    # Compute the area or perimeter of a polygon/-line
    # using the GeographicLib package, iff installed
    try:
        from geographiclib.geodesic import Geodesic
    except ImportError:
        raise ImportError('no %s' % ('geographiclib', ))

    if not wrap:  # capability LONG_UNROLL always set
        raise ValueError('%s invalid: %s' % ('wrap', wrap))

    _, points = polygon(points,
                        closed=closed)  # base=LatLonEllipsoidalBase(0, 0)

    E = datum.ellipsoid
    g = Geodesic(E.a, E.f).Polygon(line)

    # note, lon deltas are unrolled, by default
    for p in points:
        g.AddPoint(p.lat, p.lon)
    if line and closed:
        p = points[0]
        g.AddPoint(p.lat, p.lon)

    # g.Compute returns (number_of_points, perimeter, signed area)
    return g.Compute(False, True)[1 if line else 2]
Ejemplo n.º 2
0
def get_gc_positions(start, end):
    """
    Get positions along a Great Circle to enable plotting.
    I couldn't do this with Basemap as we have to instantiate
    first and we don't know the map boundaries until we have the GC
    positions.

    :param start: The start position
    :type start: tuple
    :param end: The end position
    :type end: tuple
    :return: list of positions
    :rtype: list of dict
    """
    positions = []
    spacing = 100000  # Positions 100km apart
    geoid = Geodesic(Constants.WGS84_a, Constants.WGS84_f)
    gc = geoid.InverseLine(
            start[0], start[1],
            end[0], end[1]
    )

    n = math.ceil(gc.s13 / spacing)

    for i in range(n + 1):
        s = min(spacing * i, gc.s13)
        result = gc.Position(s, Geodesic.STANDARD | Geodesic.LONG_UNROLL)
        position = {
            'Lat': result['lat2'],
            'Lon': result['lon2']
        }
        positions.append(position)

    return positions
Ejemplo n.º 3
0
def getEndpoint(lat1, lon1, d1, d2):
    bearing = random.uniform(0, 360)
    dist = random.uniform(d1, d2)
    radius = (d2-d1)/2
    geod = Geodesic(Constants.WGS84_a, Constants.WGS84_f)
    d = geod.Direct(lat1, lon1, bearing, dist)
    return d['lon2'], d['lat2'], radius
def get_endpoint(lat1, lon1, bearing, d):


    geod = Geodesic(Constants.WGS84_a, Constants.WGS84_f)
    d = geod.Direct(lat1, lon1, bearing, d)#* 1852.0)

    return d['lat2'], d['lon2']
Ejemplo n.º 5
0
 def test_GeodSolve12(self):
     # Check fix for inverse geodesics on extreme prolate/oblate
     # ellipsoids Reported 2012-08-29 Stefan Guenther
     # <*****@*****.**>; fixed 2012-10-07
     geod = Geodesic(89.8, -1.83)
     inv = geod.Inverse(0, 0, -10, 160)
     self.assertAlmostEqual(inv["azi1"], 120.27, delta=1e-2)
     self.assertAlmostEqual(inv["azi2"], 105.15, delta=1e-2)
     self.assertAlmostEqual(inv["s12"], 266.7, delta=1e-1)
Ejemplo n.º 6
0
def calc_dist_azi(source_latitude_in_deg, source_longitude_in_deg,
                  receiver_latitude_in_deg, receiver_longitude_in_deg,
                  radius_of_planet_in_km, flattening_of_planet):
    """
    Given the source and receiver location, calculate the azimuth from the
    source to the receiver at the source, the backazimuth from the receiver
    to the source at the receiver and distance between the source and receiver.

    :param source_latitude_in_deg: Source location latitude in degrees
    :type source_latitude_in_deg: float
    :param source_longitude_in_deg: Source location longitude in degrees
    :type source_longitude_in_deg: float
    :param receiver_latitude_in_deg: Receiver location latitude in degrees
    :type receiver_latitude_in_deg: float
    :param receiver_longitude_in_deg: Receiver location longitude in degrees
    :type receiver_longitude_in_deg: float
    :param radius_of_planet_in_km: Radius of the planet in km
    :type radius_of_planet_in_km: float
    :param flattening_of_planet: Flattening of planet (0 for a sphere)
    :type receiver_longitude_in_deg: float

    :returns: distance_in_deg (in degrees), source_receiver_azimuth (in
              degrees) and receiver_to_source_backazimuth (in degrees).
    :rtype: tuple of three floats
    """
    if geodetics.HAS_GEOGRAPHICLIB:
        ellipsoid = Geodesic(a=radius_of_planet_in_km * 1000.0,
                             f=flattening_of_planet)
        g = ellipsoid.Inverse(source_latitude_in_deg, source_longitude_in_deg,
                              receiver_latitude_in_deg,
                              receiver_longitude_in_deg)
        distance_in_deg = g['a12']
        source_receiver_azimuth = g['azi1'] % 360
        receiver_to_source_backazimuth = (g['azi2'] + 180) % 360

    else:
        # geographiclib is not installed - use obspy/geodetics
        values = gps2dist_azimuth(source_latitude_in_deg,
                                  source_longitude_in_deg,
                                  receiver_latitude_in_deg,
                                  receiver_longitude_in_deg,
                                  a=radius_of_planet_in_km * 1000.0,
                                  f=flattening_of_planet)
        distance_in_km = values[0] / 1000.0
        source_receiver_azimuth = values[1] % 360
        receiver_to_source_backazimuth = values[2] % 360
        # NB - km2deg assumes spherical planet... generate a warning
        if flattening_of_planet != 0.0:
            msg = "Assuming spherical planet when calculating epicentral " + \
                  "distance. Install the Python module 'geographiclib' " + \
                  "to solve this."
            warnings.warn(msg)
        distance_in_deg = kilometer2degrees(distance_in_km,
                                            radius=radius_of_planet_in_km)
    return (distance_in_deg, source_receiver_azimuth,
            receiver_to_source_backazimuth)
Ejemplo n.º 7
0
 def test_GeodSolve2(self):
     # Check fix for antipodal prolate bug found 2010-09-04
     geod = Geodesic(6.4e6, -1 / 150.0)
     inv = geod.Inverse(0.07476, 0, -0.07476, 180)
     self.assertAlmostEqual(inv["azi1"], 90.00078, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 90.00078, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20106193, delta=0.5)
     inv = geod.Inverse(0.1, 0, -0.1, 180)
     self.assertAlmostEqual(inv["azi1"], 90.00105, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 90.00105, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20106193, delta=0.5)
Ejemplo n.º 8
0
 def ellipsoid(self, acronym):
     '''Return the ellipsoid associated with the acronym'''
     if acronym == 'WGS84':
         return (Geodesic.WGS84)
     elif acronym in self.acronymList:
         param = self.acronymList[acronym].parameters
         return(Geodesic(param.semiMajor, 1.0 / param.inverseFlattening))
     elif acronym in historical_ellipsoids:
         return(Geodesic(historical_ellipsoids[acronym][1], 1.0 / historical_ellipsoids[acronym][2]))
     else:
         return(None)
    def measure(self, a, b):
        a, b = Point(a), Point(b)
        lat1, lon1 = a.latitude, a.longitude
        lat2, lon2 = b.latitude, b.longitude

        if not (isinstance(self.geod, Geodesic) and self.geod.a
                == self.ELLIPSOID[0] and self.geod.f == self.ELLIPSOID[2]):
            self.geod = Geodesic(self.ELLIPSOID[0], self.ELLIPSOID[2])

        s12 = self.geod.Inverse(lat1, lon1, lat2, lon2,
                                Geodesic.DISTANCE)['s12']

        return s12
Ejemplo n.º 10
0
def calc_dist(source_latitude_in_deg, source_longitude_in_deg,
              receiver_latitude_in_deg, receiver_longitude_in_deg,
              radius_of_earth_in_km, flattening_of_earth):
    """
    Given the source and receiver location, calculate the azimuth and distance.

    :param source_latitude_in_deg: Source location latitude in degrees
    :type source_latitude_in_deg: float
    :param source_longitude_in_deg: Source location longitude in degrees
    :type source_longitude_in_deg: float
    :param receiver_latitude_in_deg: Receiver location latitude in degrees
    :type receiver_latitude_in_deg: float
    :param receiver_longitude_in_deg: Receiver location longitude in degrees
    :type receiver_longitude_in_deg: float
    :param radius_of_earth_in_km: Radius of the Earth in km
    :type radius_of_earth_in_km: float
    :param flattening_of_earth: Flattening of Earth (0 for a sphere)
    :type receiver_longitude_in_deg: float

    :return: distance_in_deg
    :rtype: float
    """
    if geodetics.HAS_GEOGRAPHICLIB:
        ellipsoid = Geodesic(a=radius_of_earth_in_km*1000.0,
                             f=flattening_of_earth)
        g = ellipsoid.Inverse(source_latitude_in_deg,
                              source_longitude_in_deg,
                              receiver_latitude_in_deg,
                              receiver_longitude_in_deg)
        distance_in_deg = g['a12']

    else:
        # geographiclib is not installed - use obspy/geodetics
        values = gps2dist_azimuth(source_latitude_in_deg,
                                  source_longitude_in_deg,
                                  receiver_latitude_in_deg,
                                  receiver_longitude_in_deg,
                                  a=radius_of_earth_in_km*1000.0,
                                  f=flattening_of_earth)
        distance_in_km = values[0]/1000.0
        # NB - km2deg assumes spherical Earth... generate a warning
        if flattening_of_earth != 0.0:
            msg = "Assuming spherical Earth when calculating epicentral " + \
                  "distance. Install the Python module 'geographiclib' " + \
                  "to solve this."
            warnings.warn(msg)
        distance_in_deg = kilometer2degrees(distance_in_km,
                                            radius=radius_of_earth_in_km)
    return distance_in_deg
Ejemplo n.º 11
0
class MocLocalizer(GeodesicLocalizer):
    """
    A localizer for the MOC observations (subclass of
    :py:class:`GeodesicLocalizer`)
    """

    DEFAULT_RESOLUTION_M = 1e-3
    """
    Sets the default resolution for MOC localization
    """

    BODY = Geodesic(MARS_RADIUS_M, 0.0)
    """
    Uses a Geodesic model for MOC that assumes Mars is spherical, which seems to
    work better in practice.
    """

    def __init__(self, metadata):
        """
        :param metadata:
            "moc" :py:class:`~pdsc.metadata.PdsMetadata` object
        """
        super(MocLocalizer, self).__init__(
            metadata.lines / 2.0, metadata.samples / 2.0,
            metadata.center_latitude, metadata.center_longitude,
            metadata.lines, metadata.samples,
            metadata.image_height / metadata.lines,
            metadata.image_width / metadata.samples,
            metadata.north_azimuth, 1
        )
Ejemplo n.º 12
0
class CtxLocalizer(GeodesicLocalizer):
    """
    A localizer for the CTX instrument (subclass of
    :py:class:`GeodesicLocalizer`)
    """

    DEFAULT_RESOLUTION_M = 1e-3
    """
    Sets the default resolution for CTX localization
    """

    BODY = Geodesic(MARS_RADIUS_M, 0.0) # Works better assuming sphere
    """
    Uses a Geodesic model for CTX that assumes Mars is spherical, which seems to
    work better in practice.
    """

    def __init__(self, metadata):
        """
        :param metadata:
            "ctx" :py:class:`~pdsc.metadata.PdsMetadata` object
        """
        flipped_na = (180 - metadata.north_azimuth
            if metadata.usage_note == 'F' else metadata.north_azimuth)
        super(CtxLocalizer, self).__init__(
            metadata.lines / 2.0, metadata.samples / 2.0,
            metadata.center_latitude, metadata.center_longitude,
            metadata.lines, metadata.samples,
            metadata.image_height / metadata.lines,
            metadata.image_width / metadata.samples,
            flipped_na, -1
        )
Ejemplo n.º 13
0
    def __init__(self):
        """Initiate all instances of the classes that are needed for the simulation, and start PyGame.

        Raises:
            Exception: If you fail to choose a valid simulation setting, an Exception is raised.
        """
        while True:
            try:
                string = str(
                    input('Do you want to take in live GNSS-signals? (y/n)\t'))
                if string in ('y', 'yes'):
                    self.test_mode_on = False
                    try:
                        self.gps = serial.Serial('/dev/ttyACM0', baudrate=9600)
                    except Exception:
                        print(
                            "The port cannot be opened. Check that your unit is connected to the port you're opening."
                        )
                        exit()
                elif string in ('n', 'no'):
                    self.test_mode_on = True
                else:
                    raise Exception
            except Exception:
                print('Please type "y" or "n".')
            else:
                break
        self.clock = pg.time.Clock()
        self.the_arrow = Arrow()
        self.map = Map(60.75, 11.99)
        self.the_text = Text()
        self.earth = Geodesic(cf.R_E, 0)
        self.out_q = mp.Manager().Value('i', [0.0, 0.0])
        self.NS = self.map.center_y
        self.EW = self.map.center_x
        self.the_background = pg.image.load('background.jpg')
        self.process = False
        self.background = False
        self.dot_x, self.dot_y = self.the_arrow.two_d_pos.x, self.the_arrow.two_d_pos.y
        self.pastNS, self.pastEW = self.NS, self.EW
        # Create a new log file for this session.
        with open("log.txt", "w") as log:
            log.write("")
        try:
            what_os = platform.system()
            if what_os == 'Linux':
                self.g_earth = subprocess.Popen('google-earth-pro')
            elif what_os == 'Darwin':
                subprocess.call([
                    "/usr/bin/open", "-n", "-a",
                    "/Applications/Google Earth Pro.app"
                ])
        except Exception:
            pass
        # Wait for Google Earth Pro to open so that the PyGame window opens as the top layer.
        time.sleep(5)
        pg.init()
        pg.display.set_caption("Real time spoofer")
        self.screen = pg.display.set_mode((cf.SCREEN_WIDTH, cf.SCREEN_HEIGHT))
Ejemplo n.º 14
0
  def ArcPosition(self, a12,
                  outmask = GeodesicCapability.LATITUDE |
                  GeodesicCapability.LONGITUDE | GeodesicCapability.AZIMUTH |
                  GeodesicCapability.DISTANCE):
    """Return the point a spherical arc length a12 along the geodesic line.
    Return a dictionary with (some) of the following entries:

      lat1 latitude of point 1
      lon1 longitude of point 1
      azi1 azimuth of line at point 1
      lat2 latitude of point 2
      lon2 longitude of point 2
      azi2 azimuth of line at point 2
      s12 distance from 1 to 2
      a12 arc length on auxiliary sphere from 1 to 2
      m12 reduced length of geodesic
      M12 geodesic scale 2 relative to 1
      M21 geodesic scale 1 relative to 2
      S12 area between geodesic and equator

    outmask determines which fields get included and if outmask is
    omitted, then only the basic geodesic fields are computed.  The
    LONG_UNROLL bit unrolls the longitudes (instead of reducing them to
    the range [-180,180)).  The mask is an or'ed combination of the
    following values

      Geodesic.LATITUDE
      Geodesic.LONGITUDE
      Geodesic.AZIMUTH
      Geodesic.DISTANCE
      Geodesic.REDUCEDLENGTH
      Geodesic.GEODESICSCALE
      Geodesic.AREA
      Geodesic.ALL (all of the above)
      Geodesic.LONG_UNROLL

    The default value of outmask is LATITUDE | LONGITUDE | AZIMUTH |
    DISTANCE.

    """

    from geographiclib.geodesic import Geodesic
    Geodesic.CheckDistance(a12)
    result = {'lat1': self._lat1,
              'lon1': self._lon1 if outmask & Geodesic.LONG_UNROLL else
              Math.AngNormalize(self._lon1),
              'azi1': self._azi1, 'a12': a12}
    a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 = self.GenPosition(
      True, a12, outmask)
    outmask &= Geodesic.OUT_MASK
    if outmask & Geodesic.DISTANCE: result['s12'] = s12
    if outmask & Geodesic.LATITUDE: result['lat2'] = lat2
    if outmask & Geodesic.LONGITUDE: result['lon2'] = lon2
    if outmask & Geodesic.AZIMUTH: result['azi2'] = azi2
    if outmask & Geodesic.REDUCEDLENGTH: result['m12'] = m12
    if outmask & Geodesic.GEODESICSCALE:
      result['M12'] = M12; result['M21'] = M21
    if outmask & Geodesic.AREA: result['S12'] = S12
    return result
Ejemplo n.º 15
0
def gps2dist_azimuth(lat1, lon1, lat2, lon2, a=WGS84_A, f=WGS84_F):
    """
    Computes the distance between two geographic points on the WGS84
    ellipsoid and the forward and backward azimuths between these points.

    :param lat1: Latitude of point A in degrees (positive for northern,
        negative for southern hemisphere)
    :param lon1: Longitude of point A in degrees (positive for eastern,
        negative for western hemisphere)
    :param lat2: Latitude of point B in degrees (positive for northern,
        negative for southern hemisphere)
    :param lon2: Longitude of point B in degrees (positive for eastern,
        negative for western hemisphere)
    :param a: Radius of Earth in m. Uses the value for WGS84 by default.
    :param f: Flattening of Earth. Uses the value for WGS84 by default.
    :return: (Great circle distance in m, azimuth A->B in degrees,
        azimuth B->A in degrees)

    .. note::
        This function will check if you have installed the Python module
        `geographiclib <http://geographiclib.sf.net>`_ - a very fast module
        for converting between geographic, UTM, UPS, MGRS, and geocentric
        coordinates, for geoid calculations, and for solving geodesic problems.
        Otherwise the locally implemented Vincenty's Inverse formulae
        (:func:`obspy.core.util.geodetics.calc_vincenty_inverse`) is used which
        has known limitations for two nearly antipodal points and is ca. 4x
        slower.
    """
    if HAS_GEOGRAPHICLIB:
        if lat1 > 90 or lat1 < -90:
            msg = "Latitude of Point 1 out of bounds! (-90 <= lat1 <=90)"
            raise ValueError(msg)
        if lat2 > 90 or lat2 < -90:
            msg = "Latitude of Point 2 out of bounds! (-90 <= lat2 <=90)"
            raise ValueError(msg)
        result = Geodesic(a=a, f=f).Inverse(lat1, lon1, lat2, lon2)
        azim = result['azi1']
        if azim < 0:
            azim += 360
        bazim = result['azi2'] + 180
        return (result['s12'], azim, bazim)
    else:
        try:
            values = calc_vincenty_inverse(lat1, lon1, lat2, lon2, a, f)
            if np.alltrue(np.isnan(values)):
                raise StopIteration
            return values
        except StopIteration:
            msg = ("Catching unstable calculation on antipodes. "
                   "The currently used Vincenty's Inverse formulae "
                   "has known limitations for two nearly antipodal points. "
                   "Install the Python module 'geographiclib' to solve this "
                   "issue.")
            warnings.warn(msg)
            return (20004314.5, 0.0, 0.0)
        except ValueError as e:
            raise e
Ejemplo n.º 16
0
    def Position(self,
                 s12,
                 outmask=GeodesicCapability.LATITUDE
                 | GeodesicCapability.LONGITUDE | GeodesicCapability.AZIMUTH):
        """
    Return the point a distance s12 along the geodesic line.  Return
    a dictionary with (some) of the following entries:

      lat1 latitude of point 1
      lon1 longitude of point 1
      azi1 azimuth of line at point 1
      lat2 latitude of point 2
      lon2 longitude of point 2
      azi2 azimuth of line at point 2
      s12 distance from 1 to 2
      a12 arc length on auxiliary sphere from 1 to 2
      m12 reduced length of geodesic
      M12 geodesic scale 2 relative to 1
      M21 geodesic scale 1 relative to 2
      S12 area between geodesic and equator

    outmask determines which fields get included and if outmask is
    omitted, then only the basic geodesic fields are computed.  The mask
    is an or'ed combination of the following values

      Geodesic.LATITUDE
      Geodesic.LONGITUDE
      Geodesic.AZIMUTH
      Geodesic.DISTANCE
      Geodesic.REDUCEDLENGTH
      Geodesic.GEODESICSCALE
      Geodesic.AREA
      Geodesic.ALL
    """

        from geographiclib.geodesic import Geodesic
        Geodesic.CheckDistance(s12)
        result = {
            'lat1': self._lat1,
            'lon1': self._lon1,
            'azi1': self._azi1,
            's12': s12
        }
        a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 = self.GenPosition(
            False, s12, outmask)
        outmask &= Geodesic.OUT_ALL
        result['a12'] = a12
        if outmask & Geodesic.LATITUDE: result['lat2'] = lat2
        if outmask & Geodesic.LONGITUDE: result['lon2'] = lon2
        if outmask & Geodesic.AZIMUTH: result['azi2'] = azi2
        if outmask & Geodesic.REDUCEDLENGTH: result['m12'] = m12
        if outmask & Geodesic.GEODESICSCALE:
            result['M12'] = M12
            result['M21'] = M21
        if outmask & Geodesic.AREA: result['S12'] = S12
        return result
Ejemplo n.º 17
0
    def destination(self, point, bearing, distance=None):
        point = Point(point)
        lat1 = point.latitude
        lon1 = point.longitude
        azi1 = bearing

        if distance is None:
            distance = self
        if isinstance(distance, Distance):
            distance = distance.kilometers

        if not (isinstance(self.geod, Geodesic) and self.geod.a
                == self.ELLIPSOID[0] and self.geod.f == self.ELLIPSOID[2]):
            self.geod = Geodesic(self.ELLIPSOID[0], self.ELLIPSOID[2])

        r = self.geod.Direct(lat1, lon1, azi1, distance,
                             Geodesic.LATITUDE | Geodesic.LONGITUDE)

        return Point(r['lat2'], r['lon2'])
Ejemplo n.º 18
0
	def calculate_distances(self):
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		# Calculates the distance between all neuron positions *along the surface* (using geodesic distances) ~
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		# The geographiclib.geodesic module is needed to calculate geodesic distance along the surface of a spheroid.
		# - Library is based on Karney 2013 (seemingly written by Karney himself)
		# - See https://geographiclib.sourceforge.io/1.48/python/
		try:
			from geographiclib.geodesic import Geodesic
		except:
			exitOnNetworkGeometryError("The geographiclib.geodesic module is needed to calculate geodesic distance along the surface of a spheroid, but this module could not be imported.")
		# The Geodesic object constructor has two parameters:
		#	- a: the equatorial (xy) radius of the ellipsoid
		#	- f: the flattening of the ellipsoid. b = a(1-f) --> f = 1 - b/a  (where b is the polar (z) axis)
		a 			= self.r_xy
		b 			= self.r_z
		f 			= 1.0 - b/a
		geodesic 	= Geodesic(a, f)

		# Convert parametric coordinates (theta(lon), phi(lat)) from radian to degree angles
		degreecoords	= numpy.degrees(self.parametricCoords)
		# print self.coordinates
		# print "degrees"
		# print degreecoords

		for i in range(self.N):
			for j in range(self.N):
				# Geodesic.Inverse(...) calculates the geodesic distance between two points along a spheroid surface.
				#	arguments: 	(lat1, lon1, lat2, lon2, outmask=1025)
				#				- expects lat1, lon1, lat2, lon2 to be latitude(theta)/longitude(phi) angles in degrees
				#				- outmask=1025 tells it to return the distance measure
				#	returns:	a Geodesic dictionary including key-value pair 's12', which is the calculated distance between the points.
				lat1	= degreecoords[i][1]
				lon1	= degreecoords[i][0]
				lat2	= degreecoords[j][1]
				lon2	= degreecoords[j][0]
				# print geodesic.Inverse(lat1, lon1, lat2, lon2, outmask=1025)
				# print "lat" + str(lat1) + " lon" + str(lon1) + "  ::  lat" + str(lat2) + " lon" + str(lon2)
				dist 	= geodesic.Inverse(lat1, lon1, lat2, lon2, outmask=1025)['s12']
				self.distances[i,j]	= dist
Ejemplo n.º 19
0
def find_corners(lat_lon_file,
                 tolerance=1,
                 min_angle=np.pi * 0.22,
                 geoid=Geodesic(6371., 0.),
                 lsz=None):
    """
    Credit to unutbu from Stack Overflow
    (https://stackoverflow.com/questions/14631776/calculate-turning-points-pivot-points-in-trajectory-path)
    Modifed by PySkew author Kevin Gaastra to run on a sphere

    Runs the Ramer-Douglas-Peucker algorithm to simplify the lat, lon path stored in the specified lat_lon_file.
    It prints out the number of points in the origional path and the number of points in the simplified path.
    Then calculates the points in the simplified path that constitute a turn greater than min_angle.
    
    Parameters
    ----------
    lat_lon_file - file containing lattitude and longitude in that order in columns seperated by whitespace
    tolerance - the tolerance of the RDP algorithm more tolerance=less points kept (default=1)
    min_angle - angle in radians that should constitute a "significant" change in heading
    
    Returns
    -------
    Tuple - (numpy array number_of_points_in_origionalx2 containing the origional data, 
    number_of_points_in_simplifiedx2 containing the simplified data, and an array containing the locations in
    the simplified data where turns exceeding min_angle are found)
    """
    path = os.path.expanduser(lat_lon_file)
    points = np.genfromtxt(path)
    print("original number of points = %d" % len(points))

    # Use the Ramer-Douglas-Peucker algorithm to simplify the path
    # http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm
    # Python implementation: https://github.com/sebleier/RDP/
    simplified = np.array(
        rdp.rdp(points.tolist(), tolerance, dist=create_geo_gc_dist(geoid)))
    #    simplified = np.array(kmg9_rdp(points.tolist(), tolerance, create_geo_gc_dist(geoid),lsz=lsz))

    print("simplified number of points = %d" % len(simplified))

    # compute the direction vectors on the simplified curve
    #    directions = np.diff(simplified, axis=0)
    theta = geo_angle(simplified)
    # Select the index of the points with the greatest theta
    # Large theta is associated with greatest change in direction.
    idx = np.where(theta > np.rad2deg(min_angle))[0] + 1
    print("number of significant turns = %d" % len(idx))

    return points, simplified, idx
Ejemplo n.º 20
0
def geo_angle(points, geoid=Geodesic(6371., 0.)):
    """
    Returns the angles between two great circles for a set of points

    Parameters
    ----------
    points : 2D-Ndarray
        lat-lon array of points on a sphere between which to calculate angles

    Returns
    ----------
    angles : 1D-Ndarray
        angles between the great circles defined by points of shape (N-1,), with each value between 0 and 180 degrees.
    """
    return np.abs(
        np.diff(
            list(
                map(lambda a, b, c, d: geoid.Inverse(a, b, c, d)["azi1"],
                    points[:-1, 0], points[:-1,
                                           1], points[1:, 0], points[1:, 1]))))
Ejemplo n.º 21
0
def globe_distance(
    lat1: float,
    lon1: float,
    lat2: float,
    lon2: float,
    round_to: int = 5,
) -> float:
    """
    Compute the great circle distance in km between two points on Earth.
    
    """

    _check_latitude(lat1, 'lat1')
    _check_latitude(lat2, 'lat2')
    result = Geodesic(a=WGS84_A, f=WGS84_F).Inverse(lat1, lon1, lat2, lon2)

    # Used to cope with minor floating point differences between operating
    # systems that we don't care about for ESCI451
    if round_to:
        return round(result['s12'] / 1000., round_to)
    else:
        return result['s12'] / 1000.
Ejemplo n.º 22
0
    def __init__(self, geod, lat1, lon1, azi1, caps=GeodesicCapability.ALL):
        from geographiclib.geodesic import Geodesic
        self._a = geod._a
        self._f = geod._f
        self._b = geod._b
        self._c2 = geod._c2
        self._f1 = geod._f1
        self._caps = caps | Geodesic.LATITUDE | Geodesic.AZIMUTH

        # Guard against underflow in salp0
        azi1 = Geodesic.AngRound(Math.AngNormalize(azi1))
        lon1 = Math.AngNormalize(lon1)
        self._lat1 = lat1
        self._lon1 = lon1
        self._azi1 = azi1
        # alp1 is in [0, pi]
        alp1 = azi1 * Math.degree
        # Enforce sin(pi) == 0 and cos(pi/2) == 0.  Better to face the ensuing
        # problems directly than to skirt them.
        self._salp1 = 0 if azi1 == -180 else math.sin(alp1)
        self._calp1 = 0 if abs(azi1) == 90 else math.cos(alp1)
        # real cbet1, sbet1, phi
        phi = lat1 * Math.degree
        # Ensure cbet1 = +epsilon at poles
        sbet1 = self._f1 * math.sin(phi)
        cbet1 = Geodesic.tiny_ if abs(lat1) == 90 else math.cos(phi)
        sbet1, cbet1 = Geodesic.SinCosNorm(sbet1, cbet1)
        self._dn1 = math.sqrt(1 + geod._ep2 * Math.sq(sbet1))

        # Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
        self._salp0 = self._salp1 * cbet1  # alp0 in [0, pi/2 - |bet1|]
        # Alt: calp0 = hypot(sbet1, calp1 * cbet1).  The following
        # is slightly better (consider the case salp1 = 0).
        self._calp0 = math.hypot(self._calp1, self._salp1 * sbet1)
        # Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
        # sig = 0 is nearest northward crossing of equator.
        # With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
        # With bet1 =  pi/2, alp1 = -pi, sig1 =  pi/2
        # With bet1 = -pi/2, alp1 =  0 , sig1 = -pi/2
        # Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
        # With alp0 in (0, pi/2], quadrants for sig and omg coincide.
        # No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
        # With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
        self._ssig1 = sbet1
        self._somg1 = self._salp0 * sbet1
        self._csig1 = self._comg1 = (cbet1 * self._calp1
                                     if sbet1 != 0 or self._calp1 != 0 else 1)
        # sig1 in (-pi, pi]
        self._ssig1, self._csig1 = Geodesic.SinCosNorm(self._ssig1,
                                                       self._csig1)
        # No need to normalize
        # self._somg1, self._comg1 = Geodesic.SinCosNorm(self._somg1, self._comg1)

        self._k2 = Math.sq(self._calp0) * geod._ep2
        eps = self._k2 / (2 * (1 + math.sqrt(1 + self._k2)) + self._k2)

        if self._caps & Geodesic.CAP_C1:
            self._A1m1 = Geodesic.A1m1f(eps)
            self._C1a = range(Geodesic.nC1_ + 1)
            Geodesic.C1f(eps, self._C1a)
            self._B11 = Geodesic.SinCosSeries(True, self._ssig1, self._csig1,
                                              self._C1a, Geodesic.nC1_)
            s = math.sin(self._B11)
            c = math.cos(self._B11)
            # tau1 = sig1 + B11
            self._stau1 = self._ssig1 * c + self._csig1 * s
            self._ctau1 = self._csig1 * c - self._ssig1 * s
            # Not necessary because C1pa reverts C1a
            #    _B11 = -SinCosSeries(true, _stau1, _ctau1, _C1pa, nC1p_)

        if self._caps & Geodesic.CAP_C1p:
            self._C1pa = range(Geodesic.nC1p_ + 1)
            Geodesic.C1pf(eps, self._C1pa)

        if self._caps & Geodesic.CAP_C2:
            self._A2m1 = Geodesic.A2m1f(eps)
            self._C2a = range(Geodesic.nC2_ + 1)
            Geodesic.C2f(eps, self._C2a)
            self._B21 = Geodesic.SinCosSeries(True, self._ssig1, self._csig1,
                                              self._C2a, Geodesic.nC2_)

        if self._caps & Geodesic.CAP_C3:
            self._C3a = range(Geodesic.nC3_)
            geod.C3f(eps, self._C3a)
            self._A3c = -self._f * self._salp0 * geod.A3f(eps)
            self._B31 = Geodesic.SinCosSeries(True, self._ssig1, self._csig1,
                                              self._C3a, Geodesic.nC3_ - 1)

        if self._caps & Geodesic.CAP_C4:
            self._C4a = range(Geodesic.nC4_)
            geod.C4f(eps, self._C4a)
            # Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
            self._A4 = Math.sq(self._a) * self._calp0 * self._salp0 * geod._e2
            self._B41 = Geodesic.SinCosSeries(False, self._ssig1, self._csig1,
                                              self._C4a, Geodesic.nC4_)
Ejemplo n.º 23
0
    def GenPosition(self, arcmode, s12_a12, outmask):

        from geographiclib.geodesic import Geodesic
        a12 = lat2 = lon2 = azi2 = s12 = m12 = M12 = M21 = S12 = Math.nan
        outmask &= self._caps & Geodesic.OUT_ALL
        if not (arcmode or
                (self._caps & Geodesic.DISTANCE_IN & Geodesic.OUT_ALL)):
            # Uninitialized or impossible distance calculation requested
            return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12

        # Avoid warning about uninitialized B12.
        B12 = 0
        AB1 = 0
        if arcmode:
            # Interpret s12_a12 as spherical arc length
            sig12 = s12_a12 * Math.degree
            s12a = abs(s12_a12)
            s12a -= 180 * math.floor(s12a / 180)
            ssig12 = 0 if s12a == 0 else math.sin(sig12)
            csig12 = 0 if s12a == 90 else math.cos(sig12)
        else:
            # Interpret s12_a12 as distance
            tau12 = s12_a12 / (self._b * (1 + self._A1m1))
            s = math.sin(tau12)
            c = math.cos(tau12)
            # tau2 = tau1 + tau12
            B12 = -Geodesic.SinCosSeries(
                True, self._stau1 * c + self._ctau1 * s,
                self._ctau1 * c - self._stau1 * s, self._C1pa, Geodesic.nC1p_)
            sig12 = tau12 - (B12 - self._B11)
            ssig12 = math.sin(sig12)
            csig12 = math.cos(sig12)
            if abs(self._f) > 0.01:
                # Reverted distance series is inaccurate for |f| > 1/100, so correct
                # sig12 with 1 Newton iteration.  The following table shows the
                # approximate maximum error for a = WGS_a() and various f relative to
                # GeodesicExact.
                #     erri = the error in the inverse solution (nm)
                #     errd = the error in the direct solution (series only) (nm)
                #     errda = the error in the direct solution (series + 1 Newton) (nm)
                #
                #       f     erri  errd errda
                #     -1/5    12e6 1.2e9  69e6
                #     -1/10  123e3  12e6 765e3
                #     -1/20   1110 108e3  7155
                #     -1/50  18.63 200.9 27.12
                #     -1/100 18.63 23.78 23.37
                #     -1/150 18.63 21.05 20.26
                #      1/150 22.35 24.73 25.83
                #      1/100 22.35 25.03 25.31
                #      1/50  29.80 231.9 30.44
                #      1/20   5376 146e3  10e3
                #      1/10  829e3  22e6 1.5e6
                #      1/5   157e6 3.8e9 280e6
                ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12
                csig2 = self._csig1 * csig12 - self._ssig1 * ssig12
                B12 = Geodesic.SinCosSeries(True, ssig2, csig2, self._C1a,
                                            Geodesic.nC1_)
                serr = ((1 + self._A1m1) * (sig12 + (B12 - self._B11)) -
                        s12_a12 / self._b)
                sig12 = sig12 - serr / math.sqrt(1 + self._k2 * Math.sq(ssig2))
                ssig12 = math.sin(sig12)
                csig12 = math.cos(sig12)
                # Update B12 below

        # real omg12, lam12, lon12
        # real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2
        # sig2 = sig1 + sig12
        ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12
        csig2 = self._csig1 * csig12 - self._ssig1 * ssig12
        dn2 = math.sqrt(1 + self._k2 * Math.sq(ssig2))
        if outmask & (Geodesic.DISTANCE | Geodesic.REDUCEDLENGTH
                      | Geodesic.GEODESICSCALE):
            if arcmode or abs(self._f) > 0.01:
                B12 = Geodesic.SinCosSeries(True, ssig2, csig2, self._C1a,
                                            Geodesic.nC1_)
            AB1 = (1 + self._A1m1) * (B12 - self._B11)
        # sin(bet2) = cos(alp0) * sin(sig2)
        sbet2 = self._calp0 * ssig2
        # Alt: cbet2 = hypot(csig2, salp0 * ssig2)
        cbet2 = math.hypot(self._salp0, self._calp0 * csig2)
        if cbet2 == 0:
            # I.e., salp0 = 0, csig2 = 0.  Break the degeneracy in this case
            cbet2 = csig2 = Geodesic.tiny_
        # tan(omg2) = sin(alp0) * tan(sig2)
        somg2 = self._salp0 * ssig2
        comg2 = csig2  # No need to normalize
        # tan(alp0) = cos(sig2)*tan(alp2)
        salp2 = self._salp0
        calp2 = self._calp0 * csig2  # No need to normalize
        # omg12 = omg2 - omg1
        omg12 = math.atan2(somg2 * self._comg1 - comg2 * self._somg1,
                           comg2 * self._comg1 + somg2 * self._somg1)

        if outmask & Geodesic.DISTANCE:
            s12 = self._b * (
                (1 + self._A1m1) * sig12 + AB1) if arcmode else s12_a12

        if outmask & Geodesic.LONGITUDE:
            lam12 = omg12 + self._A3c * (sig12 + (Geodesic.SinCosSeries(
                True, ssig2, csig2, self._C3a, Geodesic.nC3_ - 1) - self._B31))
            lon12 = lam12 / Math.degree
            # Use Math.AngNormalize2 because longitude might have wrapped
            # multiple times.
            lon12 = Math.AngNormalize2(lon12)
            lon2 = Math.AngNormalize(self._lon1 + lon12)

        if outmask & Geodesic.LATITUDE:
            lat2 = math.atan2(sbet2, self._f1 * cbet2) / Math.degree

        if outmask & Geodesic.AZIMUTH:
            # minus signs give range [-180, 180). 0- converts -0 to +0.
            azi2 = 0 - math.atan2(-salp2, calp2) / Math.degree

        if outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
            B22 = Geodesic.SinCosSeries(True, ssig2, csig2, self._C2a,
                                        Geodesic.nC2_)
            AB2 = (1 + self._A2m1) * (B22 - self._B21)
            J12 = (self._A1m1 - self._A2m1) * sig12 + (AB1 - AB2)
            if outmask & Geodesic.REDUCEDLENGTH:
                # Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
                # accurate cancellation in the case of coincident points.
                m12 = self._b * (
                    (dn2 * (self._csig1 * ssig2) - self._dn1 *
                     (self._ssig1 * csig2)) - self._csig1 * csig2 * J12)
            if outmask & Geodesic.GEODESICSCALE:
                t = (self._k2 * (ssig2 - self._ssig1) * (ssig2 + self._ssig1) /
                     (self._dn1 + dn2))
                M12 = csig12 + (t * ssig2 -
                                csig2 * J12) * self._ssig1 / self._dn1
                M21 = csig12 - (t * self._ssig1 -
                                self._csig1 * J12) * ssig2 / dn2

        if outmask & Geodesic.AREA:
            B42 = Geodesic.SinCosSeries(False, ssig2, csig2, self._C4a,
                                        Geodesic.nC4_)
            # real salp12, calp12
            if self._calp0 == 0 or self._salp0 == 0:
                # alp12 = alp2 - alp1, used in atan2 so no need to normalized
                salp12 = salp2 * self._calp1 - calp2 * self._salp1
                calp12 = calp2 * self._calp1 + salp2 * self._salp1
                # The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz
                # salp12 = -0 and alp12 = -180.  However this depends on the sign being
                # attached to 0 correctly.  The following ensures the correct behavior.
                if salp12 == 0 and calp12 < 0:
                    salp12 = Geodesic.tiny_ * self._calp1
                    calp12 = -1
            else:
                # tan(alp) = tan(alp0) * sec(sig)
                # tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
                # = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
                # If csig12 > 0, write
                #   csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
                # else
                #   csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
                # No need to normalize
                salp12 = self._calp0 * self._salp0 * (
                    self._csig1 * (1 - csig12) +
                    ssig12 * self._ssig1 if csig12 <= 0 else ssig12 *
                    (self._csig1 * ssig12 / (1 + csig12) + self._ssig1))
                calp12 = (Math.sq(self._salp0) +
                          Math.sq(self._calp0) * self._csig1 * csig2)
            S12 = self._c2 * math.atan2(salp12,
                                        calp12) + self._A4 * (B42 - self._B41)

        a12 = s12_a12 if arcmode else sig12 / Math.degree
        return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
Ejemplo n.º 24
0
 def Inverse(self, lat1, lon1, lat2, lon2, *outmask):
     '''Return the C{Inverse} result.
     '''
     d = _Geodesic.Inverse(self, lat1, lon1, lat2, lon2,
                           *outmask)
     return _Adict(d)
Ejemplo n.º 25
0
 def Direct(self, lat1, lon1, azi1, s12, *outmask):
     '''Return the C{Direct} result.
     '''
     d = _Geodesic.Direct(self, lat1, lon1, azi1, s12, *outmask)
     return _Adict(d)
class geodesic(Distance):
    """
    Calculate the geodesic distance between two points.

    Set which ellipsoidal model of the earth to use by specifying an
    ``ellipsoid`` keyword argument. The default is 'WGS-84', which is the
    most globally accurate model.  If ``ellipsoid`` is a string, it is
    looked up in the `ELLIPSOIDS` dictionary to obtain the major and minor
    semiaxes and the flattening. Otherwise, it should be a tuple with those
    values.  See the comments above the `ELLIPSOIDS` dictionary for
    more information.

    Example::

        >>> from geopy.distance import geodesic
        >>> newport_ri = (41.49008, -71.312796)
        >>> cleveland_oh = (41.499498, -81.695391)
        >>> print(geodesic(newport_ri, cleveland_oh).miles)
        538.390445368


    .. versionadded:: 1.13.0
    """

    ellipsoid_key = None
    ELLIPSOID = None
    geod = None

    def __init__(self, *args, **kwargs):
        self.set_ellipsoid(kwargs.pop('ellipsoid', 'WGS-84'))
        if 'iterations' in kwargs:
            warnings.warn(
                'Ignoring unused `iterations` kwarg for geodesic '
                'distance.', UserWarning)
        kwargs.pop('iterations', 0)
        major, minor, f = self.ELLIPSOID
        super(geodesic, self).__init__(*args, **kwargs)

    def set_ellipsoid(self, ellipsoid):
        """
        Change the ellipsoid used in the calculation.
        """
        if not isinstance(ellipsoid, (list, tuple)):
            try:
                self.ELLIPSOID = ELLIPSOIDS[ellipsoid]
                self.ellipsoid_key = ellipsoid
            except KeyError:
                raise Exception(
                    "Invalid ellipsoid. See geopy.distance.ELLIPSOIDS")
        else:
            self.ELLIPSOID = ellipsoid
            self.ellipsoid_key = None
        return

    # Call geographiclib routines for measure and destination
    def measure(self, a, b):
        a, b = Point(a), Point(b)
        lat1, lon1 = a.latitude, a.longitude
        lat2, lon2 = b.latitude, b.longitude

        if not (isinstance(self.geod, Geodesic) and self.geod.a
                == self.ELLIPSOID[0] and self.geod.f == self.ELLIPSOID[2]):
            self.geod = Geodesic(self.ELLIPSOID[0], self.ELLIPSOID[2])

        s12 = self.geod.Inverse(lat1, lon1, lat2, lon2,
                                Geodesic.DISTANCE)['s12']

        return s12

    def destination(self, point, bearing, distance=None):
        """
        TODO docs.
        """
        point = Point(point)
        lat1 = point.latitude
        lon1 = point.longitude
        azi1 = bearing

        if distance is None:
            distance = self
        if isinstance(distance, Distance):
            distance = distance.kilometers

        if not (isinstance(self.geod, Geodesic) and self.geod.a
                == self.ELLIPSOID[0] and self.geod.f == self.ELLIPSOID[2]):
            self.geod = Geodesic(self.ELLIPSOID[0], self.ELLIPSOID[2])

        r = self.geod.Direct(lat1, lon1, azi1, distance,
                             Geodesic.LATITUDE | Geodesic.LONGITUDE)

        return Point(r['lat2'], r['lon2'])
Ejemplo n.º 27
0
 def test_GeodSolve33(self):
     # Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in
     # Octave -- sind(-0.0) = +0.0 -- and in some version of Visual
     # Studio -- fmod(-0.0, 360.0) = +0.0.
     inv = Geodesic.WGS84.Inverse(0, 0, 0, 179)
     self.assertAlmostEqual(inv["azi1"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 19926189, delta=0.5)
     inv = Geodesic.WGS84.Inverse(0, 0, 0, 179.5)
     self.assertAlmostEqual(inv["azi1"], 55.96650, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 124.03350, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 19980862, delta=0.5)
     inv = Geodesic.WGS84.Inverse(0, 0, 0, 180)
     self.assertAlmostEqual(inv["azi1"], 0.00000, delta=0.5e-5)
     self.assertAlmostEqual(abs(inv["azi2"]), 180.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20003931, delta=0.5)
     inv = Geodesic.WGS84.Inverse(0, 0, 1, 180)
     self.assertAlmostEqual(inv["azi1"], 0.00000, delta=0.5e-5)
     self.assertAlmostEqual(abs(inv["azi2"]), 180.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 19893357, delta=0.5)
     geod = Geodesic(6.4e6, 0)
     inv = geod.Inverse(0, 0, 0, 179)
     self.assertAlmostEqual(inv["azi1"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 19994492, delta=0.5)
     inv = geod.Inverse(0, 0, 0, 180)
     self.assertAlmostEqual(inv["azi1"], 0.00000, delta=0.5e-5)
     self.assertAlmostEqual(abs(inv["azi2"]), 180.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20106193, delta=0.5)
     inv = geod.Inverse(0, 0, 1, 180)
     self.assertAlmostEqual(inv["azi1"], 0.00000, delta=0.5e-5)
     self.assertAlmostEqual(abs(inv["azi2"]), 180.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 19994492, delta=0.5)
     geod = Geodesic(6.4e6, -1 / 300.0)
     inv = geod.Inverse(0, 0, 0, 179)
     self.assertAlmostEqual(inv["azi1"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 19994492, delta=0.5)
     inv = geod.Inverse(0, 0, 0, 180)
     self.assertAlmostEqual(inv["azi1"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 90.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20106193, delta=0.5)
     inv = geod.Inverse(0, 0, 0.5, 180)
     self.assertAlmostEqual(inv["azi1"], 33.02493, delta=0.5e-5)
     self.assertAlmostEqual(inv["azi2"], 146.97364, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20082617, delta=0.5)
     inv = geod.Inverse(0, 0, 1, 180)
     self.assertAlmostEqual(inv["azi1"], 0.00000, delta=0.5e-5)
     self.assertAlmostEqual(abs(inv["azi2"]), 180.00000, delta=0.5e-5)
     self.assertAlmostEqual(inv["s12"], 20027270, delta=0.5)
Ejemplo n.º 28
0
  def _GenPosition(self, arcmode, s12_a12, outmask):
    """Private: General solution of position along geodesic"""
    from geographiclib.geodesic import Geodesic
    a12 = lat2 = lon2 = azi2 = s12 = m12 = M12 = M21 = S12 = Math.nan
    outmask &= self.caps & Geodesic.OUT_MASK
    if not (arcmode or
            (self.caps & (Geodesic.OUT_MASK & Geodesic.DISTANCE_IN))):
      # Uninitialized or impossible distance calculation requested
      return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12

    # Avoid warning about uninitialized B12.
    B12 = 0.0; AB1 = 0.0
    if arcmode:
      # Interpret s12_a12 as spherical arc length
      sig12 = math.radians(s12_a12)
      ssig12, csig12 = Math.sincosd(s12_a12)
    else:
      # Interpret s12_a12 as distance
      tau12 = s12_a12 / (self._b * (1 + self._A1m1))
      s = math.sin(tau12); c = math.cos(tau12)
      # tau2 = tau1 + tau12
      B12 = - Geodesic._SinCosSeries(True,
                                    self._stau1 * c + self._ctau1 * s,
                                    self._ctau1 * c - self._stau1 * s,
                                    self._C1pa)
      sig12 = tau12 - (B12 - self._B11)
      ssig12 = math.sin(sig12); csig12 = math.cos(sig12)
      if abs(self.f) > 0.01:
        # Reverted distance series is inaccurate for |f| > 1/100, so correct
        # sig12 with 1 Newton iteration.  The following table shows the
        # approximate maximum error for a = WGS_a() and various f relative to
        # GeodesicExact.
        #     erri = the error in the inverse solution (nm)
        #     errd = the error in the direct solution (series only) (nm)
        #     errda = the error in the direct solution (series + 1 Newton) (nm)
        #
        #       f     erri  errd errda
        #     -1/5    12e6 1.2e9  69e6
        #     -1/10  123e3  12e6 765e3
        #     -1/20   1110 108e3  7155
        #     -1/50  18.63 200.9 27.12
        #     -1/100 18.63 23.78 23.37
        #     -1/150 18.63 21.05 20.26
        #      1/150 22.35 24.73 25.83
        #      1/100 22.35 25.03 25.31
        #      1/50  29.80 231.9 30.44
        #      1/20   5376 146e3  10e3
        #      1/10  829e3  22e6 1.5e6
        #      1/5   157e6 3.8e9 280e6
        ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12
        csig2 = self._csig1 * csig12 - self._ssig1 * ssig12
        B12 = Geodesic._SinCosSeries(True, ssig2, csig2, self._C1a)
        serr = ((1 + self._A1m1) * (sig12 + (B12 - self._B11)) -
                s12_a12 / self._b)
        sig12 = sig12 - serr / math.sqrt(1 + self._k2 * Math.sq(ssig2))
        ssig12 = math.sin(sig12); csig12 = math.cos(sig12)
        # Update B12 below

    # real omg12, lam12, lon12
    # real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2
    # sig2 = sig1 + sig12
    ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12
    csig2 = self._csig1 * csig12 - self._ssig1 * ssig12
    dn2 = math.sqrt(1 + self._k2 * Math.sq(ssig2))
    if outmask & (
      Geodesic.DISTANCE | Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
      if arcmode or abs(self.f) > 0.01:
        B12 = Geodesic._SinCosSeries(True, ssig2, csig2, self._C1a)
      AB1 = (1 + self._A1m1) * (B12 - self._B11)
    # sin(bet2) = cos(alp0) * sin(sig2)
    sbet2 = self._calp0 * ssig2
    # Alt: cbet2 = hypot(csig2, salp0 * ssig2)
    cbet2 = math.hypot(self._salp0, self._calp0 * csig2)
    if cbet2 == 0:
      # I.e., salp0 = 0, csig2 = 0.  Break the degeneracy in this case
      cbet2 = csig2 = Geodesic.tiny_
    # tan(alp0) = cos(sig2)*tan(alp2)
    salp2 = self._salp0; calp2 = self._calp0 * csig2 # No need to normalize

    if outmask & Geodesic.DISTANCE:
      s12 = self._b * ((1 + self._A1m1) * sig12 + AB1) if arcmode else s12_a12

    if outmask & Geodesic.LONGITUDE:
      # tan(omg2) = sin(alp0) * tan(sig2)
      somg2 = self._salp0 * ssig2; comg2 = csig2 # No need to normalize
      E = Math.copysign(1, self._salp0)          # East or west going?
      # omg12 = omg2 - omg1
      omg12 = (E * (sig12
                    - (math.atan2(          ssig2,       csig2) -
                       math.atan2(    self._ssig1, self._csig1))
                    + (math.atan2(E *       somg2,       comg2) -
                       math.atan2(E * self._somg1, self._comg1)))
               if outmask & Geodesic.LONG_UNROLL
               else math.atan2(somg2 * self._comg1 - comg2 * self._somg1,
                               comg2 * self._comg1 + somg2 * self._somg1))
      lam12 = omg12 + self._A3c * (
        sig12 + (Geodesic._SinCosSeries(True, ssig2, csig2, self._C3a)
                 - self._B31))
      lon12 = math.degrees(lam12)
      lon2 = (self.lon1 + lon12 if outmask & Geodesic.LONG_UNROLL else
              Math.AngNormalize(Math.AngNormalize(self.lon1) +
                                Math.AngNormalize(lon12)))

    if outmask & Geodesic.LATITUDE:
      lat2 = Math.atan2d(sbet2, self._f1 * cbet2)

    if outmask & Geodesic.AZIMUTH:
      # minus signs give range [-180, 180). 0- converts -0 to +0.
      azi2 = Math.atan2d(salp2, calp2)

    if outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
      B22 = Geodesic._SinCosSeries(True, ssig2, csig2, self._C2a)
      AB2 = (1 + self._A2m1) * (B22 - self._B21)
      J12 = (self._A1m1 - self._A2m1) * sig12 + (AB1 - AB2)
      if outmask & Geodesic.REDUCEDLENGTH:
        # Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
        # accurate cancellation in the case of coincident points.
        m12 = self._b * ((      dn2 * (self._csig1 * ssig2) -
                          self._dn1 * (self._ssig1 * csig2))
                         - self._csig1 * csig2 * J12)
      if outmask & Geodesic.GEODESICSCALE:
        t = (self._k2 * (ssig2 - self._ssig1) *
             (ssig2 + self._ssig1) / (self._dn1 + dn2))
        M12 = csig12 + (t * ssig2 - csig2 * J12) * self._ssig1 / self._dn1
        M21 = csig12 - (t * self._ssig1 - self._csig1 * J12) * ssig2 / dn2

    if outmask & Geodesic.AREA:
      B42 = Geodesic._SinCosSeries(False, ssig2, csig2, self._C4a)
      # real salp12, calp12
      if self._calp0 == 0 or self._salp0 == 0:
        # alp12 = alp2 - alp1, used in atan2 so no need to normalize
        salp12 = salp2 * self.calp1 - calp2 * self.salp1
        calp12 = calp2 * self.calp1 + salp2 * self.salp1
      else:
        # tan(alp) = tan(alp0) * sec(sig)
        # tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
        # = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
        # If csig12 > 0, write
        #   csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
        # else
        #   csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
        # No need to normalize
        salp12 = self._calp0 * self._salp0 * (
          self._csig1 * (1 - csig12) + ssig12 * self._ssig1 if csig12 <= 0
          else ssig12 * (self._csig1 * ssig12 / (1 + csig12) + self._ssig1))
        calp12 = (Math.sq(self._salp0) +
                  Math.sq(self._calp0) * self._csig1 * csig2)
      S12 = (self._c2 * math.atan2(salp12, calp12) +
             self._A4 * (B42 - self._B41))

    a12 = s12_a12 if arcmode else math.degrees(sig12)
    return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
Ejemplo n.º 29
0
  def __init__(self, geod, lat1, lon1, azi1,
               caps = GeodesicCapability.STANDARD |
               GeodesicCapability.DISTANCE_IN,
               salp1 = Math.nan, calp1 = Math.nan):
    """Construct a GeodesicLine object

    :param geod: a :class:`~geographiclib.geodesic.Geodesic` object
    :param lat1: latitude of the first point in degrees
    :param lon1: longitude of the first point in degrees
    :param azi1: azimuth at the first point in degrees
    :param caps: the :ref:`capabilities <outmask>`

    This creates an object allowing points along a geodesic starting at
    (*lat1*, *lon1*), with azimuth *azi1* to be found.  The default
    value of *caps* is STANDARD | DISTANCE_IN.  The optional parameters
    *salp1* and *calp1* should not be supplied; they are part of the
    private interface.

    """

    from geographiclib.geodesic import Geodesic
    self.a = geod.a
    """The equatorial radius in meters (readonly)"""
    self.f = geod.f
    """The flattening (readonly)"""
    self._b = geod._b
    self._c2 = geod._c2
    self._f1 = geod._f1
    self.caps = (caps | Geodesic.LATITUDE | Geodesic.AZIMUTH |
                  Geodesic.LONG_UNROLL)
    """the capabilities (readonly)"""

    # Guard against underflow in salp0
    self.lat1 = Math.LatFix(lat1)
    """the latitude of the first point in degrees (readonly)"""
    self.lon1 = lon1
    """the longitude of the first point in degrees (readonly)"""
    if Math.isnan(salp1) or Math.isnan(calp1):
      self.azi1 = Math.AngNormalize(azi1)
      self.salp1, self.calp1 = Math.sincosd(Math.AngRound(azi1))
    else:
      self.azi1 = azi1
      """the azimuth at the first point in degrees (readonly)"""
      self.salp1 = salp1
      """the sine of the azimuth at the first point (readonly)"""
      self.calp1 = calp1
      """the cosine of the azimuth at the first point (readonly)"""

    # real cbet1, sbet1
    sbet1, cbet1 = Math.sincosd(Math.AngRound(lat1)); sbet1 *= self._f1
    # Ensure cbet1 = +epsilon at poles
    sbet1, cbet1 = Math.norm(sbet1, cbet1); cbet1 = max(Geodesic.tiny_, cbet1)
    self._dn1 = math.sqrt(1 + geod._ep2 * Math.sq(sbet1))

    # Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
    self._salp0 = self.salp1 * cbet1 # alp0 in [0, pi/2 - |bet1|]
    # Alt: calp0 = hypot(sbet1, calp1 * cbet1).  The following
    # is slightly better (consider the case salp1 = 0).
    self._calp0 = math.hypot(self.calp1, self.salp1 * sbet1)
    # Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
    # sig = 0 is nearest northward crossing of equator.
    # With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
    # With bet1 =  pi/2, alp1 = -pi, sig1 =  pi/2
    # With bet1 = -pi/2, alp1 =  0 , sig1 = -pi/2
    # Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
    # With alp0 in (0, pi/2], quadrants for sig and omg coincide.
    # No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
    # With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
    self._ssig1 = sbet1; self._somg1 = self._salp0 * sbet1
    self._csig1 = self._comg1 = (cbet1 * self.calp1
                                 if sbet1 != 0 or self.calp1 != 0 else 1)
    # sig1 in (-pi, pi]
    self._ssig1, self._csig1 = Math.norm(self._ssig1, self._csig1)
    # No need to normalize
    # self._somg1, self._comg1 = Math.norm(self._somg1, self._comg1)

    self._k2 = Math.sq(self._calp0) * geod._ep2
    eps = self._k2 / (2 * (1 + math.sqrt(1 + self._k2)) + self._k2)

    if self.caps & Geodesic.CAP_C1:
      self._A1m1 = Geodesic._A1m1f(eps)
      self._C1a = list(range(Geodesic.nC1_ + 1))
      Geodesic._C1f(eps, self._C1a)
      self._B11 = Geodesic._SinCosSeries(
        True, self._ssig1, self._csig1, self._C1a)
      s = math.sin(self._B11); c = math.cos(self._B11)
      # tau1 = sig1 + B11
      self._stau1 = self._ssig1 * c + self._csig1 * s
      self._ctau1 = self._csig1 * c - self._ssig1 * s
      # Not necessary because C1pa reverts C1a
      #    _B11 = -_SinCosSeries(true, _stau1, _ctau1, _C1pa)

    if self.caps & Geodesic.CAP_C1p:
      self._C1pa = list(range(Geodesic.nC1p_ + 1))
      Geodesic._C1pf(eps, self._C1pa)

    if self.caps & Geodesic.CAP_C2:
      self._A2m1 = Geodesic._A2m1f(eps)
      self._C2a = list(range(Geodesic.nC2_ + 1))
      Geodesic._C2f(eps, self._C2a)
      self._B21 = Geodesic._SinCosSeries(
        True, self._ssig1, self._csig1, self._C2a)

    if self.caps & Geodesic.CAP_C3:
      self._C3a = list(range(Geodesic.nC3_))
      geod._C3f(eps, self._C3a)
      self._A3c = -self.f * self._salp0 * geod._A3f(eps)
      self._B31 = Geodesic._SinCosSeries(
        True, self._ssig1, self._csig1, self._C3a)

    if self.caps & Geodesic.CAP_C4:
      self._C4a = list(range(Geodesic.nC4_))
      geod._C4f(eps, self._C4a)
      # Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
      self._A4 = Math.sq(self.a) * self._calp0 * self._salp0 * geod._e2
      self._B41 = Geodesic._SinCosSeries(
        False, self._ssig1, self._csig1, self._C4a)
    self.s13 = Math.nan
    """the distance between point 1 and point 3 in meters (readonly)"""
    self.a13 = Math.nan
    """the arc length between point 1 and point 3 in degrees (readonly)"""
Ejemplo n.º 30
0
 def test_GeodSolve26(self):
     # Check 0/0 problem with area calculation on sphere 2015-09-08
     geod = Geodesic(6.4e6, 0)
     inv = geod.Inverse(1, 2, 3, 4, Geodesic.AREA)
     self.assertAlmostEqual(inv["S12"], 49911046115.0, delta=0.5)
Ejemplo n.º 31
0
def add_geo_to_arrivals(arrivals, source_latitude_in_deg,
                        source_longitude_in_deg, receiver_latitude_in_deg,
                        receiver_longitude_in_deg, radius_of_earth_in_km,
                        flattening_of_earth):
    """
    Add geographical information to arrivals.

    :param arrivals: Set of taup arrivals
    :type: :class:`Arrivals`
    :param source_latitude_in_deg: Source location latitude in degrees
    :type source_latitude_in_deg: float
    :param source_longitude_in_deg: Source location longitude in degrees
    :type source_longitude_in_deg: float
    :param receiver_latitude_in_deg: Receiver location latitude in degrees
    :type receiver_latitude_in_deg: float
    :param receiver_longitude_in_deg: Receiver location longitude in degrees
    :type receiver_longitude_in_deg: float
    :param radius_of_earth_in_km: Radius of the Earth in km
    :type radius_of_earth_in_km: float
    :param flattening_of_earth: Flattening of Earth (0 for a sphere)
    :type receiver_longitude_in_deg: float

    :return: List of ``Arrival`` objects, each of which has the time,
        corresponding phase name, ray parameter, takeoff angle, etc. as
        attributes.
    :rtype: :class:`Arrivals`
    """
    if geodetics.HAS_GEOGRAPHICLIB:
        ellipsoid = Geodesic(a=radius_of_earth_in_km * 1000.0,
                             f=flattening_of_earth)
        g = ellipsoid.Inverse(source_latitude_in_deg, source_longitude_in_deg,
                              receiver_latitude_in_deg,
                              receiver_longitude_in_deg)
        azimuth = g['azi1']
        line = ellipsoid.Line(source_latitude_in_deg, source_longitude_in_deg,
                              azimuth)

        # We may need to update many arrival objects
        # and each could have pierce points and a
        # path
        for arrival in arrivals:

            if arrival.pierce is not None:
                geo_pierce = np.empty(arrival.pierce.shape, dtype=TimeDistGeo)
                for i, pierce_point in enumerate(arrival.pierce):
                    pos = line.ArcPosition(np.degrees(pierce_point['dist']))
                    geo_pierce[i] = (pierce_point['p'], pierce_point['time'],
                                     pierce_point['dist'],
                                     pierce_point['depth'],
                                     pos['lat2'], pos['lon2'])
                arrival.pierce = geo_pierce

            if arrival.path is not None:
                geo_path = np.empty(arrival.path.shape, dtype=TimeDistGeo)
                for i, path_point in enumerate(arrival.path):
                    pos = line.ArcPosition(np.degrees(path_point['dist']))
                    geo_path[i] = (path_point['p'], path_point['time'],
                                   path_point['dist'], path_point['depth'],
                                   pos['lat2'], pos['lon2'])
                arrival.path = geo_path

    else:
        # geographiclib is not installed ...
        # and  obspy/geodetics does not help much
        msg = "You need to install the Python module 'geographiclib' in " + \
              "order to add geographical information to arrivals."
        raise ImportError(msg)

    return arrivals
Ejemplo n.º 32
0
 def test_GeodSolve28(self):
     # Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in
     # Java implementation fixed on 2015-05-19).
     geod = Geodesic(6.4e6, 0.1)
     dir = geod.Direct(1, 2, 10, 5e6)
     self.assertAlmostEqual(dir["a12"], 48.55570690, delta=0.5e-8)