def __init__(self, e, n, h=0, conic=Conics.WRF_Lb, name=''): '''New L{Lcc} Lamber conformal conic position. @param e: Easting (C{meter}). @param n: Northing (C{meter}). @keyword h: Optional height (C{meter}). @keyword conic: Optional, the conic projection (L{Conic}). @keyword name: Optional name (C{str}). @return: The Lambert location (L{Lcc}). @raise TypeError: If B{C{conic}} is not L{Conic}. @raise LCCError: Invalid or negative B{C{e}} or B{C{n}}. @example: >>> lb = Lcc(448251, 5411932.0001) ''' _TypeError(Conic, conic=conic) self._conic = conic self._easting = false2f(e, 'easting', false=conic.E0 > 0, Error=LCCError) self._northing = false2f(n, 'northing', false=conic.N0 > 0, Error=LCCError) if h: self._height = float(h) if name: self.name = name
def __init__(self, latlonh0=0, lon0=0, height0=0, ecef=EcefKarney(Datums.WGS84), name=''): '''New L{EcefCartesian} converter. @keyword latlonh0: Either a C{LatLon}, an L{Ecef9Tuple} or C{scalar} latitude in C{degrees} of the cartesian origin. @keyword lon0: Optional C{scalar} longitude of the cartesian origin in C{degrees} for C{scalar} B{C{latlonh0}}. @keyword height0: Optional height of the cartesian origin in C{meter}, vertically above (or below) the surface of the ellipsoid. @keyword ecef: An EXEC converter (L{EcefKarney}). @keyword name: Optional name (C{str}). @raise EcefError: If B{C{latlonh0}} not C{LatLon}, L{Ecef9Tuple} or C{scalar} or B{C{lon0}} not C{scalar} for C{scalar} B{C{latlonh0}} or C{abs(B{lat0})} exceeds 90°. @raise TypeError: Invalid B{C{ecef}}, not L{EcefKarney}. ''' _TypeError(EcefKarney, ecef=ecef) self._ecef = ecef self.reset(latlonh0, lon0, height0, name=name)
def convertDatum(self, datum2, datum): '''Convert this Cartesian point from one to an other datum. @param datum2: Datum to convert I{to} (L{Datum}). @param datum: Datum to convert I{from} (L{Datum}). @return: The converted Cartesian point (C{Cartesian}). @raise TypeError: B{C{datum2}} or B{C{datum}} not a L{Datum}. ''' _TypeError(Datum, datum2=datum2, datum=datum) if datum == datum2: return self.copy() elif datum == Datums.WGS84: # converting from WGS 84 c, d, i = self, datum2, False elif datum2 == Datums.WGS84: # converting to WGS84, use inverse transform c, d, i = self, datum, True else: # neither datum2 nor datum is WGS84, convert to WGS84 first c, d, i = self._applyHelmert(datum.transform, True), datum2, False return c._applyHelmert(d.transform, i)
def toUtmUps(self, pole=''): '''Convert this C{LatLon} point to a UTM or UPS coordinate. @keyword pole: Optional top/center of UPS (stereographic) projection (C{str}, 'N[orth]' or 'S[outh]'). @return: The UTM or UPS coordinate (L{Utm} or L{Ups}). @raise TypeError: Result in L{Utm} or L{Ups}. @see: Function L{toUtmUps}. ''' if self._utm: u = self._utm elif self._ups and (self._utm.pole == pole or not pole): u = self._ups else: from pygeodesy.utmups import toUtmUps8, Utm, Ups # PYCHOK recursive import u = toUtmUps8(self, datum=self.datum, Utm=Utm, Ups=Ups, pole=pole) if isinstance(u, Utm): self._utm = u elif isinstance(u, Ups): self._ups = u else: _TypeError(Utm, Ups, toUtmUps8=u) return u
def convertDatum(self, datum2, datum=None): '''Convert this cartesian point from one to an other datum. @param datum2: Datum to convert I{to} (L{Datum}). @keyword datum: Datum to convert I{from} (L{Datum}). @return: The converted point (C{Cartesian}). @raise TypeError: B{C{datum2}} or B{C{datum}} not a L{Datum}. ''' _TypeError(Datum, datum2=datum2) if datum and self.datum != datum: c = self.convertDatum(datum) else: c = self i, d = False, c.datum if d == datum2: return c.copy() if c is self else c elif d == Datums.WGS84: d = datum2 # convert from WGS84 to datum2 elif datum2 == Datums.WGS84: i = True # convert to WGS84 by inverse transform else: # neither datum2 nor c.datum is WGS84, invert to WGS84 first c = c._applyHelmert(d.transform, True) d = datum2 return c._applyHelmert(d.transform, i, datum=datum2)
def convertRefFrame(self, reframe2, reframe, epoch=None): '''Convert this Cartesian point from one to an other reference frame. @param reframe2: Reference frame to convert I{to} (L{RefFrame}). @param reframe: Reference frame to convert I{from} (L{RefFrame}). @keyword epoch: Optional epoch to observe for B{C{reframe}}, a fractional calendar year (C{scalar}). @return: The converted Cartesian point (C{Cartesian}) or this Cartesian point if conversion is C{nil}. @raise TRFError: No conversion available from B{C{reframe}} to B{C{reframe2}}. @raise TypeError: B{C{reframe2}} or B{C{reframe}} not a L{RefFrame} or B{C{epoch}} not C{scalar}. ''' _TypeError(RefFrame, reframe2=reframe2, reframe=reframe) c = self for t in _reframeTransforms( reframe2, reframe, reframe.epoch if epoch is None else _2epoch(epoch)): c = c._applyHelmert(t, False) return c
def toLatLon(self, datum=None, LatLon=None, **kwds): '''Convert this cartesian point to a geodetic point. @keyword datum: Optional datum (L{Datum}) or C{None}. @keyword LatLon: Optional (sub-)class to return the geodetic point (C{LatLon}) or C{None}. @keyword kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon}=None}. @return: The B{C{LatLon}} point or if C{B{LatLon}=None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)} with C{C} and C{M} if available. @raise TypeError: Invalid B{C{datum}} or B{C{kwds}}. ''' c = self if datum is not None: _TypeError(Datum, datum=datum) if datum != self.datum: c = self.convertDatum(datum) r = c.Ecef(c.datum).reverse(c, M=True) if LatLon is not None: # class or .classof r = LatLon(r.lat, r.lon, height=r.height, datum=c.datum, **kwds) return self._xnamed(r)
def latlon(self, latlonh): '''Set the lat- and longitude and optionally the height. @param latlonh: New lat-, longitude and height (2- or 3-tuple of C{degrees} and C{meter}). @raise TypeError: Height of B{C{latlonh}} not C{scalar} or B{C{latlonh}} not C{list} or C{tuple}. @raise ValueError: Invalid B{C{latlonh}} or M{len(latlonh)}. @see: Function L{parse3llh} to parse a B{C{latlonh}} string into a 3-tuple (lat, lon, h). ''' _TypeError(list, tuple, latlonh=latlonh) if len(latlonh) == 3: h = scalar(latlonh[2], None, name='latlonh') elif len(latlonh) != 2: raise ValueError('%s invalid: %r' % ('latlonh', latlonh)) else: h = self._height lat, lon = parseDMS2(latlonh[0], latlonh[1]) self._update(lat != self._lat or lon != self._lon or h != self._height) self._lat, self._lon, self._height = lat, lon, h
def convertRefFrame(self, reframe2): '''Convert this point to an other reference frame. @param reframe2: Reference frame to convert I{to} (L{RefFrame}). @return: The converted point (ellipsoidal C{LatLon}) or this point if conversion is C{nil}. @raise TRFError: No B{C{.reframe}} or no conversion available from B{C{.reframe}} to B{C{reframe2}}. @raise TypeError: The B{C{reframe2}} is not a L{RefFrame}. @example: >>> p = LatLon(51.4778, -0.0016, reframe=RefFrames.ETRF2000) # default Datums.WGS84 >>> p.convertRefFrame(RefFrames.ITRF2014) # 51.477803°N, 000.001597°W, +0.01m ''' _TypeError(RefFrame, reframe2=reframe2) if not self.reframe: raise TRFError('no %r.%s' % (self, 'reframe')) ts = _reframeTransforms(reframe2, self.reframe, self.epoch) if ts: c = self.toCartesian() for t in ts: c = c._applyHelmert(t, False) ll = c.toLatLon(datum=self.datum, LatLon=self.classof, epoch=self.epoch, reframe=reframe2) # ll.reframe, ll.epoch = reframe2, self.epoch else: ll = self return ll
def nearestOn2(point, points, closed=False, radius=R_M, height=None): '''Locate the point on a polygon (with great circle arcs joining consecutive points) closest to an other point. If the given point is within the extent of any great circle arc, the closest point is on that arc. Otherwise, the closest is the nearest of the arc's end points. @param point: The other, reference point (L{LatLon}). @param points: The polygon points (L{LatLon}[]). @keyword closed: Optionally, close the polygon (C{bool}). @keyword radius: Mean earth radius (C{meter}). @keyword height: Optional height, overriding the mean height for a point within the arc (C{meter}). @return: 2-Tuple C{(closest, distance)} of the closest point (L{LatLon}) on the polygon and the C{distance} from the C{closest} to the other B{C{point}} in C{meter}, same units as B{C{radius}}. @raise TypeError: Some B{C{points}} or B{C{point}} not C{LatLon}. @raise ValueError: No B{C{points}}. ''' _TypeError(LatLon, point=point) return point.nearestOn2(points, closed=closed, radius=radius, height=height)
def __init__(self, latlon0, par1, par2=None, E0=0, N0=0, k0=1, opt3=0, name='', auth=''): '''New Lambert conformal conic projection. @param latlon0: Origin with (ellipsoidal) datum (C{LatLon}). @param par1: First standard parallel (C{degrees90}). @keyword par2: Optional, second standard parallel (C{degrees90}). @keyword E0: Optional, false easting (C{meter}). @keyword N0: Optional, false northing (C{meter}). @keyword k0: Optional scale factor (C{scalar}). @keyword opt3: Optional meridian (C{degrees180}). @keyword name: Optional name of the conic (C{str}). @keyword auth: Optional authentication authority (C{str}). @return: A Lambert projection (L{Conic}). @raise TypeError: Non-ellipsoidal B{C{latlon0}}. @example: >>> from pygeodesy import Conic, Datums, ellipsoidalNvector >>> ll0 = ellipsoidalNvector.LatLon(23, -96, datum=Datums.NAD27) >>> Snyder = Conic(ll0, 33, 45, E0=0, N0=0, name='Snyder') ''' if latlon0 is not None: _TypeError(_LLEB, latlon0=latlon0) self._lat0, self._lon0 = latlon0.to2ab() self._par1 = radians(par1) if par2 is None: self._par2 = self._par1 else: self._par2 = radians(par2) self._opt3 = radians(opt3) if k0 != 1: self._k0 = float(k0) if N0: self._N0 = float(N0) if E0: self._E0 = float(E0) self.toDatum(latlon0.datum)._dup2(self) self._register(Conics, name) elif name: self._name = name if auth: self._auth = auth
def exactTM(self, exactTM): '''Set the ETM projection (L{ExactTransverseMercator}). ''' _TypeError(ExactTransverseMercator, exactTM=exactTM) E = self.datum.ellipsoid if exactTM._E != E or exactTM.majoradius != E.a \ or exactTM.flattening != E.f: raise ETMError('%r vs %r' % (exactTM, E)) self._exactTM = exactTM self._scale0 = exactTM.k0
def _CassiniSoldner(cs0): '''(INTERNAL) Get/set default projection. ''' if cs0 is None: global _CassiniSoldner0 if _CassiniSoldner0 is None: _CassiniSoldner0 = CassiniSoldner(0, 0, name='Default') cs0 = _CassiniSoldner0 else: _TypeError(CassiniSoldner, cs0=cs0) return cs0
def reframe(self, reframe): '''Set or clear this point's reference frame. @param reframe: Reference frame (L{RefFrame}) or C{None}. @raise TypeError: The B{C{reframe}} is not a L{RefFrame}. ''' if isinstance(reframe, RefFrame): self._reframe = reframe elif reframe is not None: _TypeError(RefFrame, reframe=reframe) elif self.reframe is not None: self._reframe = None
def datum(self, datum): '''Set this point's datum I{without conversion}. @param datum: New datum (L{Datum}). @raise TypeError: The B{C{datum}} is not a L{Datum} or not ellipsoidal. ''' _TypeError(Datum, datum=datum) if not datum.isEllipsoidal: raise _IsNotError('ellipsoidal', datum=datum) self._update(datum != self._datum) self._datum = datum
def _to4lldn(latlon, lon, datum, name): '''(INTERNAL) Return 4-tuple (C{lat, lon, datum, name}). ''' try: # if lon is not None: # raise AttributeError lat, lon = latlon.lat, latlon.lon _TypeError(_LLEB, LatLonDatum5Tuple, latlon=latlon) d = datum or latlon.datum except AttributeError: lat, lon = parseDMS2(latlon, lon) d = datum or Datums.WGS84 return lat, lon, d, (name or nameof(latlon))
def datum(self, datum): '''Set this point's datum without conversion. @param datum: New datum (L{Datum}). @raise TypeError: If B{C{datum}} is not a L{Datum}. @raise ValueError: If B{C{datum}} is not spherical. ''' _TypeError(Datum, datum=datum) if not datum.isSpherical: raise ValueError('%r not %s: %r' % ('datum', 'spherical', datum)) self._update(datum != self._datum) self._datum = datum
def datum(self, datum): '''Set this geocentric point's C{datum} I{without conversion}. @param datum: New datum (L{Datum}). @raise TypeError: The B{C{datum}} is not a L{Datum}. ''' _TypeError(Datum, datum=datum) d = self.datum if d is not None: if d.isEllipsoidal and not datum.isEllipsoidal: raise _IsNotError('ellipsoidal', datum=datum) elif d.isSpherical and not datum.isSpherical: raise _IsNotError('spherical', datum=datum) self._update(datum != d) self._datum = datum
def multiply(self, other): '''Matrix multiply M{M0' ⋅ M} this matrix transposed with an other matrix. @param other: The other matrix (L{EcefMatrix}). @return: The matrix product (L{EcefMatrix}). @raise TypeError: If B{C{other}} is not L{EcefMatrix}. ''' _TypeError(EcefMatrix, other=other) # like LocalCartesian.MatrixMultiply, transposed(self) x other # <https://GeographicLib.SourceForge.io/html/LocalCartesian_8cpp_source.html> M = (fdot(self[r::3], *other[c::3]) for r in range(3) for c in range(3)) return EcefMatrix(*M)
def datum(self, datum): '''Set the datum and ellipsoid (L{Datum}). @raise EllipticError: No convergence. @raise TypeError: Invalid B{C{datum}}. ''' _TypeError(Datum, datum=datum) E = datum.ellipsoid self._reset(E.e, E.e2) self._a = E.a self._f = E.f # flattening = (a - b) / a self._datum = datum self._E = E
def __init__(self, epoch, ellipsoid, name=''): '''New L{RefFrame}. @param epoch: Epoch, a fractional calendar year (C{scalar}). @param ellipsoid: The ellipsoid (L{Ellipsoid}). @keyword name: Optional, unique name (C{str}). @raise NameError: A L{RefFrame} with that B{C{name}} already exists. @raise TypeError: If B{C{epoch}} is not C{scalar} or B{C{ellipsoid}} is not an L{Ellipsoid}. ''' _TypeError(Ellipsoid, ellipsoid=ellipsoid) self._ellipsoid = ellipsoid self._epoch = _2epoch(epoch) self._register(RefFrames, name)
def toMgrs(utm, Mgrs=Mgrs, name=''): '''Convert a UTM coordinate to an MGRS grid reference. @param utm: A UTM coordinate (L{Utm} or L{Etm}). @keyword Mgrs: Optional (sub-)class to return the MGRS grid reference (L{Mgrs}) or C{None}. @keyword name: Optional B{C{Mgrs}} name (C{str}). @return: The MGRS grid reference (B{L{Mgrs}}) or an L{Mgrs6Tuple}C{(zone, digraph, easting, northing, band, datum)} if B{L{Mgrs}} is C{None}. @raise TypeError: If B{C{utm}} is not L{Utm} nor L{Etm}. @raise MGRSError: Invalid B{C{utm}}. @example: >>> u = Utm(31, 'N', 448251, 5411932) >>> m = u.toMgrs() # 31U DQ 48251 11932 ''' _TypeError(Utm, utm=utm) # Utm, Etm e, n = utm.to2en(falsed=True) # truncate east-/northing to within 100 km grid square # XXX add rounding to nm precision? E, e = divmod(e, _100km) N, n = divmod(n, _100km) # columns in zone 1 are A-H, zone 2 J-R, zone 3 S-Z, then # repeating every 3rd zone (note -1 because eastings start # at 166e3 due to 500km false origin) z = utm.zone - 1 en = ( _Le100k[z % 3][int(E) - 1] + # rows in even zones are A-V, in odd zones are F-E _Ln100k[z % 2][int(N) % len(_Ln100k[0])]) if Mgrs is None: r = Mgrs6Tuple(utm.zone, en, e, n, utm.band, utm.datum) else: r = Mgrs(utm.zone, en, e, n, band=utm.band, datum=utm.datum) return _xnamed(r, name or utm.name)
def __new__(cls, eisu): '''New L{Epsg} European Petroleum Survey Group (EPSG) code from a UTM/USP coordinate or other EPSG code. @param eisu: Other (L{Epsg}, C{int}, C{str}, L{Utm} or L{Ups}). @return: New L{Epsg}. @raise TypeError: Invalid B{C{eisu}}. @raise EPSGError: Invalid B{C{eisu}}. ''' if isinstance(eisu, Epsg): self = int.__new__(cls, int(eisu)) self._band = eisu.band self._epsg = self # XXX eisu self._hemisphere = eisu.hemisphere self._utmups = eisu.utmups self._zone = eisu.zone if eisu.name: self.name = eisu.name elif isinstance(eisu, _Ints): self = int.__new__(cls, eisu) self._epsg = eisu self._zone, self._hemisphere = decode2(eisu) # PYCHOK UtmUps2Tuple elif isinstance(eisu, _Strs): self = encode(eisu) else: u = eisu _TypeError(Utm, Ups, eisu=u) self = encode(u.zone, hemipole=u.hemisphere, band=u.band) # PYCHOK **kwds self._utmups = u if u.name: self.name = u.name return self
def destinationNed(self, delta): '''Calculate the destination point using the supplied NED delta from this point. @param delta: Delta from this to the other point in the local tangent plane (LTP) of this point (L{Ned}). @return: Destination point (L{Cartesian}). @raise TypeError: If {delta} is not L{Ned}. @example: >>> a = LatLon(49.66618, 3.45063) >>> delta = toNed(116807.681, 222.493, -0.5245) # [N:-86126, E:-78900, D:1069] >>> b = a.destinationNed(delta) # 48.88667°N, 002.37472°E @JSname: I{destinationPoint}. ''' _TypeError(Ned, delta=delta) n, e, d = self._rotation3() # convert NED delta to standard Vector3d in coordinate frame of n-vector dn = delta.toVector3d().to3xyz() # rotate dn to get delta in cartesian (ECEF) coordinate # reference frame using the rotation matrix column vectors dc = Cartesian(fdot(dn, n.x, e.x, d.x), fdot(dn, n.y, e.y, d.y), fdot(dn, n.z, e.z, d.z)) # apply (cartesian) delta to this Cartesian to # obtain destination point as cartesian v = self.toCartesian().plus(dc) # the plus() gives a plain vector return v.toLatLon( datum=self.datum, LatLon=self.classof) # Cartesian(v.x, v.y, v.z).toLatLon(...)
def __init__(self, x, y, z, h=0, datum=None, ll=None, name=''): '''New n-vector normal to the earth's surface. @param x: X component (C{meter}). @param y: Y component (C{meter}). @param z: Z component (C{meter}). @keyword h: Optional height above model surface (C{meter}). @keyword datum: Optional datum this n-vector is defined within (L{Datum}). @keyword ll: Optional, original latlon (C{LatLon}). @keyword name: Optional name (C{str}). @raise TypeError: If B{C{datum}} is not a L{Datum}. @example: >>> from ellipsoidalNvector import Nvector >>> v = Nvector(0.5, 0.5, 0.7071, 1) >>> v.toLatLon() # 45.0°N, 045.0°E, +1.00m ''' NvectorBase.__init__(self, x, y, z, h=h, ll=ll, name=name) if datum: _TypeError(Datum, datum=datum) self._datum = datum