exe/ogn2kml
#!/usr/bin/env ruby
require 'bundler/inline'
require 'optparse'
gemfile do
source 'https://rubygems.org'
ruby '>= 2.4'
gem 'builder', '~> 3'
gem 'ogn_client-ruby', '~> 0.2', require: 'ogn_client'
end
class Converter
def initialize
OptionParser.new do |o|
o.banner = <<~END
Convert raw OGN APRS to KML tracks.
Usage: ogn2kml [options] infile
END
o.on('-a', '--about', 'author and license information') { puts 'Written by Sven Schwyn (bitcetera.com) and distributed under MIT license.'; exit }
o.on('-c', '--callsign STRING', String, 'aircraft callsign (e.g. FLRAABBCC)') { |v| @callsign = v }
o.on('-d', '--date YYYY-MM-DD', String, 'date the APRS messages were recorded (default: today)') { |v| @date = v }
o.on('-o', '--outfile FILE', String, 'generated KML file (default: INFILE.kml)') { |v| @outfile = v.sub(/\.kml$/, '') + '.kml' }
end.parse!
@infile = ARGV.pop
fail 'infile not found' unless @infile && File.exists?(@infile)
@outfile ||= @infile.sub(/\.\w+$/, '') + '.kml'
end
def convert!
lines = File.readlines(@infile)
points = lines.map do |line|
message = OGNClient::Message.parse(line, date: @date)
if message.is_a?(OGNClient::SenderBeacon) && (!@callsign || @callsign == message.callsign)
{
when: message.time.xmlschema,
coord: "#{message.longitude} #{message.latitude} #{message.altitude}",
angles: "#{message.heading} 0 0",
time: message.time.getlocal.strftime('%H:%M:%S'),
ground_speed: message.ground_speed.to_i.to_s,
climb_rate: message.climb_rate.to_i.to_s,
turn_rate: message.turn_rate.to_i.to_s
}
end
end.compact
xml = Builder::XmlMarkup.new indent: 2
xml.instruct!
xml.kml(
xmlns: 'http://www.opengis.net/kml/2.2',
'xmlns:gx': 'http://www.google.com/kml/ext/2.2'
) do
xml.Document do
xml.name 'OGN'
# Styles
xml.Style(id: 'track_normal') do
xml.IconStyle do
xml.Icon do
xml.href 'http://earth.google.com/images/kml-icons/track-directional/track-0.png'
end
end
xml.LineStyle do
xml.color '99ffac59'
xml.width '6'
end
end
xml.Style(id: 'track_highlight') do
xml.IconStyle do
xml.scale '1.2'
xml.Icon do
xml.href 'http://earth.google.com/images/kml-icons/track-directional/track-0.png'
end
end
xml.LineStyle do
xml.color '99ffac59'
xml.width '8'
end
end
xml.StyleMap(id: 'track') do
xml.Pair do
xml.key 'normal'
xml.styleUrl '#track_normal'
end
xml.Pair do
xml.key 'highlight'
xml.styleUrl '#track_highlight'
end
end
# Tracks
xml.Folder do
xml.name 'Tracks'
xml.Placemark do
xml.name @callsign || 'all'
xml.styleUrl '#track'
xml.tag!('gx:Track') do
xml.altitudeMode 'absolute'
points.each { |p| xml.when p[:when] }
points.each { |p| xml.tag!('gx:coord', p[:coord]) }
points.each { |p| xml.tag!('gx:angles', p[:angles]) }
xml.ExtendedData do
xml.SchemaData(schemaUrl: '#schema') do
xml.tag!('gx:SimpleArrayData', name: 'Time') do
points.each { |p| xml.tag!('gx:value', p[:time]) }
end
xml.tag!('gx:SimpleArrayData', name: 'Ground Speed') do
points.each { |p| xml.tag!('gx:value', p[:ground_speed]) }
end
xml.tag!('gx:SimpleArrayData', name: 'Turn Rate') do
points.each { |p| xml.tag!('gx:value', p[:turn_rate]) }
end
end
end
end
end
end
end
end
File.write(@outfile, xml.target!)
end
end
begin
Converter.new.convert!
#rescue => exception
# puts "#{File.basename($0)}: #{exception.message}"
# exit 1
end