def geteastingshift(self, zonen: float, zonel: float) -> Optional[float]: """ If the lat, lon coordinates being converted are located in a different UTM zone than the canvas reference point, the UTM meters may need to be shifted. This picks a reference point in the same longitudinal band (UTM zone number) as the provided zone, to calculate the shift in meters for the x coordinate. :param zonen: zonen :param zonel: zone1 :return: the x shift value """ rzonen = int(self.refutm[0][0]) # same zone number, no x shift required if zonen == rzonen: return None z = (zonen, zonel) # x shift already calculated, cached if z in self.zoneshifts and self.zoneshifts[z][0] is not None: return self.zoneshifts[z][0] rlat, rlon, _ralt = self.refgeo # ea. zone is 6deg band lon2 = rlon + 6 * (zonen - rzonen) # ignore northing e2, _n2, _zonen2, _zonel2 = utm.from_latlon(rlat, lon2) # NOTE: great circle distance used here, not reference ellipsoid! xshift = utm.haversine(rlon, rlat, lon2, rlat) - e2 # cache the return value yshift = None if z in self.zoneshifts: yshift = self.zoneshifts[z][1] self.zoneshifts[z] = (xshift, yshift) return xshift
def getutmzoneshift( self, e: float, n: float ) -> Tuple[float, float, Tuple[float, str]]: """ Given UTM easting and northing values, check if they fall outside the reference point's zone boundary. Return the UTM coordinates in a different zone and the new zone if they do. Zone lettering is only changed when the reference point is in the opposite hemisphere. :param e: easting value :param n: northing value :return: modified easting, northing, and zone values """ zone = self.refutm[0] rlat, rlon, _ralt = self.refgeo if e > 834000 or e < 166000: num_zones = (int(e) - 166000) / (utm.R / 10) # estimate number of zones to shift, E (positive) or W (negative) rlon2 = self.refgeo[1] + (num_zones * 6) _e2, _n2, zonen2, zonel2 = utm.from_latlon(rlat, rlon2) xshift = utm.haversine(rlon, rlat, rlon2, rlat) # after >3 zones away from refpt, the above estimate won't work # (the above estimate could be improved) if not 100000 <= (e - xshift) < 1000000: # move one more zone away num_zones = (abs(num_zones) + 1) * (abs(num_zones) / num_zones) rlon2 = self.refgeo[1] + (num_zones * 6) _e2, _n2, zonen2, zonel2 = utm.from_latlon(rlat, rlon2) xshift = utm.haversine(rlon, rlat, rlon2, rlat) e = e - xshift zone = (zonen2, zonel2) if n < 0: # refpt in northern hemisphere and we crossed south of equator n += 10000000 zone = (zone[0], "M") elif n > 10000000: # refpt in southern hemisphere and we crossed north of equator n -= 10000000 zone = (zone[0], "N") return e, n, zone
def getnorthingshift(self, zonen: float, zonel: float) -> Optional[float]: """ If the lat, lon coordinates being converted are located in a different UTM zone than the canvas reference point, the UTM meters may need to be shifted. This picks a reference point in the same latitude band (UTM zone letter) as the provided zone, to calculate the shift in meters for the y coordinate. :param zonen: zonen :param zonel: zone1 :return: calculated y shift """ rzonel = self.refutm[0][1] # same zone letter, no y shift required if zonel == rzonel: return None z = (zonen, zonel) # y shift already calculated, cached if z in self.zoneshifts and self.zoneshifts[z][1] is not None: return self.zoneshifts[z][1] rlat, rlon, _ralt = self.refgeo # zonemap is used to calculate degrees difference between zone letters latshift = self.zonemap[zonel] - self.zonemap[rzonel] # ea. latitude band is 8deg high lat2 = rlat + latshift _e2, n2, _zonen2, _zonel2 = utm.from_latlon(lat2, rlon) # NOTE: great circle distance used here, not reference ellipsoid yshift = -(utm.haversine(rlon, rlat, rlon, lat2) + n2) # cache the return value xshift = None if z in self.zoneshifts: xshift = self.zoneshifts[z][0] self.zoneshifts[z] = (xshift, yshift) return yshift