open_gpx_2_kml 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ require 'csv'
2
+
3
+ module TF1Converter
4
+ class Config
5
+
6
+ def self.load(path)
7
+ last_key = nil
8
+ current_control = nil
9
+ @constant_color_switch = false
10
+
11
+ CSV.read(path).each do |row|
12
+ if last_key == 'INPUT'
13
+ @input = row[0]
14
+ elsif last_key == 'OUTPUT'
15
+ @output = row[0]
16
+ elsif last_key == 'ICON_PATH'
17
+ @icon_path = row[0]
18
+ elsif last_key == 'ICONS'
19
+ @icons = {}
20
+ current_control = 'ICONS'
21
+ elsif last_key == 'COLORS'
22
+ @colors = {}
23
+ current_control = 'COLORS'
24
+ elsif last_key == 'USE_CONSTANT_COLOR'
25
+ @use_constant_color = row[0].strip.downcase == 'true'
26
+ elsif last_key == 'CONSTANT_COLOR'
27
+ @constant_color = row[0]
28
+ end
29
+
30
+ if current_control == 'ICONS'
31
+ if row.empty?
32
+ current_control = nil
33
+ else
34
+ @icons[row[0]] = {
35
+ 'icon' => row[1],
36
+ 'meaning' => row[2],
37
+ 'name' => row[3]
38
+ }
39
+ end
40
+ elsif current_control == 'COLORS'
41
+ if row.empty?
42
+ current_control = nil
43
+ else
44
+ @colors[row[0]] = row[1]
45
+ end
46
+ end
47
+
48
+ last_key = row[0]
49
+ end
50
+ end
51
+
52
+ %w(icon_path icons colors input output use_constant_color).each do |name|
53
+ define_singleton_method(name.to_sym) do
54
+ instance_variable_get("@#{name}")
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ def self.constant_color
61
+ colors[@constant_color]
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ require 'csv'
2
+
3
+ module TF1Converter
4
+ class CsvFile
5
+ def initialize(waypoints, path)
6
+ @waypoints = waypoints
7
+ @path = path
8
+ end
9
+
10
+ def to_csv!
11
+ CSV.open(@path, 'wb') do |csv|
12
+ csv << ['filename', 'name', 'meaning', 'time', 'lat', 'long', 'usng', 'elevation']
13
+ @waypoints.each do |wp|
14
+ csv << [@path.split('.').first, wp.name, wp.icon_meaning, wp.timestamp, wp.lat, wp.long, wp.usng, wp.elevation]
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'trackpoint'
2
+
3
+ module TF1Converter
4
+ module Gpx
5
+ class Track
6
+ def initialize(xml_node, color_map = TF1Converter::Config.colors)
7
+ @node = xml_node
8
+ @color_map = color_map
9
+ end
10
+
11
+ def name
12
+ @node.xpath('name').first.text
13
+ end
14
+
15
+ def display_color
16
+ color_node = @node.xpath('extensions/TrackExtension/DisplayColor').first
17
+ if color_node
18
+ @color_map[color_node.text]
19
+ else
20
+ 'f0000080'
21
+ end
22
+ end
23
+
24
+ def coordinate_string
25
+ trackpoints = @node.xpath('trkseg/trkpt').map{ |node| Trackpoint.new(node) }
26
+ trackpoints.inject([]) { |points, tp| points << tp.to_s }.join(' ')
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ module TF1Converter
2
+ module Gpx
3
+ class Trackpoint
4
+ def initialize(xml_node)
5
+ @node = xml_node
6
+ end
7
+
8
+ def lat
9
+ @node.attribute('lat').value.strip
10
+ end
11
+
12
+ def long
13
+ @node.attribute('lon').value.strip
14
+ end
15
+
16
+ def to_s
17
+ "" << long << ',' << lat << ',0'
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,76 @@
1
+ module TF1Converter
2
+ module Gpx
3
+ class Waypoint
4
+ def initialize(xml_node, icon_map = TF1Converter::Config.icons)
5
+ @node = xml_node
6
+ @icon_map = icon_map
7
+ end
8
+
9
+ def name
10
+ name_node = @node.xpath('name').first
11
+ name_node.nil? ? '' : name_node.text
12
+ end
13
+
14
+ def icon_name
15
+ if symbol_name
16
+ map_entry = @icon_map[symbol_name]
17
+ return map_entry['icon'] if map_entry
18
+ elsif name
19
+ @icon_map.values.each do |icon_data|
20
+ return icon_data['icon'] if icon_data['name'] == name
21
+ end
22
+ end
23
+ 'default.png'
24
+ end
25
+
26
+ def icon_meaning
27
+ if symbol_name
28
+ map_entry = @icon_map[symbol_name]
29
+ return map_entry['meaning'] if map_entry
30
+ end
31
+ 'Default'
32
+ end
33
+
34
+ def timestamp
35
+ @node.children.select{ |child| child.name == 'cmt' }.first.text
36
+ end
37
+
38
+ def lat
39
+ @node.attribute('lat').value
40
+ end
41
+
42
+ def long
43
+ @node.attribute('lon').value
44
+ end
45
+
46
+ def elevation
47
+ @node.children.select{ |child| child.name == 'ele' }.first.text
48
+ end
49
+
50
+ def usng
51
+ u = utm_object
52
+ GeoSwap.utm_to_usng(u.easting, u.northing, u.zone.number, u.zone.letter)
53
+ end
54
+
55
+ def utm
56
+ utm_object.to_s
57
+ end
58
+
59
+ private
60
+
61
+ def utm_object
62
+ @_utm_object ||= GeoSwap.lat_long_to_utm(lat.to_f, long.to_f)
63
+ end
64
+
65
+ def symbol_name
66
+ sym_node = @node.children.select{ |child| child.name == 'sym' }.first
67
+ if sym_node
68
+ sym_node.text
69
+ else
70
+ nil
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'gpx/waypoint'
2
+ require_relative 'gpx/track'
3
+
4
+ module TF1Converter
5
+ class GpxFile
6
+ def initialize(gpx_file)
7
+ @gpx = gpx_file
8
+ end
9
+
10
+ def waypoints
11
+ @gpx.xpath('//gpx/wpt').map{ |node| Gpx::Waypoint.new(node) }
12
+ end
13
+
14
+ def tracks
15
+ @gpx.xpath('//gpx/trk').map{ |node| Gpx::Track.new(node) }
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ module TF1Converter::Kml
2
+ class TrackColor
3
+ def self.next
4
+ if config.use_constant_color
5
+ config.constant_color
6
+ else
7
+ @colors ||= config.colors.values
8
+ @color_index ||= 0
9
+ return_color = @colors[@color_index]
10
+ @color_index += 1
11
+ if @color_index >= @colors.length
12
+ @color_index = 0
13
+ end
14
+ return_color
15
+ end
16
+ end
17
+
18
+ def self.uncache!
19
+ @colors = nil
20
+ @color_index = nil
21
+ end
22
+
23
+ private
24
+ def self.config
25
+ ::TF1Converter::Config
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'track_color'
2
+
3
+ module TF1Converter
4
+ module Kml
5
+ class TrackNode
6
+ def initialize(track, filename)
7
+ @track = track
8
+ @filename = filename
9
+ end
10
+
11
+ def write_to(xml)
12
+ xml.Style(id: "#{@track.name}_Style") do
13
+ xml.LineStyle do
14
+ xml.color TrackColor.next
15
+ xml.width 3
16
+ end
17
+ end
18
+
19
+ xml.Placemark(id: @track.name) do
20
+ xml.name @track.name
21
+ xml.description do
22
+ xml.cdata @filename
23
+ end
24
+ xml.styleUrl "##{@track.name}_Style"
25
+ xml.LineString do
26
+ xml.extrude 1
27
+ xml.tessellate 1
28
+ xml.altitudeMode 'clampedToGround'
29
+ xml.coordinates @track.coordinate_string
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,80 @@
1
+ require_relative 'kml/track_node'
2
+
3
+ module TF1Converter
4
+ class KmlFile
5
+ def initialize(waypoints, tracks, filename)
6
+ @waypoints = waypoints
7
+ @tracks = tracks
8
+ @filename = filename
9
+ end
10
+
11
+ def to_xml
12
+ Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
13
+ xml.kml('xmlns' => 'http://www.opengis.net/kml/2.2') do
14
+ xml.Document do
15
+ write_xml_header(xml)
16
+
17
+ xml.Folder do
18
+ xml.name "Waypoints"
19
+ @waypoints.each do |waypoint|
20
+ write_waypoint_xml(waypoint, xml)
21
+ end
22
+ end
23
+
24
+ xml.Folder do
25
+ xml.name "Tracks"
26
+ @tracks.each do |track|
27
+ Kml::TrackNode.new(track, @filename).write_to(xml)
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end.to_xml
34
+ end
35
+
36
+ private
37
+
38
+ def write_xml_header(xml)
39
+ xml.open 1
40
+ xml.Snippet(maxLines: '1')
41
+ xml.description do
42
+ xml.cdata "#{Time.now.strftime('%m-%d-%Y %I:%M:%S %p')}<br/><br/>TF1 Converter Version 1.0<br/>MO Task Force 1<br/>"
43
+ end
44
+ xml.Style(id: "sn_noicon") { xml.IconStyle { xml.Icon } }
45
+ end
46
+
47
+ def write_waypoint_xml(waypoint, xml)
48
+ xml.Placemark do
49
+ xml.name(waypoint.name)
50
+ xml.Snippet(maxLines: '0')
51
+ xml.Style(id: 'normalPlacemark') do
52
+ xml.IconStyle do
53
+ xml.Icon do
54
+ xml.href("files/#{waypoint.icon_name}")
55
+ end
56
+ end
57
+ end
58
+
59
+ xml.description do
60
+ xml.cdata description_for(waypoint)
61
+ end
62
+
63
+ xml.Point do
64
+ xml.coordinates "#{waypoint.long},#{waypoint.lat}"
65
+ end
66
+ end
67
+ end
68
+
69
+ def description_for(waypoint)
70
+ desc = ""
71
+ desc << waypoint.timestamp
72
+ desc << '<br>' << waypoint.icon_meaning
73
+ desc << '<br>' << "Filename: #{@filename}"
74
+ desc << "<br>" << "USNG: #{waypoint.usng}"
75
+ desc << "<br>" << "Lat,Long: #{waypoint.lat},#{waypoint.long}"
76
+ desc << "<br>" << "Elevation: #{waypoint.elevation}"
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,27 @@
1
+ require 'fileutils'
2
+ require 'zip/zip'
3
+
4
+ module TF1Converter
5
+ class KmzFile
6
+ def self.assemble!(filename)
7
+ raw_name = filename.split(/[\/\\]/).last
8
+ zip_path = "#{filename}.zip"
9
+ FileUtils.rm(zip_path) if File.exists?(zip_path)
10
+ Zip::ZipFile.open(zip_path, Zip::ZipFile::CREATE) do |zipfile|
11
+ zipfile.add("#{raw_name}.kml", "#{filename}.kml")
12
+ zipfile.mkdir("files")
13
+ Dir.foreach(TF1Converter::Config.icon_path) do |item|
14
+ if item != '.' && item != '..'
15
+ zipfile.add("files/#{item}", full_filepath(item))
16
+ end
17
+ end
18
+ end
19
+ FileUtils.mv(zip_path, "#{filename}.kmz")
20
+ end
21
+
22
+ def self.full_filepath(filename)
23
+ path = "#{TF1Converter::Config.icon_path}/#{filename}"
24
+ path.gsub('//', '/')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ require 'nokogiri'
2
+ require 'geo_swap'
3
+ require_relative 'gpx_file'
4
+ require_relative 'kml_file'
5
+ require_relative 'kmz_file'
6
+ require_relative 'csv_file'
7
+
8
+ module TF1Converter
9
+ class Translation
10
+ def self.from(file)
11
+ new(file)
12
+ end
13
+
14
+ def initialize(gpx_file)
15
+ @filename = File.basename(gpx_file.path).split('.').first
16
+ parsed_gpx = Nokogiri::XML(gpx_file)
17
+ parsed_gpx.remove_namespaces!
18
+ @gpx = GpxFile.new(parsed_gpx)
19
+ end
20
+
21
+ def into(output_file)
22
+ raw_file_name = output_file.path.split('.').first
23
+ csv_path = raw_file_name + '.csv'
24
+ CsvFile.new(@gpx.waypoints, csv_path).to_csv!
25
+
26
+ kml = KmlFile.new(@gpx.waypoints, @gpx.tracks, @filename).to_xml
27
+ output_file.puts kml
28
+ output_file.close
29
+
30
+ kmz = KmzFile.assemble!(raw_file_name)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module TF1Converter
2
+ VERSION = "0.9.1"
3
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'tf1_converter/config'
2
+ require_relative 'tf1_converter/version'
3
+ require_relative 'tf1_converter/translation'
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tf1_converter/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "open_gpx_2_kml"
8
+ gem.version = TF1Converter::VERSION
9
+ gem.authors = ["Ethan Vizitei"]
10
+ gem.email = ["ethan.vizitei@gmail.com"]
11
+ gem.description = %q{A GPX to KML converter for FEMA US&R}
12
+ gem.summary = %q{A GPX to KML converter for FEMA US&R}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'rake', '10.0.3'
21
+ gem.add_development_dependency 'rspec', '2.12.0'
22
+ gem.add_development_dependency 'pry', '0.9.11.4'
23
+ gem.add_development_dependency 'timecop', '0.5.9.2'
24
+
25
+ gem.add_dependency 'thor', '0.17.0'
26
+ gem.add_dependency 'nokogiri', '1.5.6'
27
+ gem.add_dependency 'builder', '3.1.4'
28
+ gem.add_dependency 'geo_swap', '0.2.1'
29
+ gem.add_dependency 'rubyzip', '0.9.9'
30
+ end