def __init__(self, fname): # to be filled now... self.__authorName = "Unknown author" self.__authorLinks = set() # all links associated with the author self.__trackName = "Unnamed track" self.__trackLinks = set( ) # all links associated with the track in general self.__trackCoords = list() self.__maxElevation = None # maximum elevation in feet. none if unknown. # read and parse the xml file xml_data = xml.etree.ElementTree.parse(fname) xml_root = xml_data.getroot() xml_namespace = xml_root.tag.split("}")[0].strip("{}") xml_namespace = {"": xml_namespace} # collect the metadata track_name_node = xml_data.find("./metadata/name", xml_namespace) if not track_name_node is None: self.__trackName = track_name_node.text track_link_nodes = xml_data.findall("./metadata/link", xml_namespace) for node in track_link_nodes: self.__trackLinks.add(node.attrib["href"]) author_name_node = xml_data.find("./metadata/author/name", xml_namespace) if not author_name_node is None: self.__authorName = author_name_node.text author_link_nodes = xml_data.findall("./metadata/author/link", xml_namespace) for node in author_link_nodes: self.__authorLinks.add(node.attrib["href"]) # choose track point nodes track_point_nodes = _choose_track_point_nodes( xml_data.findall("./trk/trkseg", xml_namespace), xml_namespace) # collect the track coordinates if len(track_point_nodes) > 1: for node in track_point_nodes: # raw coordinates as strings lat = node.attrib["lat"] lon = node.attrib["lon"] # parse the coordinates lat = LatLon23.Latitude(float(lat)) lon = LatLon23.Longitude(float(lon)) coord = LatLon23.LatLon(lat, lon) # append the coordinates self.__trackCoords.append(coord) # elevation in this point? elevation_node = node.find("ele", xml_namespace) if not elevation_node is None: if self.__maxElevation is None: self.__maxElevation = 0.0 elevation = float(elevation_node.text) self.__maxElevation = max(self.__maxElevation, elevation)
def routagePointDistance (latA, lonA, distance, hdg, unit='nm'): """ Returns the point from (latA, lonA) to the given (distance, hdg) """ if unit == 'nm': d = nm2km(distance) elif unit == 'km': d = distance p = LatLon23.LatLon(LatLon23.Latitude(latA), LatLon23.Longitude(lonA)) of = p.offset (math.degrees (hdg), d).to_string('D') return (float (of[0]), float (of[1]))
def find_nearest(self, lat, lon): # convert to integers lat_idx = int(lat + 180.0) lon_idx = int(lon + 180.0) assert lat_idx >= 0 and lon_idx >= 0 # find candidate airports lat_cands = set() lon_cands = set() for offset in range(-3, +4): # find candidates with latitude offset cand_idx = (lat_idx + offset) % 360 if not self.__idxLat[cand_idx] is None: lat_cands.update(self.__idxLat[cand_idx]) # find candidates with longitude offset cand_idx = (lon_idx + offset) % 360 if not self.__idxLon[cand_idx] is None: lon_cands.update(self.__idxLon[cand_idx]) cands = lat_cands.intersection(lon_cands) # target coordiate target_coord = LatLon23.LatLon(lat, lon) nearest_icao = None nearest_dist = 999999999.999 # check all the candidates for icao in cands: # candidate coordinate info = self.__airportDict[icao] cand_coord = LatLon23.LatLon(info["lat"], info["lon"]) # distance to the candidate dist = target_coord.distance(cand_coord) # smaller distance? if dist < nearest_dist: nearest_icao = icao nearest_dist = dist # retrieve information about the nearest airport info = self.__airportDict[nearest_icao] if not info["local_code"] is None: # TODO: need to figure out when to use the local code and when not nearest_icao = info["local_code"] nearest_lat = info["lat"] nearest_lon = info["lon"] nearest_ele = info["elevation"] nearest_name = info["name"] # finished return nearest_icao, nearest_lat, nearest_lon, int( nearest_ele), nearest_name
def parse_location(raw): raw = raw.strip() utm_mo = re.fullmatch(UTM_REGEX, raw) with contextlib.suppress(ValueError): if utm_mo: gps = utm.to_latlon(int(utm_mo[3]), int(utm_mo[4]), int(utm_mo[1]), utm_mo[2]) latlon = LatLon23.LatLon(gps[0], gps[1]) else: _, lat, lon = raw.split() latlon = LatLon23.string2latlon(lat, lon, 'd%°%m%\'%S%"%H') return latlon return None
def __next__(self): outsideCircle = True while outsideCircle: # TODO: Change of hemisphere ? self.lon_min += 0.001 if self.lon_min >= 60.0: self.lon_min = 0.0 # TODO: something's wrong here self.lon_deg += 1 coord_lon = f'{self.lon_hemi} {self.lon_deg} {self.lon_min}' self.lon = LatLon23.string2geocoord(coord_lon, LatLon23.Longitude, 'H% %d% %M') # TODO: Fix mess between lat and lon # TODO: no need of decimal degree in next line ??? if self.lon.decimal_degree >= self.border_east.decimal_degree: self.lon = self.border_west self.lon_min = int( self.border_west.decimal_minute * 1000) / 1000 self.lon_deg = int(self.border_west.degree) self.lat_min += 0.001 if self.lat_min >= 60.0: self.lat_min = 0.0 self.lat_deg += 1 # TODO: if latitude reaches border_north: raise StopIteration() coord_lat = f'{self.lat_hemi} {self.lat_deg} {self.lat_min}' self.lat = LatLon23.string2geocoord(coord_lat, LatLon23.Latitude, 'H% %d% %M') if self.lat.decimal_degree > self.border_north.decimal_degree: raise StopIteration() self.point = LatLonGC(self.lat, self.lon) distance = self.point.distance(self.center) outsideCircle = distance > self.radius self.pointchecker = self.to_gc() # distance = self.point.distance(self.center) # print(distance, ' ', self.point) ''' if self.lon_min > self.border_east: self.lon_min = self.border_west self.lat_min += 0.001 ''' # return (self.to_gc(), distance) # return (self.to_gc()) return (self.point, self.pointchecker)
def douglas_peucker(coords, max_leg_length): # convert to a numpy array np_coords = np.empty((len(coords),2), dtype=np.float32) for i in range(len(coords)): np_coords[i,0] = float(coords[i].lat) np_coords[i,1] = float(coords[i].lon) # subdivide the polygon for _ in range(5): np_coords = skimage.measure.subdivide_polygon(np_coords, degree=2, preserve_ends=True) # approximate the polygon np_coords = skimage.measure.approximate_polygon(np_coords, tolerance=0.1) # convert back coords = list() for i in range(np_coords.shape[0]): c = LatLon23.LatLon(float(np_coords[i,0]), float(np_coords[i,1])) coords.append(c) # split into legs legs = list() cur_leg = [coords[0]] cur_len = 0.0 for c in coords[1:]: assert type(c) == LatLon23.LatLon last_coord = cur_leg[-1] last_dist = c.distance(last_coord) new_len = cur_len + last_dist if new_len > max_leg_length: new_over = new_len - max_leg_length cur_under = max_leg_length - cur_len if new_over < cur_under: cur_leg.append(c) legs.append(cur_leg) cur_leg = [c] cur_len = 0.0 else: legs.append(cur_leg) cur_leg = [last_coord, c] cur_len = last_dist else: cur_len = new_len cur_leg.append(c) if len(cur_leg) > 1: legs.append(cur_leg) # finished return legs
def pointDistance (latA, lonA, latB, lonB, unit='nm'): """ Returns the distance between two geo points """ p1 = LatLon23.LatLon(LatLon23.Latitude(latA), LatLon23.Longitude(lonA)) p2 = LatLon23.LatLon(LatLon23.Latitude(latB), LatLon23.Longitude(lonB)) d = p1.distance (p2) if unit == 'nm': return km2nm(d) elif unit == 'km': return d
def _distance_between_points(track_point_node_A, track_point_node_B): # first node lat = LatLon23.Latitude(float(track_point_node_A.attrib["lat"])) lon = LatLon23.Longitude(float(track_point_node_A.attrib["lon"])) coord_A = LatLon23.LatLon(lat, lon) # second node lat = LatLon23.Latitude(float(track_point_node_B.attrib["lat"])) lon = LatLon23.Longitude(float(track_point_node_B.attrib["lon"])) coord_B = LatLon23.LatLon(lat, lon) # finished return coord_A.distance(coord_B)
def _heading_of_track_segment(track_point_nodes, xml_namespace): # first node lat = LatLon23.Latitude(float(track_point_nodes[0].attrib["lat"])) lon = LatLon23.Longitude(float(track_point_nodes[0].attrib["lon"])) start = LatLon23.LatLon(lat, lon) # last node lat = LatLon23.Latitude(float(track_point_nodes[-1].attrib["lat"])) lon = LatLon23.Longitude(float(track_point_nodes[-1].attrib["lon"])) end = LatLon23.LatLon(lat, lon) # heading calculation and finish return start.heading_initial(end)
def _length_of_track_segment(track_point_nodes, xml_namespace): # sum up the distance distance = 0.0 for i in range(1, len(track_point_nodes)): # current node lat = LatLon23.Latitude(float(track_point_nodes[i].attrib["lat"])) lon = LatLon23.Longitude(float(track_point_nodes[i].attrib["lon"])) cur = LatLon23.LatLon(lat, lon) # previous node lat = LatLon23.Latitude(float(track_point_nodes[i - 1].attrib["lat"])) lon = LatLon23.Longitude(float(track_point_nodes[i - 1].attrib["lon"])) prev = LatLon23.LatLon(lat, lon) # add the distance distance += cur.distance(prev) # finished return distance
def _minimal_distance_to_track(new_point, existing_segment): # first node lat = LatLon23.Latitude(float(new_point.attrib["lat"])) lon = LatLon23.Longitude(float(new_point.attrib["lon"])) new_coord = LatLon23.LatLon(lat, lon) # find the minimum distance min_dist = 999999.999 for node in existing_segment: # existing node lat = LatLon23.Latitude(float(node.attrib["lat"])) lon = LatLon23.Longitude(float(node.attrib["lon"])) existing_coord = LatLon23.LatLon(lat, lon) # distance dist = existing_coord.distance(new_coord) min_dist = min(min_dist, dist) # finished return min_dist
#11 - Velocity str #12 - Course str #13 - Valid GPS Fix bool #14 - In Emergency bool #15 - Text str #16 - Event str d = datetime.datetime.strptime(str(kml.Document.Folder.Placemark[0].TimeStamp.when),'%Y-%m-%dT%H:%M:%SZ') aprs_timestamp = d.strftime("%d%H%Mz") inreach_lat = round(float(kml.Document.Folder.Placemark[0].ExtendedData.Data[8].value),2) # print(inreach_lat) inreach_lon = round(float(kml.Document.Folder.Placemark[0].ExtendedData.Data[9].value),2) # print(inreach_lon) aprs_pos = LatLon23.LatLon( LatLon23.Latitude(inreach_lat), LatLon23.Longitude(inreach_lon)) lat_deg, lon_deg = aprs_pos.to_string('d%') lat_mins, lon_mins = aprs_pos.to_string('M%') lat_hem, lon_hem = aprs_pos.to_string('H%') aprs_lat_mins = format(float(lat_mins),'.2f') aprs_lon_mins = format(float(lon_mins),'.2f') try: aprs_lat = lat_deg.replace("-","") + aprs_lat_mins + lat_hem # print(aprs_lat, len(aprs_lat)) aprs_lon = lon_deg.replace("-","") + aprs_lon_mins + lon_hem # print(aprs_lon, len(aprs_lon)) assert len(aprs_lat)<=8 assert len(aprs_lat)<=9 except AssertionError:
def routagePointDistance (latA,lonA,Distanza,Rotta): p = LatLon23.LatLon(LatLon23.Latitude(latA), LatLon23.Longitude(lonA)) of = p.offset (math.degrees (Rotta), Distanza).to_string('D') return (float (of[0]), float (of[1]))
def pointDistance (latA, lonA, latB, lonB): p1 = LatLon23.LatLon(LatLon23.Latitude(latA), LatLon23.Longitude(lonA)) p2 = LatLon23.LatLon(LatLon23.Latitude(latB), LatLon23.Longitude(lonB)) return p1.distance (p2)
def ortodromic (latA,lonA,latB,lonB): p1 = LatLon23.LatLon(LatLon23.Latitude(latA), LatLon23.Longitude(lonA)) p2 = LatLon23.LatLon(LatLon23.Latitude(latB), LatLon23.Longitude(lonB)) return (p1.distance (p2), math.radians (p1.heading_initial(p2)))
elif name == 'Długość [m] w tym szacowane [m]': val = columns[1].get_text().strip() val = re.sub(' +', ' ', val) d[name] = val.replace('\r\n \n\r\n ', ';') elif name == 'Współrzędne WGS84': str_gps = columns[1].get_text().strip() # from https://github.com/hickeroar/LatLon23 str_gps_arr = str_gps.replace(',', '.').split('. ') d['D lat'] = '' d['D lon'] = '' d['DM lat'] = '' d['DM lon'] = '' d['DMS lat'] = '' d['DMS lon'] = '' if len(str_gps_arr) == 2: caveLatLon = LatLon23.string2latlon(str_gps_arr[1][2:], str_gps_arr[0][2:], 'd%°%m%′%S%″') d['D lat'] = caveLatLon.to_string('D% %H')[0] d['D lon'] = caveLatLon.to_string('D% %H')[1] d['DM lat'] = caveLatLon.to_string('d% %M% %H')[0] d['DM lon'] = caveLatLon.to_string('d% %M% %H')[1] d['DMS lat'] = caveLatLon.to_string('d% %m% %S% %H')[0] d['DMS lon'] = caveLatLon.to_string('d% %m% %S% %H')[1] else: if columns[1].find('style') is not None: columns[1].style.extract() val = columns[1].get_text().strip() val = re.sub(' +', ' ', val) d[name.strip()] = val # unicodedata.normalize("NFKD", val) invalid_tags = ['div'] for tag in invalid_tags:
def write(self, fname, airport_db): # sanity checks assert not airport_db is None # select waypoints to write coords = copy.deepcopy(self.__flightCoords) # HINT: microsoft flight simulator seems to loose the last waypoint when finishing in the air. # this is why we add the last waypoint twice when not using the airport database. # the destination airport will sometimes not be consistent and then the same behaviour occurs and we # will finish in the air. # this looks strange on the world map when planning a flight, but the vfr map seems to be okay. # nearest departure airport lat = float(coords[0].lat) lon = float(coords[0].lon) departure_id, departure_lat, departure_lon, departure_ele, departure_name = airport_db.find_nearest( lat, lon) departure_type = "Airport" departure_coord = _coord2str( LatLon23.LatLon(departure_lat, departure_lon), departure_ele) # nearest destination airport lat = float(coords[-1].lat) lon = float(coords[-1].lon) destination_id, destination_lat, destination_lon, destination_ele, destination_name = airport_db.find_nearest( lat, lon) destination_type = "Airport" destination_coord = _coord2str( LatLon23.LatLon(destination_lat, destination_lon), destination_ele) # are the two airports the same? if departure_id == destination_id: # distances to the departure and destination departure_dist = coords[0].distance( LatLon23.LatLon(departure_lat, departure_lon)) destination_dist = coords[-1].distance( LatLon23.LatLon(destination_lat, destination_lon)) # use the airport to the nearest waypoint if departure_dist < destination_dist: # destination info destination_id = "CUSTA" destination_type = "Intersection" lat = float(coords[-1].lat) + DEGREE_OFFSET_END_IN_FLIGHT lon = float(coords[-1].lon) + DEGREE_OFFSET_END_IN_FLIGHT destination_coord = _coord2str(LatLon23.LatLon(lat, lon), self.__flightElevation) destination_name = "GPX destination" else: # departure info departure_id = "CUSTD" departure_type = "Intersection" departure_coord = _coord2str(coords[0], self.__flightElevation) departure_name = "GPX departure" # trim the coordinates coords = coords[1:] # empty xml document xml_root = xml.etree.ElementTree.Element("SimBase.Document", { "Type": "AceXML", "version": "1,0" }) xml_data = xml.etree.ElementTree.ElementTree(xml_root) # general information xml.etree.ElementTree.SubElement(xml_root, "Descr").text = "AceXML Document" # flight plan flightplan_node = xml.etree.ElementTree.SubElement( xml_root, "FlightPlan.FlightPlan") xml.etree.ElementTree.SubElement(flightplan_node, "Title").text = self.__flightTitle xml.etree.ElementTree.SubElement(flightplan_node, "FPType").text = "VFR" xml.etree.ElementTree.SubElement( flightplan_node, "CruisingAlt").text = str(self.__flightElevation) xml.etree.ElementTree.SubElement(flightplan_node, "DepartureID").text = departure_id xml.etree.ElementTree.SubElement(flightplan_node, "DepartureLLA").text = departure_coord xml.etree.ElementTree.SubElement(flightplan_node, "DestinationID").text = destination_id xml.etree.ElementTree.SubElement( flightplan_node, "DestinationLLA").text = destination_coord xml.etree.ElementTree.SubElement( flightplan_node, "Descr").text = self.__flightDescription xml.etree.ElementTree.SubElement(flightplan_node, "DepartureName").text = departure_name xml.etree.ElementTree.SubElement( flightplan_node, "DestinationName").text = destination_name # app version app_version_node = xml.etree.ElementTree.SubElement( flightplan_node, "AppVersion") xml.etree.ElementTree.SubElement(app_version_node, "AppVersionMajor").text = "11" xml.etree.ElementTree.SubElement(app_version_node, "AppVersionBuild").text = "282174" # write the departure departure_node = xml.etree.ElementTree.SubElement( flightplan_node, "ATCWaypoint", {"id": departure_id}) xml.etree.ElementTree.SubElement( departure_node, "ATCWaypointType").text = departure_type xml.etree.ElementTree.SubElement( departure_node, "WorldPosition").text = departure_coord xml.etree.ElementTree.SubElement(departure_node, "SpeedMaxFP").text = "-1" icao_node = xml.etree.ElementTree.SubElement(departure_node, "ICAO") xml.etree.ElementTree.SubElement(icao_node, "ICAOIdent").text = departure_id # write the waypoints for i in range(len(coords)): coord = coords[i] name = "Cust" + str(i + 1) type = "User" waypoint_node = xml.etree.ElementTree.SubElement( flightplan_node, "ATCWaypoint", {"id": name}) xml.etree.ElementTree.SubElement(waypoint_node, "ATCWaypointType").text = type xml.etree.ElementTree.SubElement( waypoint_node, "WorldPosition").text = _coord2str(coord, self.__flightElevation) xml.etree.ElementTree.SubElement(waypoint_node, "SpeedMaxFP").text = "-1" # write the destination destination_node = xml.etree.ElementTree.SubElement( flightplan_node, "ATCWaypoint", {"id": destination_id}) xml.etree.ElementTree.SubElement( destination_node, "ATCWaypointType").text = destination_type xml.etree.ElementTree.SubElement( destination_node, "WorldPosition").text = destination_coord xml.etree.ElementTree.SubElement(destination_node, "SpeedMaxFP").text = "-1" icao_node = xml.etree.ElementTree.SubElement(destination_node, "ICAO") xml.etree.ElementTree.SubElement(icao_node, "ICAOIdent").text = destination_id # write the xml document to the file xml_data.write(fname, encoding="utf-8", xml_declaration=True)
def lossodromic (latA,lonA,latB,lonB): p1 = LatLon23.LatLon(LatLon23.Latitude(latA), LatLon23.Longitude(lonA)) p2 = LatLon23.LatLon(LatLon23.Latitude(latB), LatLon23.Longitude(lonB)) return (p1.distance (p2, ellipse = 'sphere'), math.radians (p1.heading_initial(p2)))