open_gpx_2_kml 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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