gpx2exif 0.2.0 → 0.3.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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5c57a7184d4a6c5a92e236813aaac15520bfac1d
4
+ data.tar.gz: b75af062cb4ca3cd443f0cacdb34d322f8c07b9f
5
+ SHA512:
6
+ metadata.gz: 6e0978ea19d9ced64f58cc8d8adece6cf8b2930fb1d798484293d6fa99fbcec5bcacfa324ef9376b72c05a62b6fe4dad46362094c2e18b493a546f6d93c0c0ea
7
+ data.tar.gz: d45af749bd7beae9b406b5dcaac6717bb161ed75806213a8a30b735c120be2c99185bb34ffeccaffe34aa9dc98949920d7825bcc922f080067bdcc3925cb289b
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gem 'mini_exiftool'
4
4
  gem 'rmagick'
5
5
  gem 'chunky_png'
6
6
  gem 'gpx_utils'
7
+ gem 'geokit'
7
8
 
8
9
  group :development do
9
10
  gem "rspec"
@@ -1,69 +1,75 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- addressable (2.3.5)
4
+ addressable (2.3.6)
5
5
  builder (3.2.2)
6
- chunky_png (1.2.9)
6
+ chunky_png (1.3.0)
7
+ descendants_tracker (0.0.4)
8
+ thread_safe (~> 0.3, >= 0.3.1)
7
9
  diff-lcs (1.2.5)
8
- docile (1.1.1)
9
- faraday (0.8.8)
10
- multipart-post (~> 1.2.0)
10
+ docile (1.1.3)
11
+ faraday (0.9.0)
12
+ multipart-post (>= 1.2, < 3)
13
+ geokit (1.8.4)
14
+ multi_json (>= 1.3.2)
11
15
  git (1.2.6)
12
- github_api (0.10.1)
13
- addressable
14
- faraday (~> 0.8.1)
16
+ github_api (0.11.3)
17
+ addressable (~> 2.3)
18
+ descendants_tracker (~> 0.0.1)
19
+ faraday (~> 0.8, < 0.10)
15
20
  hashie (>= 1.2)
16
- multi_json (~> 1.4)
17
- nokogiri (~> 1.5.2)
21
+ multi_json (>= 1.7.5, < 2.0)
22
+ nokogiri (~> 1.6.0)
18
23
  oauth2
19
24
  gpx_utils (0.0.1)
20
25
  builder
21
26
  nokogiri
22
- hashie (2.0.5)
23
- highline (1.6.20)
24
- httpauth (0.2.0)
25
- jeweler (1.8.8)
27
+ hashie (2.1.1)
28
+ highline (1.6.21)
29
+ jeweler (2.0.1)
26
30
  builder
27
- bundler (~> 1.0)
31
+ bundler (>= 1.0)
28
32
  git (>= 1.2.5)
29
- github_api (= 0.10.1)
33
+ github_api
30
34
  highline (>= 1.6.15)
31
- nokogiri (= 1.5.10)
35
+ nokogiri (>= 1.5.10)
32
36
  rake
33
37
  rdoc
34
38
  json (1.8.1)
35
- jwt (0.1.8)
39
+ jwt (0.1.11)
36
40
  multi_json (>= 1.5)
37
- mini_exiftool (2.3.0)
38
- multi_json (1.8.2)
41
+ mini_exiftool (2.4.1)
42
+ mini_portile (0.5.3)
43
+ multi_json (1.9.2)
39
44
  multi_xml (0.5.5)
40
- multipart-post (1.2.0)
41
- nokogiri (1.5.10)
42
- oauth2 (0.9.2)
43
- faraday (~> 0.8)
44
- httpauth (~> 0.2)
45
- jwt (~> 0.1.4)
46
- multi_json (~> 1.0)
45
+ multipart-post (2.0.0)
46
+ nokogiri (1.6.1)
47
+ mini_portile (~> 0.5.0)
48
+ oauth2 (0.9.3)
49
+ faraday (>= 0.8, < 0.10)
50
+ jwt (~> 0.1.8)
51
+ multi_json (~> 1.3)
47
52
  multi_xml (~> 0.5)
48
53
  rack (~> 1.2)
49
54
  rack (1.5.2)
50
- rake (10.1.0)
51
- rdoc (4.0.1)
55
+ rake (10.3.1)
56
+ rdoc (4.1.1)
52
57
  json (~> 1.4)
53
58
  rmagick (2.13.2)
