gpx2exif 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []