def _latlon3(self, LatLon, datum): '''(INTERNAL) Convert cached LatLon ''' ll = self._latlon if LatLon is None: if datum and datum != ll.datum: raise TypeError('no %s.convertDatum: %r' % (LatLon, ll)) return _xnamed(LatLonDatum3Tuple(ll.lat, ll.lon, ll.datum), ll.name) elif issubclassof(LatLon, _LLEB): ll = _xnamed(LatLon(ll.lat, ll.lon, datum=ll.datum), ll.name) return _ll2datum(ll, datum, 'LatLon') raise TypeError('%s not ellipsoidal: %r' % ('LatLon', LatLon))
def _latlon3(self, LatLon, datum): '''(INTERNAL) Convert cached LatLon ''' ll = self._latlon if LatLon is None: if datum and datum != ll.datum: raise TypeError('no %s.convertDatum: %r' % (LatLon, ll)) return _xnamed(LatLonDatum3Tuple(ll.lat, ll.lon, ll.datum), ll.name) else: _xsubclassof(_LLEB, LatLon=LatLon) ll = _xnamed(LatLon(ll.lat, ll.lon, datum=ll.datum), ll.name) return _ll2datum(ll, datum, 'LatLon')
def _latlon3(self, LatLon, datum): '''(INTERNAL) Convert cached LatLon ''' ll = self._latlon if LatLon is None: if datum and datum != ll.datum: raise _TypeError(latlon=ll, txt=_item_ps(_no_convertDatum_, datum.name)) return _xnamed(LatLonDatum3Tuple(ll.lat, ll.lon, ll.datum), ll.name) else: _xsubclassof(_LLEB, LatLon=LatLon) ll = _xnamed(LatLon(ll.lat, ll.lon, datum=ll.datum), ll.name) return _ll2datum(ll, datum, _LatLon_)
def meanOf(points, datum=Datums.WGS84, height=None, LatLon=LatLon, **LatLon_kwds): '''Compute the geographic mean of several points. @arg points: Points to be averaged (L{LatLon}[]). @kwarg datum: Optional datum to use (L{Datum}). @kwarg height: Optional height at mean point, overriding the mean height (C{meter}). @kwarg LatLon: Optional class to return the mean point (L{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}. @return: Geographic mean point and mean height (B{C{LatLon}}) or a L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}} is C{None}. @raise ValueError: Insufficient number of B{C{points}}. ''' _, points = _Nvll.points2(points, closed=False) # geographic mean m = sumOf(p._N_vector for p in points) lat, lon, h = m._N_vector.latlonheight if height is not None: h = height if LatLon is None: r = LatLon3Tuple(lat, lon, h) else: kwds = _xkwds(LatLon_kwds, height=h, datum=datum) r = LatLon(lat, lon, **kwds) return _xnamed(r, meanOf.__name__)
def reverse(self, x, y, name=NN, LatLon=None, **LatLon_kwds): '''Convert an azimuthal equidistant location to (ellipsoidal) geodetic lat- and longitude. @arg x: Easting of the location (C{meter}). @arg y: Northing of the location (C{meter}). @kwarg name: Optional name for the location (C{str}). @kwarg LatLon: Class to use (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}. @return: The geodetic (C{LatLon}) or if B{C{LatLon}} is C{None} an L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}. @note: The C{lat} will be in the range C{[-90..90] degrees} and C{lon} in the range C{[-180..180] degrees}. The scale of the projection is C{1} in I{radial} direction, C{azimuth} clockwise from true North and is C{1 / reciprocal} in the direction perpendicular to this. ''' x = Scalar(x, name=_x_) y = Scalar(y, name=_y_) z = atan2d(x, y) # (x, y) for azimuth from true North s = hypot(x, y) r = self.geodesic.Direct(self.lat0, self.lon0, z, s, self._mask) t = self._toLatLon(r.lat2, r.lon2, LatLon, LatLon_kwds) if LatLon else \ Azimuthal7Tuple(x, y, r.lat2, r.lon2, r.azi2, self._1_rk(r), self.datum) return _xnamed(t, name or self.name)
def forward(self, lat, lon, name=NN): '''Convert an (ellipsoidal) geodetic location to azimuthal equidistant east- and northing. @arg lat: Latitude of the location (C{degrees90}). @arg lon: Longitude of the location (C{degrees180}). @kwarg name: Optional name for the location (C{str}). @return: An L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)} with C{x} and C{y} in C{meter} and C{lat} and C{lon} in C{degrees}. The C{scale} of the projection is C{1} in I{radial} direction, C{azimuth} clockwise from true North and is C{1 / reciprocal} in the direction perpendicular to this. @see: Method L{EquidistantKarney.reverse}. A call to C{.forward} followed by a call to C{.reverse} will return the original C{lat, lon} to within roundoff. @raise AzimuthalError: Invalid B{C{lat}} or B{C{lon}}. ''' r = self.geodesic.Inverse(self.lat0, self.lon0, Lat_(lat), Lon_(lon), self._mask) x, y = sincos2d(r.azi1) t = Azimuthal7Tuple(x * r.s12, y * r.s12, r.lat2, r.lon2, r.azi2, self._1_rk(r), self.datum) return _xnamed(t, name or self.name)
def _toXtm8( Xtm, z, lat, x, y, B, d, c, k, f, # PYCHOK 13+ args name, latlon, eps, Error=UTMError): '''(INTERNAL) Helper for L{toEtm8} and L{toUtm8}. ''' h = _hemi(lat) if f: x, y = _false2(x, y, h) if Xtm is None: # DEPRECATED r = UtmUps8Tuple(z, h, x, y, B, d, c, k, Error=Error) else: r = Xtm(z, h, x, y, band=B, datum=d, falsed=f, convergence=c, scale=k) if isinstance(latlon, _LLEB) and d is latlon.datum: r._latlon_to(latlon, eps, f) # XXX weakref(latlon)? latlon._convergence = c latlon._scale = k return _xnamed(r, name)
def parse3d(str3d, sep=_COMMA_, name=NN, Vector=Vector3d, **Vector_kwds): '''Parse an C{"x, y, z"} string. @arg str3d: X, y and z values (C{str}). @kwarg sep: Optional separator (C{str}). @kwarg name: Optional instance name (C{str}). @kwarg Vector: Optional class (L{Vector3d}). @kwarg Vector_kwds: Optional B{C{Vector}} keyword arguments, ignored if B{C{Vector=None}}. @return: New B{C{Vector}} or if B{C{Vector}} is C{None}, a L{Vector3Tuple}C{(x, y, z)}. @raise VectorError: Invalid B{C{str3d}}. ''' try: v = [float(v.strip()) for v in str3d.split(sep)] if len(v) != 3: raise ValueError except (TypeError, ValueError) as x: raise VectorError(str3d=str3d, txt=str(x)) r = Vector3Tuple(*v) if Vector is None else \ Vector(*v, **Vector_kwds) return _xnamed(r, name, force=True)
def _OSGR_(strOSGR, Osgr, name): s = strOSGR.strip() g = s.split(_COMMA_) if len(g) == 2: # "easting,northing" if len(s) < 13: raise ValueError e, n = map(_s2f, g) else: # "GR easting northing" g, s = s[:2], s[2:].strip() e, n = map(_c2i, g) n, m = divmod(n, 5) E = ((e - 2) % 5) * 5 + m N = 19 - (e // 5) * 5 - n if 0 > E or E > 6 or \ 0 > N or N > 12: raise ValueError g = s.split() if len(g) == 1: # no whitespace e, n = halfs2(s) elif len(g) == 2: e, n = g else: raise ValueError e = _s2i(E, e) n = _s2i(N, n) r = _EasNor2Tuple(e, n) if Osgr is None else Osgr(e, n) return _xnamed(r, name, force=True)
def parseUTM5(strUTM, datum=Datums.WGS84, Utm=Utm, falsed=True, name=''): '''Parse a string representing a UTM coordinate, consisting of C{"zone[band] hemisphere easting northing"}. @param strUTM: A UTM coordinate (C{str}). @keyword datum: Optional datum to use (L{Datum}). @keyword Utm: Optional (sub-)class to return the UTM coordinate (L{Utm}) or C{None}. @keyword falsed: Both easting and northing are falsed (C{bool}). @keyword name: Optional B{C{Utm}} name (C{str}). @return: The UTM coordinate (B{C{Utm}}) or a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)} if B{C{Utm}} is C{None}. The C{hemipole} is the hemisphere C{'N'|'S'}. @raise UTMError: Invalid B{C{strUTM}}. @example: >>> u = parseUTM5('31 N 448251 5411932') >>> u.toStr2() # [Z:31, H:N, E:448251, N:5411932] >>> u = parseUTM5('31 N 448251.8 5411932.7') >>> u.toStr() # 31 N 448252 5411933 ''' r = _parseUTM5(strUTM, UTMError) if Utm is not None: z, h, e, n, B = r r = Utm(z, h, e, n, band=B, datum=datum, falsed=falsed) return _xnamed(r, name)
def meanOf(points, height=None, LatLon=LatLon): '''Compute the geographic mean of several points. @param points: Points to be averaged (L{LatLon}[]). @keyword height: Optional height at mean point, overriding the mean height (C{meter}). @keyword LatLon: Optional (sub-)class to return the mean point (L{LatLon}) or C{None}. @return: Point at geographic mean and height (B{C{LatLon}}) or a L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}} is C{None}. @raise TypeError: Some B{C{points}} are not L{LatLon}. @raise ValueError: No B{C{points}}. ''' # geographic mean n, points = _Trll.points2(points, closed=False) m = sumOf(points[i].toVector3d() for i in range(n)) a, b = m.to2ll() if height is None: h = fmean(points[i].height for i in range(n)) else: h = height r = LatLon3Tuple(a, b, h) if LatLon is None else \ LatLon(a, b, height=h) return _xnamed(r, meanOf.__name__)
def parseUTM5(strUTM, datum=Datums.WGS84, Utm=Utm, falsed=True, name=NN): '''Parse a string representing a UTM coordinate, consisting of C{"zone[band] hemisphere easting northing"}. @arg strUTM: A UTM coordinate (C{str}). @kwarg datum: Optional datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg Utm: Optional class to return the UTM coordinate (L{Utm}) or C{None}. @kwarg falsed: Both easting and northing are falsed (C{bool}). @kwarg name: Optional B{C{Utm}} name (C{str}). @return: The UTM coordinate (B{C{Utm}}) or if B{C{Utm}} is C{None}, a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)}. The C{hemipole} is the C{'N'|'S'} hemisphere. @raise UTMError: Invalid B{C{strUTM}}. @raise TypeError: Invalid B{C{datum}}. @example: >>> u = parseUTM5('31 N 448251 5411932') >>> u.toStr2() # [Z:31, H:N, E:448251, N:5411932] >>> u = parseUTM5('31 N 448251.8 5411932.7') >>> u.toStr() # 31 N 448252 5411933 ''' r = _parseUTM5(strUTM, datum, Utm, falsed) return _xnamed(r, name, force=True)
def parseWM(strWM, radius=R_MA, Wm=Wm, name=''): '''Parse a string representing a WM coordinate, consisting of easting, northing and an optional radius. @param strWM: A WM coordinate (C{str}). @keyword radius: Optional earth radius (C{meter}). @keyword Wm: Optional (sub-)class to return the WM coordinate (L{Wm}) or C{None}. @keyword name: Optional name (C{str}). @return: The WM coordinate (B{C{Wm}}) or an L{EasNorRadius3Tuple}C{(easting, northing, radius)} if B{C{Wm}} is C{None}. @raise WebMercatorError: Invalid B{C{strWM}}. @example: >>> u = parseWM('448251 5411932') >>> u.toStr2() # [E:448251, N:5411932] ''' w = strWM.strip().replace(',', ' ').split() try: if len(w) == 2: w += [radius] elif len(w) != 3: raise ValueError # caught below x, y, r = map(float, w) except (TypeError, ValueError): raise WebMercatorError('%s invalid: %r' % ('strWM', strWM)) r = EasNorRadius3Tuple(x, y, r) if Wm is None else \ Wm(x, y, radius=r) return _xnamed(r, name)
def _reverse(self, x, y, name, LatLon, LatLon_kwds, _c_t, lea): '''(INTERNAL) Azimuthal (spherical) reverse C{x, y} to C{lat, lon}. ''' x = Scalar(x, name=_x_) y = Scalar(y, name=_y_) r = hypot(x, y) c, t = _c_t(r / self.radius) if t: s0, c0 = self._sc0 sc, cc = sincos2(c) k = c / sc z = atan2d(x, y) # (x, y) for azimuth from true North lat = degrees(asin1(s0 * cc + c0 * sc * (y / r))) if lea or abs(c0) > EPS: lon = atan2(x * sc, c0 * cc * r - s0 * sc * y) else: lon = atan2(x, (y if s0 < 0 else -y)) lon = _norm180(self.lon0 + degrees(lon)) else: k, z = 1, 0 lat, lon = self.latlon0 t = self._toLatLon(lat, lon, LatLon, LatLon_kwds) if LatLon else \ Azimuthal7Tuple(x, y, lat, lon, z, k, self.datum) return _xnamed(t, name or self.name)
def toLcc(latlon, conic=Conics.WRF_Lb, height=None, Lcc=Lcc, name=''): '''Convert an (ellipsoidal) geodetic point to a Lambert location. @param latlon: Ellipsoidal point (C{LatLon}). @keyword conic: Optional Lambert projection to use (L{Conic}). @keyword height: Optional height for the point, overriding the default height (C{meter}). @keyword Lcc: Optional (sub-)class to return the Lambert location (L{Lcc}). @keyword name: Optional B{C{Lcc}} name (C{str}). @return: The Lambert location (L{Lcc}) or an L{EasNor3Tuple}C{(easting, northing, height)} if B{C{Lcc}} is C{None}. @raise TypeError: If B{C{latlon}} is not ellipsoidal. ''' if not isinstance(latlon, _LLEB): raise _IsNotError(_LLEB.__name__, latlon=latlon) a, b = latlon.to2ab() c = conic.toDatum(latlon.datum) t = c._n * (b - c._lon0) - c._opt3 st, ct = sincos2(t) r = c._rdef(c._tdef(a)) e = c._E0 + r * st n = c._N0 + c._r0 - r * ct h = latlon.height if height is None else height r = EasNor3Tuple(e, n, h) if Lcc is None else \ Lcc(e, n, h=h, conic=c) return _xnamed(r, name or nameof(latlon))
def toNed(distance, bearing, elevation, Ned=Ned, name=NN): '''Create an NED vector from distance, bearing and elevation (in local coordinate system). @arg distance: NED vector length (C{meter}). @arg bearing: NED vector bearing (compass C{degrees360}). @arg elevation: NED vector elevation from local coordinate frame horizontal (C{degrees}). @kwarg Ned: Optional class to return the NED (L{Ned}) or C{None}. @kwarg name: Optional name (C{str}). @return: An NED vector equivalent to this B{C{distance}}, B{C{bearing}} and B{C{elevation}} (L{Ned}) or if B{C{Ned=None}}, an L{Ned3Tuple}C{(north, east, down)}. @raise ValueError: Invalid B{C{distance}}, B{C{bearing}} or B{C{elevation}}. @JSname: I{fromDistanceBearingElevation}. ''' d = Distance(distance) sb, cb, se, ce = sincos2d(Bearing(bearing), Height(elevation=elevation)) n = cb * d * ce e = sb * d * ce d *= se r = Ned3Tuple(n, e, -d) if Ned is None else \ Ned(n, e, -d) return _xnamed(r, name)
def parseUPS5(strUPS, datum=Datums.WGS84, Ups=Ups, falsed=True, name=NN): '''Parse a string representing a UPS coordinate, consisting of C{"[zone][band] pole easting northing"} where B{C{zone}} is pseudo zone C{"00"|"0"|""} and C{band} is C{'A'|'B'|'Y'|'Z'|''}. @arg strUPS: A UPS coordinate (C{str}). @kwarg datum: Optional datum to use (L{Datum}). @kwarg Ups: Optional class to return the UPS coordinate (L{Ups}) or C{None}. @kwarg falsed: Both B{C{easting}} and B{C{northing}} are falsed (C{bool}). @kwarg name: Optional B{C{Ups}} name (C{str}). @return: The UPS coordinate (B{C{Ups}}) or a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)} if B{C{Ups}} is C{None}. The C{hemipole} is the C{'N'|'S'} pole, the UPS projection top/center. @raise UPSError: Invalid B{C{strUPS}}. ''' z, p, e, n, B = _parseUTMUPS5(strUPS, _UPS_ZONE_STR, Error=UPSError) if z != _UPS_ZONE or (B and B not in _Bands): raise UPSError(strUPS=strUPS, zone=z, band=B) r = UtmUps5Tuple(z, p, e, n, B, Error=UPSError) if Ups is None else \ Ups(z, p, e, n, band=B, falsed=falsed, datum=datum) return _xnamed(r, name, force=True)
def meanOf(points, datum=Datums.WGS84, height=None, LatLon=LatLon): '''Compute the geographic mean of several points. @param points: Points to be averaged (L{LatLon}[]). @keyword datum: Optional datum to use (L{Datum}). @keyword height: Optional height at mean point, overriding the mean height (C{meter}). @keyword LatLon: Optional (sub-)class to return the mean point (L{LatLon}) or C{None}. @return: Geographic mean point and mean height (B{C{LatLon}}) or a L{LatLon3Tuple}C{(lat, lon, height)} if B{C{LatLon}} is C{None}. @raise ValueError: Insufficient number of B{C{points}}. ''' _, points = _Nvll.points2(points, closed=False) # geographic mean m = sumOf(p.toNvector() for p in points) a, b, h = m.to3llh() if height is not None: h = height r = LatLon3Tuple(a, b, h) if LatLon is None else \ LatLon(a, b, height=h, datum=datum) return _xnamed(r, meanOf.__name__)
def _V_n(v, name, Vector, Vector_kwds): # return a named Vector instance if Vector is None: v = _xnamed(v, name) else: kwds = _xkwds(Vector_kwds, name=name) v = Vector(v.x, v.y, v.z, **kwds) return v
def _latlon4(t, h, n): if LatLon is None: r = LatLon4Tuple(t.lat, t.lon, h, t.datum) else: kwds = _xkwds(LatLon_kwds, datum=t.datum, height=h) r = LatLon(t.lat, t.lon, **kwds) r._iteration = t.iteration # ._iteration for tests return _xnamed(r, n)
def _latlon3(lat, lon, height, func, LatLon, **LatLon_kwds): '''(INTERNAL) Helper for L{intersection} and L{meanof}. ''' if LatLon is None: r = LatLon3Tuple(lat, lon, height) else: kwds = _xkwds(LatLon_kwds, height=height) r = LatLon(lat, lon, **kwds) return _xnamed(r, func.__name__)
def parseMGRS(strMGRS, datum=Datums.WGS84, Mgrs=Mgrs, name=''): '''Parse a string representing a MGRS grid reference, consisting of zoneBand, grid, easting and northing. @arg strMGRS: MGRS grid reference (C{str}). @kwarg datum: Optional datum to use (L{Datum}). @kwarg Mgrs: Optional class to return the MGRS grid reference (L{Mgrs}) or C{None}. @kwarg name: Optional B{C{Mgrs}} name (C{str}). @return: The MGRS grid reference (B{L{Mgrs}}) or an L{Mgrs4Tuple}C{(zone, digraph, easting, northing)} if B{C{Mgrs}} is C{None}. @raise MGRSError: Invalid B{C{strMGRS}}. @example: >>> m = parseMGRS('31U DQ 48251 11932') >>> str(m) # 31U DQ 48251 11932 >>> m = parseMGRS('31UDQ4825111932') >>> repr(m) # [Z:31U, G:DQ, E:48251, N:11932] >>> m = mgrs.parseMGRS('42SXD0970538646') >>> str(m) # 42S XD 09705 38646 >>> m = mgrs.parseMGRS('42SXD9738') # Km >>> str(m) # 42S XD 97000 38000 ''' def _mg(cre, s): # return re.match groups m = cre.match(s) if not m: raise ValueError return m.groups() def _s2m(g): # e or n string to float meter # convert to meter if less than 5 digits m = g + '00000' return float(m[:5]) m = tuple(strMGRS.strip().replace(',', ' ').split()) try: if len(m) == 1: # 01ABC1234512345' m = _mg(_MGRSre, m[0]) m = m[:2] + halfs2(m[2]) elif len(m) == 2: # 01ABC 1234512345' m = _mg(_ZBGre, m[0]) + halfs2(m[1]) elif len(m) == 3: # 01ABC 12345 12345' m = _mg(_ZBGre, m[0]) + m[1:] if len(m) != 4: # 01A BC 1234 12345 raise ValueError e, n = map(_s2m, m[2:]) except (TypeError, ValueError): raise InvalidError(strMGRS=strMGRS, Error=MGRSError) z, EN = m[0], m[1].upper() r = Mgrs4Tuple(z, EN, e, n) if Mgrs is None else \ Mgrs(z, EN, e, n, datum=datum) return _xnamed(r, name)
def reverse(self, x, y, name=NN, LatLon=None, **LatLon_kwds): '''Convert an azimuthal gnomonic location to (ellipsoidal) geodetic lat- and longitude. @arg x: Easting of the location (C{meter}). @arg y: Northing of the location (C{meter}). @kwarg name: Optional name for the location (C{str}). @kwarg LatLon: Class to use (C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}. @return: The geodetic (C{LatLon}) or if B{C{LatLon}} is C{None} an L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}. @raise AzimuthalError: No convergence. @note: The C{lat} will be in the range C{[-90..90] degrees} and C{lon} in the range C{[-180..180] degrees}. The C{azimuth} is clockwise from true North. The scale is C{1 / reciprocal**2} in C{radial} direction and C{1 / reciprocal} in the direction perpendicular to this. ''' x = Scalar(x, name=_x_) y = Scalar(y, name=_y_) z = atan2d(x, y) # (x, y) for azimuth from true North q = hypot(x, y) d = e = self.equatoradius s = e * atan(q / e) if q > e: def _d(r, q): return (r.M12 - q * r.m12) * r.m12 # negated q = 1 / q else: # little == True def _d(r, q): # PYCHOK _d return (q * r.M12 - r.m12) * r.M12 # negated e *= _EPS_Karney S = Fsum(s) g = self.geodesic.Line(self.lat0, self.lon0, z, self._mask) for self._iteration in range(1, _TRIPS): r = g.Position(s, self._mask) if abs(d) < e: break s, d = S.fsum2_(_d(r, q)) else: raise AzimuthalError(x=x, y=y, txt=_no_convergence_fmt_ % (e, )) t = self._toLatLon(r.lat2, r.lon2, LatLon, LatLon_kwds) if LatLon else \ Azimuthal7Tuple(x, y, r.lat2, r.lon2, r.azi2, r.M12, self.datum) t._iteration = self._iteration return _xnamed(t, name or self.name)
def parseMGRS(strMGRS, datum=Datums.WGS84, Mgrs=Mgrs, name=''): '''Parse a string representing a MGRS grid reference, consisting of zoneBand, grid, easting and northing. @param strMGRS: MGRS grid reference (C{str}). @keyword datum: Optional datum to use (L{Datum}). @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{Mgrs4Tuple}C{(zone, digraph, easting, northing)} if B{C{Mgrs}} is C{None}. @raise MGRSError: Invalid B{C{strMGRS}}. @example: >>> m = parseMGRS('31U DQ 48251 11932') >>> str(m) # 31U DQ 48251 11932 >>> m = parseMGRS('31UDQ4825111932') >>> repr(m) # [Z:31U, G:DQ, E:48251, N:11932] ''' def _mg(cre, s): # return re.match groups m = cre.match(s) if not m: raise ValueError return m.groups() def _s2m(g): # e or n string to meter f = float(g) if f > 0: x = int(log10(f)) if 0 <= x < 4: # at least 5 digits f *= (10000, 1000, 100, 10)[x] return f m = tuple(strMGRS.strip().replace(',', ' ').split()) try: if len(m) == 1: # 01ABC1234512345' m = _mg(_MGRSre, m[0]) m = m[:2] + halfs2(m[2]) elif len(m) == 2: # 01ABC 1234512345' m = _mg(_GZDre, m[0]) + halfs2(m[1]) elif len(m) == 3: # 01ABC 12345 12345' m = _mg(_GZDre, m[0]) + m[1:] if len(m) != 4: # 01A BC 1234 12345 raise ValueError e, n = map(_s2m, m[2:]) except (TypeError, ValueError): raise MGRSError('%s invalid: %r' % ('strMGRS', strMGRS)) z, EN = m[0], m[1].upper() r = Mgrs4Tuple(z, EN, e, n) if Mgrs is None else \ Mgrs(z, EN, e, n, datum=datum) return _xnamed(r, name)
def forward(self, lat, lon, lon0=0, name=NN): '''Convert a geodetic location to east- and northing. @arg lat: Latitude of the location (C{degrees}). @arg lon: Longitude of the location (C{degrees}). @kwarg lon0: Optional central meridian longitude (C{degrees}). @kwarg name: Optional name for the location (C{str}). @return: An L{Albers7Tuple}C{(x, y, lat, lon, gamma, scale, datum)}. @note: The origin latitude is returned by C{property lat0}. No false easting or northing is added. The value of B{C{lat}} should be in the range C{[-90..90] degrees}. The returned values C{x} and C{y} will be large but finite for points projecting to infinity, i.e. one or both of the poles. ''' E = self.datum.ellipsoid s = self._sign k0 = self._k0 n0 = self._n0 nrho0 = self._nrho0 txi0 = self._txi0 sa, ca = sincos2d(_Lat_(lat) * s) ca = max(_EPSX, ca) ta = sa / ca _, sxi, txi = self._cstxif3(ta) dq = self._qZ * _Dsn(txi, txi0, sxi, self._sxi0) * (txi - txi0) drho = -E.a * dq / (sqrt(self._m02 - n0 * dq) + self._m0) lon = _Lon_(lon) if lon0: lon, _ = _diff182(_Lon_(lon0, name=_lon0_), lon) b = radians(lon) th = self._k02n0 * b sth, cth = sincos2(th) # XXX sin, cos if n0: x = sth / n0 y = (1 - cth if cth < 0 else sth**2 / (1 + cth)) / n0 else: x = self._k02 * b y = 0 t = nrho0 + n0 * drho x = t * x / k0 y = s * (nrho0 * y - drho * cth) / k0 g = degrees360(s * th) if t: k0 *= t * hypot1(E.b_a * ta) / E.a t = Albers7Tuple(x, y, lat, lon, g, k0, self.datum) return _xnamed(t, name or self.name)
def toWm(latlon, lon=None, radius=R_MA, Wm=Wm, name='', **Wm_kwds): '''Convert a lat-/longitude point to a WM coordinate. @arg latlon: Latitude (C{degrees}) or an (ellipsoidal or spherical) geodetic C{LatLon} point. @kwarg lon: Optional longitude (C{degrees} or C{None}). @kwarg radius: Optional earth radius (C{meter}). @kwarg Wm: Optional class to return the WM coordinate (L{Wm}) or C{None}. @kwarg name: Optional name (C{str}). @kwarg Wm_kwds: Optional, additional B{C{Wm}} keyword arguments, ignored if B{C{Wm=None}}. @return: The WM coordinate (B{C{Wm}}) or an L{EasNorRadius3Tuple}C{(easting, northing, radius)} if B{C{Wm}} is C{None}. @raise ValueError: If B{C{lon}} value is missing, if B{C{latlon}} is not scalar, if B{C{latlon}} is beyond the valid WM range and L{rangerrors} is set to C{True} or if B{C{radius}} is invalid. @example: >>> p = LatLon(48.8582, 2.2945) # 448251.8 5411932.7 >>> w = toWm(p) # 448252 5411933 >>> p = LatLon(13.4125, 103.8667) # 377302.4 1483034.8 >>> w = toWm(p) # 377302 1483035 ''' e, r = None, Radius(radius) try: lat, lon = latlon.lat, latlon.lon if isinstance(latlon, _LLEB): r = latlon.datum.ellipsoid.a e = latlon.datum.ellipsoid.e if not name: # use latlon.name name = nameof(latlon) lat = clipDegrees(lat, _LatLimit) except AttributeError: lat, lon = parseDMS2(latlon, lon, clipLat=_LatLimit) s = sin(radians(lat)) y = atanh(s) # == log(tan((90 + lat) / 2)) == log(tanPI_2_2(radians(lat))) if e: y -= e * atanh(e * s) e, n = r * radians(lon), r * y if Wm is None: r = EasNorRadius3Tuple(e, n, r) else: kwds = _xkwds(Wm_kwds, radius=r) r = Wm(e, n, **kwds) return _xnamed(r, name)
def _WM_(strWM, radius, Wm, name): w = strWM.replace(_COMMA_, _SPACE_).strip().split() if len(w) == 2: w += [radius] elif len(w) != 3: raise ValueError x, y, r = map(float, w) r = EasNorRadius3Tuple(x, y, r) if Wm is None else \ Wm(x, y, radius=r) return _xnamed(r, name, force=True)
def decode3(garef, center=True): '''Decode a C{garef} to lat-, longitude and precision. @param garef: To be decoded (L{Garef} or C{str}). @keyword center: If C{True} the center, otherwise the south-west, lower-left corner (C{bool}). @return: A L{LatLonPrec3Tuple}C{(lat, lon, precision)}. @raise GARSError: Invalid B{C{garef}}, INValid, non-alphanumeric or bad length B{C{garef}}. ''' def _Error(i): return GARSError('%s invalid: %r[%s]' % ('garef', garef, i)) def _ll(chars, g, i, j, lo, hi): ll, b = 0, len(chars) for i in range(i, j): d = chars.find(g[i]) if d < 0: raise _Error(i) ll = ll * b + d if ll < lo or ll > hi: raise _Error(j) return ll def _ll2(lon, lat, g, i, m): d = _Digits.find(g[i]) if d < 1 or d > m * m: raise _Error(i) d, r = divmod(d - 1, m) lon = lon * m + r lat = lat * m + (m - 1 - d) return lon, lat g, precision = _2garstr2(garef) lon = _ll(_Digits, g, 0, _LonLen, 1, 720) + _LonOrig_M1_1 lat = _ll(_Letters, g, _LonLen, _MinLen, 0, 359) + _LatOrig_M1 if precision > 0: lon, lat = _ll2(lon, lat, g, _MinLen, _M2) if precision > 1: lon, lat = _ll2(lon, lat, g, _MinLen + 1, _M3) r = _Resolutions[precision] # == 1.0 / unit if center: lon = lon * 2 + 1 lat = lat * 2 + 1 r *= 0.5 lon *= r lat *= r return _xnamed(LatLonPrec3Tuple(lat, lon, precision), nameof(garef))
def _latlon3(self, LatLon, datum): '''(INTERNAL) Convert cached latlon to C{LatLon} ''' ll = self._latlon if LatLon is None: r = _ll2datum(ll, datum, LatLonDatum3Tuple.__name__) r = LatLonDatum3Tuple(r.lat, r.lon, r.datum) else: # must be ellipsoidal _xsubclassof(_LLEB, LatLon=LatLon) r = _ll2datum(ll, datum, LatLon.__name__) r = LatLon(r.lat, r.lon, datum=r.datum) r._iteration = ll._iteration return _xnamed(r, ll)
def _latlon5(self, LatLon, **LatLon_kwds): '''(INTERNAL) Convert cached LatLon ''' ll = self._latlon if LatLon is None: r = LatLonDatum5Tuple(ll.lat, ll.lon, ll.datum, ll.convergence, ll.scale) else: _xsubclassof(_LLEB, LatLon=LatLon) kwds = _xkwds(LatLon_kwds, datum=ll.datum) r = _xattrs(LatLon(ll.lat, ll.lon, **kwds), ll, '_convergence', '_scale') return _xnamed(r, ll.name)