54
59
  rspec (2.14.1)
55
60
  rspec-core (~> 2.14.0)
56
61
  rspec-expectations (~> 2.14.0)
57
62
  rspec-mocks (~> 2.14.0)
58
- rspec-core (2.14.7)
59
- rspec-expectations (2.14.4)
63
+ rspec-core (2.14.8)
64
+ rspec-expectations (2.14.5)
60
65
  diff-lcs (>= 1.1.3, < 2.0)
61
- rspec-mocks (2.14.4)
66
+ rspec-mocks (2.14.6)
62
67
  simplecov (0.8.2)
63
68
  docile (~> 1.1.0)
64
69
  multi_json
65
70
  simplecov-html (~> 0.8.0)
66
71
  simplecov-html (0.8.0)
72
+ thread_safe (0.3.3)
67
73
 
68
74
  PLATFORMS
69
75
  ruby
@@ -71,6 +77,7 @@ PLATFORMS
71
77
  DEPENDENCIES
72
78
  bundler
73
79
  chunky_png
80
+ geokit
74
81
  gpx_utils
75
82
  jeweler
76
83
  mini_exiftool
data/README.md CHANGED
@@ -112,6 +112,6 @@ Contributing to gpx2xif
112
112
  Copyright
113
113
  ---------
114
114
 
115
- Copyright (c) 2012 Aleksander Kwiatkowski. See LICENSE.txt for
115
+ Copyright (c) 2012-2014 Aleksander Kwiatkowski, Craig Taverner. See LICENSE.txt for
116
116
  further details.
117
117
 
data/Rakefile CHANGED
@@ -20,9 +20,9 @@ Jeweler::Tasks.new do |gem|
20
20
  gem.summary = %Q{Mass geotagger using GPX files}
21
21
  gem.description = %Q{Mass geotagger using GPX files.}
22
22
  gem.email = "bobikx@poczta.fm"
23
- gem.authors = ["Aleksander Kwiatkowski"]
23
+ gem.authors = ["Aleksander Kwiatkowski", "Craig Taverner"]
24
24
  # dependencies defined in Gemfile
25
- gem.executables = ['geotag_all_images', 'geotag_simulate', 'gpx2png']
25
+ gem.executables = ['geotag', 'geotag_all_images', 'geotag_simulate', 'gpx2png']
26
26
 
27
27
  gem.files = FileList[
28
28
  "[A-Z]*", "{bin,generators,lib,test}/**/*"
@@ -49,4 +49,11 @@ desc "Run RSpec with code coverage"
49
49
  task :coverage do
50
50
  `rake spec COVERAGE=true`
51
51
  #`open coverage/index.html`
52
- end
52
+ end
53
+
54
+ desc "Make temp directory for image output"
55
+ task :make_temp do
56
+ FileUtils.mkdir_p "samples/tmp"
57
+ end
58
+
59
+ task :spec => :make_temp
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.1
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'gpx2exif'
5
+ require 'gpx2png/osm'
6
+ require 'gpx2png/ump'
7
+ require 'optparse'
8
+ require 'geotagger/geotagger'
9
+
10
+ options = { }
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: geotag [options] [files]"
13
+
14
+ opts.on("-d", "--[no-]debug", "Debug mode") do |v|
15
+ options[:debug] = v
16
+ end
17
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
18
+ options[:verbose] = v
19
+ end
20
+ opts.on("-m", "--[no-]simulate", "Run geotagging as simulation") do |v|
21
+ options[:verbose] = v
22
+ end
23
+ opts.on("-a", "--[no-]autofind", "Run geotagging using all GPX and JPG files found from current path") do |v|
24
+ options[:verbose] = v
25
+ end
26
+ opts.on("-g", "--gpx FILE", "Input GPX file") do |v|
27
+ options[:gpx] = v
28
+ end
29
+ opts.on("-T", "--start-time DATETIME", "Time of first image for images without timestamps") do |v|
30
+ options[:start_time] = v
31
+ end
32
+ opts.on("-G", "--time-gap MILLIS", "Time gap in milliseconds between images for images without timestamps") do |v|
33
+ options[:time_gap] = v.to_f
34
+ end
35
+ opts.on("-t", "--timeoffset OFFSET", "Timeoffset between GPX and EXIF") do |v|
36
+ options[:time_offset] = v.to_i
37
+ end
38
+ opts.on("-p", "--pattern FILEPAT", "Input image file pattern") do |v|
39
+ options[:pattern] = v
40
+ end
41
+ opts.on("-z", "--zoom ZOOM", "Set zoom") do |v|
42
+ options[:zoom] = v
43
+ end
44
+ opts.on("-s", "--size WIDTHxHEIGHT", "Set output map image size") do |v|
45
+ options[:size] = v
46
+ end
47
+ opts.on("-e", "--scale SCALE:XFACTOR:YFACTOR", "Scale dataset for closeup view") do |v|
48
+ s,x,y = v.split(/\:/)
49
+ options[:scale] = {
50
+ :scale => [[s.to_f,0.00001].max,1000].min,
51
+ :shift_x => x,
52
+ :shift_y => (y || x)
53
+ }
54
+ end
55
+ opts.on("-x", "--gpx-tags TAGS", "Comma separated list of key=value tags to add to EXIF") do |v|
56
+ options[:exif_tags] ||= {}
57
+ v.split(/\,+/).each do |pair|
58
+ key,value = pair.split(/\s*\=\s*/)
59
+ options[:exif_tags][key] = value
60
+ end
61
+ puts "Setting extra EXIF tags: #{options[:exif_tags].inspect}"
62
+ end
63
+ opts.on("-o", "--output FILE", "Output map image file") do |v|
64
+ options[:output_file] = v
65
+ end
66
+ opts.on("-u", "--ump", "Use UMP tiles rather than OSM for output map background") do
67
+ options[:ump] = true
68
+ end
69
+ end.parse!
70
+
71
+ if options[:debug]
72
+ puts options.inspect
73
+ puts ARGV.inspect
74
+ end
75
+
76
+ unless options[:gpx]
77
+ puts "Input GPX file needed"
78
+ @fail = true
79
+ end
80
+ if options[:output_file]
81
+ options[:size] ||= "600x600"
82
+ options[:zoom] ||= 1.0
83
+ end
84
+ if options[:output_file].nil? && options[:pattern].nil? && ARGV.length == 0
85
+ puts "Need at least one file, or a file pattern, or an output image"
86
+ @fail = true
87
+ end
88
+
89
+ if @fail
90
+ exit 0
91
+ end
92
+
93
+ $track_importer = nil
94
+
95
+ if ARGV.length > 0 || options[:pattern] || options[:autofind]
96
+ g = Geotagger::Geotagger.new(options.merge(files: ARGV))
97
+ $track_importer = g.ti
98
+ if options[:autofind]
99
+ g.add_all_files(options[:time_offset].to_i)
100
+ end
101
+ if options[:gpx]
102
+ g.add_gpx_file(options[:gpx])
103
+ end
104
+ if options[:pattern]
105
+ g.add_pattern(options[:pattern])
106
+ end
107
+ if ARGV.length > 0
108
+ ARGV.each{|file| g.add_image(file)}
109
+ end
110
+ if options[:start_time]
111
+ g.fix_times
112
+ end
113
+ g.match_up
114
+ if options[:simulate]
115
+ g.simulate
116
+ else
117
+ g.save!
118
+ end
119
+ end
120
+
121
+ if options[:output_file]
122
+ g = $track_importer
123
+ unless g
124
+ g = Geotagger::TrackImporter.new
125
+ g.add_file(options[:gpx])
126
+ end
127
+
128
+ if options[:ump]
129
+ e = Gpx2png::Ump.new
130
+ else
131
+ e = Gpx2png::Osm.new
132
+ end
133
+ e.coords = g.coords
134
+ g.auto_marker do |marker|
135
+ e.add_marker marker
136
+ end
137
+
138
+ if options[:scale]
139
+ e.scale_options = options[:scale]
140
+ end
141
+
142
+ if options[:size]
143
+ # constant size
144
+ options[:size] =~ /(\d+)x(\d+)/
145
+ e.fixed_size($1.to_i, $1.to_i)
146
+ else
147
+ # constant zoom
148
+ e.zoom = options[:zoom].to_i
149
+ e.renderer_options = {crop_enabled: true}
150
+ end
151
+
152
+ e.save(options[:output_file])
153
+ end
154
+
155
+ # testing
156
+ # ruby -Ilib bin/geotag -g spec/fixtures/sample.gpx -s 400x400 -o samples/tmp/cli_test.png -u
157
+
@@ -40,6 +40,26 @@ if options[:zoom].nil? and options[:size].nil?
40
40
  @fail = true
41
41
  end
42
42
 
43
+ module GpxUtils
44
+ class TrackImporter
45
+ def self.make_label point
46
+ "#{point[:time].strftime('%H:%M:%S')}: (#{point[:lat]}, #{point[:lon]})"
47
+ end
48
+ def auto_marker
49
+ puts "Track starts: #{self.class.make_label self.coords[0]}"
50
+ puts "Track ends: #{self.class.make_label self.coords[-1]}"
51
+
52
+ (0..(self.coords.length/20)).each do |i|
53
+ index = i * 20
54
+ point = self.coords[index]
55
+ label = self.class.make_label point
56
+ yield({lat: point[:lat], lon: point[:lon], label: label})
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+
43
63
  unless @fail
44
64
  g = GpxUtils::TrackImporter.new
45
65
  g.add_file(options[:gpx])
@@ -50,6 +70,9 @@ unless @fail
50
70
  e = Gpx2png::Osm.new
51
71
  end
52
72
  e.coords = g.coords
73
+ g.auto_marker do |marker|
74
+ e.add_marker marker
75
+ end
53
76
 
54
77
  if options[:size]
55
78
  # constant size
@@ -65,4 +88,4 @@ unless @fail
65
88
  end
66
89
 
67
90
  # testing
68
- # ruby -Ilib bin/gpx2png -g spec/fixtures/sample.gpx -s 400x400 -o samples/tmp/cli_test.png -u
91
+ # ruby -Ilib bin/gpx2png -g spec/fixtures/sample.gpx -s 400x400 -o samples/tmp/cli_test.png -u
@@ -4,58 +4,75 @@ require 'mini_exiftool'
4
4
  $:.unshift(File.dirname(__FILE__))
5
5
 
6
6
  module Geotagger
7
- class ExifEditor
8
7
 
9
- def initialize(options = {})
10
- @images = Array.new
11
- @global_time_offset = 0
12
- @verbose = options[:verbose]
13
- end
8
+ # Wrapper class for path to image and the EXIF data read (and written)
9
+ # to the image.
10
+ class Image
14
11
 
15
- attr_reader :images
16
- attr_accessor :global_time_offset
12
+ attr_reader :editor, :photo, :attr
17
13
 
18
- def read_file(path, time_offset = 0)
19
- i = {
20
- :path => path,
21
- :time => get_photo_time(path) + time_offset + @global_time_offset
14
+ def initialize(editor, path, time_offset = 0)
15
+ @editor = editor
16
+ @photo = MiniExiftool.new path
17
+ @attr = {
18
+ :path => path,
19
+ :time => (time = @photo['DateTimeOriginal']) && (time + time_offset + editor.global_time_offset)
22
20
  }
23
- @images << i
24
- puts "Added file #{path}, time #{i[:time]}" if @verbose
25
21
  end
26
22
 
27
- def get_photo_time(path)
28
- photo = MiniExiftool.new path
29
- photo['DateTimeOriginal']
23
+ def path
24
+ self[:path]
25
+ end
26
+
27
+ def time
28
+ self[:time]
30
29
  end
31
30
 
32
- def set_photo_coords_internal(im)
33
- set_photo_coords(im[:path], im[:coord][:lat], im[:coord][:lon], im[:coord][:alt])
31
+ def [](key)
32
+ @attr[key]
34
33
  end
35
34
 
36
- def set_photo_coords(path, lat, lon, alt = 0.0)
37
- photo = MiniExiftool.new path
35
+ def []=(key,value)
36
+ @attr[key] = value
37
+ end
38
38
 
39
+ def get_photo_time(offset=0)
40
+ (time = photo['DateTimeOriginal']) && (time + offset)
41
+ end
42
+
43
+ def save!
39
44
  # http://en.wikipedia.org/wiki/Geotagging#JPEG_photos
40
45
 
46
+ photo['ProcessingSoftware'] = 'gpx2exif'
47
+
41
48
  photo['GPSVersionID'] = '2 2 0 0'
49
+ photo['DateTimeOriginal'] = self.time
42
50
 
43
- photo['GPSLatitude'] = lat
44
- photo['GPSLongitude'] = lon
51
+ photo['GPSLatitude'] = @attr[:coord][:lat]
52
+ photo['GPSLongitude'] = @attr[:coord][:lon]
45
53
 
46
- lat_ref = "N"
47
- lon_ref = "E"
48
- lat_ref = "S" if lat < 0.0
49
- lon_ref = "W" if lon < 0.0
54
+ lat_ref = (@attr[:coord][:lat] < 0.0) ? "S" : "N"
55
+ lon_ref = (@attr[:coord][:lon] < 0.0) ? "W" : "E"
50
56
 
51
57
  photo['GPSLatitudeRef'] = lat_ref
52
58
  photo['GPSLongitudeRef'] = lon_ref
53
59
 
54
- photo['GPSAltitude'] = alt
55
- photo.save
60
+ photo['GPSAltitude'] = @attr[:coord][:alt]
61
+ photo['Orientation'] = 1 # 1 means normal upright landscape mode
62
+ photo['GPSImgDirectionRef'] = 'T' # T=true north (as opposed to M=magnetic)
63
+ photo['GPSImgDirection'] = @attr[:coord][:direction] # calculated in TrackImporter
64
+ photo['GPSMapDatum'] = 'WGS-84' # We assume all GPS data is WGS-84
65
+
66
+ (editor.options[:exif_tags]||{}).each do |key,value|
67
+ photo[key] = value
68
+ end
69
+
70
+ unless photo.save
71
+ puts "Failed to save exif data to '#{path}': #{photo.errors.inspect}"
72
+ end
56
73
 
57
74
  photo2 = MiniExiftool.new path
58
- puts " - coord saved lat #{photo2['GPSLatitude']} lon #{photo2['GPSLongitude']}" if @verbose
75
+ puts " - coord saved lat #{photo2['GPSLatitude']} lon #{photo2['GPSLongitude']}" if editor.verbose
59
76
 
60
77
  # exiftool -GPSMapDatum="WGS-84" -gps:GPSLatitude="34,57,57"
61
78
  # -gps:GPSLatitudeRef="N" -gps:GPSLongitude="83,17,59" -gps:GPSLongitudeRef="W"
@@ -63,5 +80,51 @@ module Geotagger
63
80
  # -State="North Carolina" -Country="USA" ~/Desktop/RabunBaldSummit_NC.jpg
64
81
  end
65
82
 
83
+ def to_s
84
+ "Image[#{path}] at '#{time}'"
85
+ end
86
+
87
+ end
88
+
89
+ class ExifEditor
90
+
91
+ attr_reader :options, :verbose
92
+
93
+ def initialize(options = {})
94
+ @options = options
95
+ @images = []
96
+ @image_map = {}
97
+ @global_time_offset = 0
98
+ @verbose = options[:verbose]
99
+ end
100
+
101
+ attr_reader :images
102
+ attr_accessor :global_time_offset
103
+
104
+ def fix_times
105
+ start_time = options[:start_time] && DateTime.parse(options[:start_time]) || DateTime.now
106
+ puts "Start: #{start_time}"
107
+ @images.each_with_index do |image,index|
108
+ if image[:time].nil?
109
+ timestamp = start_time + ((options[:time_gap] || 1000).to_i * index) / (1000.0 * 24 * 60 * 60)
110
+ image[:time] = timestamp.to_time
111
+ end
112
+ puts "#{index}: #{image.attr.inspect}"
113
+ end
114
+ end
115
+
116
+ def read_file(path, time_offset = 0)
117
+ image = Image.new(self,path,time_offset)
118
+ @images << image
119
+ @image_map[path] = image
120
+ puts "Added #{image}" if @verbose
121
+ image
122
+ end
123
+
124
+ def get_photo_time(path)
125
+ image = @image_map[path] || read_file(path)
126
+ image.photo['DateTimeOriginal']
127
+ end
128
+
66
129
  end
67
130
  end
@@ -8,9 +8,11 @@ $:.unshift(File.dirname(__FILE__))
8
8
  module Geotagger
9
9
  class Geotagger
10
10
 
11
+ attr_reader :options, :ti
12
+
11
13
  def initialize(options = {})
12
14
  @verbose = options[:verbose]
13
- @ee = ExifEditor.new
15
+ @ee = ExifEditor.new options
14
16
  @ti = TrackImporter.new
15
17
  @ti.verbose = @verbose
16
18
  end
@@ -39,12 +41,19 @@ module Geotagger
39
41
  @ee.read_file(path, time_offset)
40
42
  end
41
43
 
44
+ def fix_times
45
+ @ee.fix_times
46
+ end
47
+
42
48
  def match_up
49
+ @ti.determine_directions
43
50
  @ee.images.each do |i|
44
- puts "* searching for #{i[:path]}, time #{i[:time]}" if @verbose
51
+ puts "* searching for #{i}" if @verbose
45
52
  i[:coord] = @ti.find_by_time(i[:time])
46
53
  if i[:coord].nil?
47
54
  puts " - not found" if @verbose
55
+ else
56
+ @ti.add_image_marker(i)
48
57
  end
49
58
  end
50
59
 
@@ -55,7 +64,7 @@ module Geotagger
55
64
  @ee.images.each do |i|
56
65
  if not i[:coord].nil?
57
66
  puts "! saving for #{i[:path]}" if @verbose
58
- @ee.set_photo_coords_internal(i)
67
+ i.save!
59
68
  end
60
69
 
61
70
  end
@@ -67,4 +76,4 @@ module Geotagger
67
76
  end
68
77
 
69
78
  end
70
- end
79
+ end
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'nokogiri'
3
+ require 'geokit'
3
4
 
4
5
  $:.unshift(File.dirname(__FILE__))
5
6
 
@@ -17,17 +18,70 @@ module Geotagger
17
18
  return false
18
19
  end
19
20
 
21
+ def determine_directions(index=0)
22
+ if @coords.length > 1
23
+ previous_point = nil
24
+ @coords.each do |coord|
25
+ point = Geokit::LatLng.new(coord[:lat], coord[:lon])
26
+ if previous_point
27
+ coord[:direction] = previous_point.heading_to(point)
28
+ end
29
+ previous_point = point
30
+ end
31
+ @coords[0][:direction] = @coords[1][:direction]
32
+ end
33
+ end
34
+
20
35
  def find_by_time(time)
21
- selected_coords = @coords.select { |c| (c[:time].localtime - time.localtime).abs < THRESHOLD }
22
- selected_coords = selected_coords.sort { |a, b| (a[:time].localtime - time.localtime).abs <=> (b[:time].localtime - time.localtime).abs }
23
- puts " - found #{selected_coords.size} coords within #{THRESHOLD}s from image time" if @verbose
24
- if selected_coords.size > 0
25
- puts " - best is #{selected_coords.first[:time].localtime}, time offset #{selected_coords.first[:time].localtime - time.localtime}" if @verbose
26
- puts " - lat #{selected_coords.first[:lat]} lon #{selected_coords.first[:lon]}" if @verbose
36
+ selected_coords = @coords.select do |c|
37
+ (c[:time].localtime - time.localtime).abs < THRESHOLD
38
+ end
39
+ selected_coords = selected_coords.sort do |a, b|
40
+ (a[:time].localtime - time.localtime).abs <=> (b[:time].localtime - time.localtime).abs
41
+ end
42
+ if @verbose
43
+ puts " - found #{selected_coords.size} coords within #{THRESHOLD}s from image time"
44
+ if selected_coords.size > 0
45
+ puts " - best is #{selected_coords.first[:time].localtime}, time offset #{selected_coords.first[:time].localtime - time.localtime}"
46
+ puts " - lat #{selected_coords.first[:lat]} lon #{selected_coords.first[:lon]}"
47
+ end
27
48
  end
28
49
 
29
50
  return selected_coords.first
30
51
  end
31
52
 
53
+ def self.make_label(point, image=nil)
54
+ "#{point[:time].strftime('%H:%M:%S')}: (#{point[:lat]}, #{point[:lon]})#{image.nil? ? '' : image[:path]}"
55
+ end
56
+
57
+ def add_image_marker(image)
58
+ @images ||= []
59
+ @images << image
60
+ end
61
+
62
+ def auto_marker
63
+ puts "Track starts: #{self.class.make_label self.coords[0]}"
64
+ puts "Track ends: #{self.class.make_label self.coords[-1]}"
65
+
66
+ coordset = self.coords.map do |coord|
67
+ image = @images.select {|i| i[:coord] == coord}[0]
68
+ {coord: coord, image: image}
69
+ end
70
+
71
+ prev_point = nil
72
+ coordset.each_with_index do |co,index|
73
+ coord = co[:coord]
74
+ image = co[:image]
75
+ puts "Labeling coord:#{coord} with image: #{image}" if image
76
+ point = Geokit::LatLng.new(coord[:lat], coord[:lon])
77
+ if prev_point.nil? || (distance = point.distance_from(prev_point, units: :kms) > 0.02)
78
+ label = self.class.make_label coord, image
79
+ prev_point = point
80
+ yield({lat: coord[:lat], lon: coord[:lon], label: label})
81
+ end
82
+ end
83
+
84
+ end
85
+
32
86
  end
33
- end
87
+ end
@@ -32,10 +32,11 @@ module Gpx2png
32
32
 
33
33
  def render
34
34
  setup_renderer
35
- initial_calculations
35
+ initial_calculations(@scale_options)
36
36
  download_and_join_tiles
37
37
  end
38
38
 
39
+ attr_accessor :scale_options
39
40
  attr_accessor :renderer_options
40
41
 
41
42
  # Get proper renderer class
@@ -54,4 +55,4 @@ module Gpx2png
54
55
  end
55
56
 
56
57
  end
57
- end
58
+ end
@@ -110,11 +110,36 @@ module Gpx2png
110
110
  @fixed_height = _height
111
111
  end
112
112
 
113
- def initial_calculations
114
- @lat_min = @coords.collect { |c| c[:lat] }.min
115
- @lat_max = @coords.collect { |c| c[:lat] }.max
116
- @lon_min = @coords.collect { |c| c[:lon] }.min
117
- @lon_max = @coords.collect { |c| c[:lon] }.max
113
+ def min_coords_or(key,val)
114
+ val || @coords.collect { |c| c[key] }.min
115
+ end
116
+
117
+ def max_coords_or(key,val)
118
+ val || @coords.collect { |c| c[key] }.max
119
+ end
120
+
121
+ def initial_calculations(scale={})
122
+ scale ||= {}
123
+ puts "Initializing with scale: #{scale.inspect}"
124
+ @lat_min = min_coords_or(:lat,scale[:lat_min])
125
+ @lat_max = max_coords_or(:lat,scale[:lat_max])
126
+ @lon_min = min_coords_or(:lon,scale[:lon_min])
127
+ @lon_max = max_coords_or(:lon,scale[:lon_max])
128
+
129
+ if scale[:scale].to_f > 0.00001
130
+ puts "Scaling from: (#{lat_min},#{lon_min})-(#{lat_max},#{lon_max})"
131
+ dlat = @lat_max - @lat_min
132
+ dlon = @lon_max - @lon_min
133
+ dlat2 = dlat * scale[:scale].to_f
134
+ dlon2 = dlon * scale[:scale].to_f
135
+ shift_x = dlon2 * scale[:shift_x].to_f
136
+ shift_y = dlat2 * scale[:shift_y].to_f
137
+ @lat_max += shift_y - (dlat - dlat2)/2.0
138
+ @lat_min += shift_y + (dlat - dlat2)/2.0
139
+ @lon_max += shift_x - (dlon - dlon2)/2.0
140
+ @lon_max += shift_x + (dlon - dlon2)/2.0
141
+ puts "Scaled to: (#{lat_min},#{lon_min})-(#{lat_max},#{lon_max})"
142
+ end
118
143
 
119
144
  # auto zoom must be here
120
145
  # drawing must fit into fixed resolution
@@ -207,4 +207,4 @@ module Gpx2png
207
207
  end
208
208
 
209
209
  end
210
- end
210
+ end
metadata CHANGED
@@ -1,147 +1,146 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gpx2exif
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.3.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aleksander Kwiatkowski
8
+ - Craig Taverner
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-08 00:00:00.000000000 Z
12
+ date: 2014-04-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mini_exiftool
16
16
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
17
  requirements:
19
- - - ! '>='
18
+ - - ">="
20
19
  - !ruby/object:Gem::Version
21
20
  version: '0'
22
21
  type: :runtime
23
22
  prerelease: false
24
23
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
24
  requirements:
27
- - - ! '>='
25
+ - - ">="
28
26
  - !ruby/object:Gem::Version
29
27
  version: '0'
30
28
  - !ruby/object:Gem::Dependency
31
29
  name: rmagick
32
30
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
31
  requirements:
35
- - - ! '>='
32
+ - - ">="
36
33
  - !ruby/object:Gem::Version
37
34
  version: '0'
38
35
  type: :runtime
39
36
  prerelease: false
40
37
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
38
  requirements:
43
- - - ! '>='
39
+ - - ">="
44
40
  - !ruby/object:Gem::Version
45
41
  version: '0'
46
42
  - !ruby/object:Gem::Dependency
47
43
  name: chunky_png
48
44
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
45
  requirements:
51
- - - ! '>='
46
+ - - ">="
52
47
  - !ruby/object:Gem::Version
53
48
  version: '0'
54
49
  type: :runtime
55
50
  prerelease: false
56
51
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
52
  requirements:
59
- - - ! '>='
53
+ - - ">="
60
54
  - !ruby/object:Gem::Version
61
55
  version: '0'
62
56
  - !ruby/object:Gem::Dependency
63
57
  name: gpx_utils
64
58
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
59
  requirements:
67
- - - ! '>='
60
+ - - ">="
68
61
  - !ruby/object:Gem::Version
69
62
  version: '0'
70
63
  type: :runtime
71
64
  prerelease: false
72
65
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
66
  requirements:
75
- - - ! '>='
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: geokit
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
76
82
  - !ruby/object:Gem::Version
77
83
  version: '0'
78
84
  - !ruby/object:Gem::Dependency
79
85
  name: rspec
80
86
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
87
  requirements:
83
- - - ! '>='
88
+ - - ">="
84
89
  - !ruby/object:Gem::Version
85
90
  version: '0'
86
91
  type: :development
87
92
  prerelease: false
88
93
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
94
  requirements:
91
- - - ! '>='
95
+ - - ">="
92
96
  - !ruby/object:Gem::Version
93
97
  version: '0'
94
98
  - !ruby/object:Gem::Dependency
95
99
  name: bundler
96
100
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
101
  requirements:
99
- - - ! '>='
102
+ - - ">="
100
103
  - !ruby/object:Gem::Version
101
104
  version: '0'
102
105
  type: :development
103
106
  prerelease: false
104
107
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
108
  requirements:
107
- - - ! '>='
109
+ - - ">="
108
110
  - !ruby/object:Gem::Version
109
111
  version: '0'
110
112
  - !ruby/object:Gem::Dependency
111
113
  name: jeweler
112
114
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
115
  requirements:
115
- - - ! '>='
116
+ - - ">="
116
117
  - !ruby/object:Gem::Version
117
118
  version: '0'
118
119
  type: :development
119
120
  prerelease: false
120
121
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
122
  requirements:
123
- - - ! '>='
123
+ - - ">="
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: simplecov
128
128
  requirement: !ruby/object:Gem::Requirement
129
- none: false
130
129
  requirements:
131
- - - ! '>='
130
+ - - ">="
132
131
  - !ruby/object:Gem::Version
133
132
  version: '0'
134
133
  type: :development
135
134
  prerelease: false
136
135
  version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
136
  requirements:
139
- - - ! '>='
137
+ - - ">="
140
138
  - !ruby/object:Gem::Version
141
139
  version: '0'
142
140
  description: Mass geotagger using GPX files.
143
141
  email: bobikx@poczta.fm
144
142
  executables:
143
+ - geotag
145
144
  - geotag_all_images
146
145
  - geotag_simulate
147
146
  - gpx2png
@@ -156,6 +155,7 @@ files:
156
155
  - README.md
157
156
  - Rakefile
158
157
  - VERSION
158
+ - bin/geotag
159
159
  - bin/geotag_all_images
160
160
  - bin/geotag_simulate
161
161
  - bin/gpx2png
@@ -178,29 +178,25 @@ files:
178
178
  homepage: http://github.com/akwiatkowski/gpx2exif
179
179
  licenses:
180
180
  - LGPLv3
181
+ metadata: {}
181
182
  post_install_message:
182
183
  rdoc_options: []
183
184
  require_paths:
184
185
  - lib
185
186
  required_ruby_version: !ruby/object:Gem::Requirement
186
- none: false
187
187
  requirements:
188
- - - ! '>='
188
+ - - ">="
189
189
  - !ruby/object:Gem::Version
190
190
  version: '0'
191
- segments:
192
- - 0
193
- hash: -2766757745175557176
194
191
  required_rubygems_version: !ruby/object:Gem::Requirement
195
- none: false
196
192
  requirements:
197
- - - ! '>='
193
+ - - ">="
198
194
  - !ruby/object:Gem::Version
199
195
  version: '0'
200
196
  requirements: []
201
197
  rubyforge_project:
202
- rubygems_version: 1.8.25
198
+ rubygems_version: 2.2.2
203
199
  signing_key:
204
- specification_version: 3
200
+ specification_version: 4
205
201
  summary: Mass geotagger using GPX files
206
202
  test_files: []