Created
August 31, 2018 13:03
-
-
Save darksidelemm/ca54db16ffb2f092289ec9e0dd1179a7 to your computer and use it in GitHub Desktop.
Test fix for log_to_kml.py from radiosonde_auto_rx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# | |
# Radiosonde Auto RX Tools | |
# Log-to-KML Utilities | |
# | |
# 2018-02 Mark Jessop <[email protected]> | |
# | |
# Note: This utility requires the fastkml and shapely libraries, which can be installed using: | |
# sudo pip install fastkml shapely | |
# | |
import sys | |
import time | |
import datetime | |
import traceback | |
import argparse | |
import glob | |
import os | |
import fastkml | |
from dateutil.parser import * | |
from shapely.geometry import Point, LineString | |
def read_telemetry_csv(filename, | |
datetime_field = 0, | |
latitude_field = 3, | |
longitude_field = 4, | |
altitude_field = 5, | |
delimiter=','): | |
''' | |
Read in a radiosonde_auto_rx generated telemetry CSV file. | |
Fields to use can be set as arguments to this function. | |
These have output like the following: | |
2017-12-27T23:21:59.560,M2913374,982,-34.95143,138.52471,719.9,-273.0,RS92,401.520 | |
<datetime>,<serial>,<frame_no>,<lat>,<lon>,<alt>,<temp>,<sonde_type>,<freq> | |
Note that the datetime field must be parsable by dateutil.parsers.parse. | |
If any fields are missing, or invalid, this function will return None. | |
The output data structure is in the form: | |
[ | |
[datetime (as a datetime object), latitude, longitude, altitude, raw_line], | |
[datetime (as a datetime object), latitude, longitude, altitude, raw_line], | |
... | |
] | |
''' | |
output = [] | |
f = open(filename,'r') | |
for line in f: | |
try: | |
# Split line by comma delimiters. | |
_fields = line.split(delimiter) | |
# Attempt to parse fields. | |
_datetime = parse(_fields[datetime_field]) | |
_latitude = float(_fields[latitude_field]) | |
_longitude = float(_fields[longitude_field]) | |
_altitude = float(_fields[altitude_field]) | |
output.append([_datetime, _latitude, _longitude, _altitude, line]) | |
except: | |
traceback.print_exc() | |
return None | |
f.close() | |
return output | |
def flight_burst_position(flight_path): | |
''' Search through flight data for the burst position and return it. ''' | |
# Read through array and hunt for max altitude point. | |
current_alt = 0.0 | |
current_index = 0 | |
for i in range(len(flight_path)): | |
if flight_path[i][3] > current_alt: | |
current_alt = flight_path[i][3] | |
current_index = i | |
return flight_path[current_index] | |
ns = '{http://www.opengis.net/kml/2.2}' | |
def new_placemark(lat, lon, alt, | |
placemark_id="Placemark ID", | |
name="Placemark Name", | |
absolute = False, | |
icon = "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png", | |
scale = 1.0): | |
""" Generate a generic placemark object """ | |
if absolute: | |
_alt_mode = 'absolute' | |
else: | |
_alt_mode = 'clampToGround' | |
flight_icon_style = fastkml.styles.IconStyle( | |
ns=ns, | |
icon_href=icon, | |
scale=scale) | |
flight_style = fastkml.styles.Style( | |
ns=ns, | |
styles=[flight_icon_style]) | |
flight_placemark = fastkml.kml.Placemark( | |
ns=ns, | |
id=placemark_id, | |
name=name, | |
description="", | |
styles=[flight_style]) | |
flight_placemark.geometry = fastkml.geometry.Geometry( | |
ns=ns, | |
geometry=Point(lon, lat, alt), | |
altitude_mode=_alt_mode) | |
return flight_placemark | |
def flight_path_to_geometry(flight_path, | |
placemark_id="Flight Path ID", | |
name="Flight Path Name", | |
track_color="aaffffff", | |
poly_color="20000000", | |
track_width=2.0, | |
absolute = True, | |
extrude = True, | |
tessellate = True): | |
''' Produce a fastkml geometry object from a flight path array ''' | |
# Handle selection of absolute altitude mode | |
if absolute: | |
_alt_mode = 'absolute' | |
else: | |
_alt_mode = 'clampToGround' | |
# Convert the flight path array [time, lat, lon, alt, comment] into a LineString object. | |
track_points = [] | |
for _point in flight_path: | |
# Flight path array is in lat,lon,alt order, needs to be in lon,lat,alt | |
track_points.append([_point[2],_point[1],_point[3]]) | |
_flight_geom = LineString(track_points) | |
# Define the Line and Polygon styles, which are used for the flight path, and the extrusions (if enabled) | |
flight_track_line_style = fastkml.styles.LineStyle( | |
ns=ns, | |
color=track_color, | |
width=track_width) | |
flight_extrusion_style = fastkml.styles.PolyStyle( | |
ns=ns, | |
color=poly_color) | |
flight_track_style = fastkml.styles.Style( | |
ns=ns, | |
styles=[flight_track_line_style, flight_extrusion_style]) | |
# Generate the Placemark which will contain the track data. | |
flight_line = fastkml.kml.Placemark( | |
ns=ns, | |
id=placemark_id, | |
name=name, | |
styles=[flight_track_style]) | |
# Add the track data to the Placemark | |
flight_line.geometry = fastkml.geometry.Geometry( | |
ns=ns, | |
geometry=_flight_geom, | |
altitude_mode=_alt_mode, | |
extrude=extrude, | |
tessellate=tessellate) | |
return flight_line | |
def write_kml(geom_objects, | |
filename="output.kml", | |
comment=""): | |
""" Write out flight path geometry objects to a kml file. """ | |
kml_root = fastkml.kml.KML() | |
kml_doc = fastkml.kml.Document( | |
ns=ns, | |
name=comment) | |
if type(geom_objects) is not list: | |
geom_objects = [geom_objects] | |
for _flight in geom_objects: | |
kml_doc.append(_flight) | |
with open(filename,'w') as kml_file: | |
kml_str = kml_doc.to_string() | |
kml_str = kml_str.replace('kml:','').replace(':kml','') | |
kml_file.write(kml_str) | |
kml_file.close() | |
def convert_single_file(filename, absolute=True, tessellate=True, last_only=False): | |
''' Convert a single sonde log file to a fastkml KML Folder object ''' | |
# Read file. | |
_flight_data = read_telemetry_csv(filename) | |
# Extract the flight's serial number and launch time from the first line in the file. | |
_first_line = _flight_data[0][4] | |
_flight_serial = _first_line.split(',')[1] # Serial number is the second field in the line. | |
_launch_time = _flight_data[0][0].strftime("%Y%m%d-%H%M%SZ") | |
# Generate a comment line to use in the folder and placemark descriptions | |
_track_comment = "%s %s" % (_launch_time, _flight_serial) | |
_landing_comment = "%s Last Position" % (_flight_serial) | |
# Grab burst and last-seen positions | |
_burst_pos = flight_burst_position(_flight_data) | |
_landing_pos = _flight_data[-1] | |
# Generate the placemark & flight track. | |
_flight_geom = flight_path_to_geometry(_flight_data, name=_track_comment, absolute=absolute, tessellate=tessellate, extrude=tessellate) | |
_landing_geom = new_placemark(_landing_pos[1], _landing_pos[2], _landing_pos[3], name=_landing_comment, absolute=absolute) | |
_folder = fastkml.kml.Folder(ns, _flight_serial, _track_comment, 'Radiosonde Flight Path') | |
if last_only == False: | |
_folder.append(_flight_geom) | |
_folder.append(_landing_geom) | |
return _folder | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument("-i", "--input", type=str, default="../log/*_sonde.log", | |
help="Path to log file. May include wildcards, though the path must be wrapped in quotes. Default=../log/*_sonde.log") | |
parser.add_argument("-o", "--output", type=str, default="sondes.kml", help="KML output file name. Default=sondes.kml") | |
parser.add_argument('--clamp', action="store_false", default=True, help="Clamp tracks to ground instead of showing absolute altitudes.") | |
parser.add_argument('--noextrude', action="store_false", default=True, help="Disable Extrusions for absolute flight paths.") | |
parser.add_argument('--lastonly', action="store_true", default=False, help="Only plot last-seen sonde positions, not the flight paths.") | |
args = parser.parse_args() | |
_file_list = glob.glob(args.input) | |
_placemarks = [] | |
for _file in _file_list: | |
print("Processing: %s" % _file) | |
try: | |
_placemarks.append(convert_single_file(_file, absolute=args.clamp, tessellate=args.noextrude, last_only=args.lastonly)) | |
except: | |
print("Failed to process: %s" % _file) | |
write_kml(_placemarks, filename=args.output) | |
print("Output saved to: %s" % args.output) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment