gpx2exif 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -8
- data/README.md +28 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/generate_garmin_waypoints +1 -1
- data/bin/geotag_all_images +2 -2
- data/bin/geotag_simulate +2 -2
- data/bin/gpx2png +57 -0
- data/lib/geotagger.rb +9 -0
- data/lib/{gpx2exif → geotagger}/exif_editor.rb +6 -6
- data/lib/{gpx2exif/geo_manager.rb → geotagger/geotagger.rb} +16 -10
- data/lib/geotagger/track_importer.rb +33 -0
- data/lib/gpx2exif.rb +3 -7
- data/lib/gpx2png/base.rb +30 -0
- data/lib/gpx2png/gpx2png.rb +210 -0
- data/lib/gpx2png/osm.rb +51 -0
- data/lib/gpx2png/osm_base.rb +280 -0
- data/lib/gpx2png/renderers/chunky_png_renderer.rb +61 -0
- data/lib/gpx2png/renderers/rmagick_renderer.rb +134 -0
- data/lib/gpx_utils.rb +9 -0
- data/lib/gpx_utils/track_importer.rb +63 -0
- data/lib/{garmin_utils/waypoint_list_generator.rb → gpx_utils/waypoints_exporter.rb} +2 -2
- data/lib/{garmin_utils/gpx_waypoint_parser.rb → gpx_utils/waypoints_importer.rb} +3 -3
- data/lib/mini_exiftool/mini_exiftool.rb +450 -0
- metadata +35 -23
- data/lib/garmin_utils.rb +0 -7
- data/lib/gpx2exif/gpx_parser.rb +0 -71
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
GIT
|
2
|
-
remote: git://github.com/akwiatkowski/mini_exiftool.git
|
3
|
-
revision: e2ca8e0c3de9190bd08520d6c4204d510963eb54
|
4
|
-
specs:
|
5
|
-
mini_exiftool (1.5.1)
|
6
|
-
|
7
1
|
GEM
|
8
2
|
remote: http://rubygems.org/
|
9
3
|
specs:
|
@@ -14,8 +8,9 @@ GEM
|
|
14
8
|
bundler (~> 1.0)
|
15
9
|
git (>= 1.2.5)
|
16
10
|
rake
|
11
|
+
mini_exiftool (1.5.1)
|
17
12
|
multi_json (1.3.6)
|
18
|
-
nokogiri (1.5.
|
13
|
+
nokogiri (1.5.5)
|
19
14
|
rake (0.9.2.2)
|
20
15
|
rspec (2.3.0)
|
21
16
|
rspec-core (~> 2.3.0)
|
@@ -37,7 +32,7 @@ DEPENDENCIES
|
|
37
32
|
builder
|
38
33
|
bundler (~> 1.0.0)
|
39
34
|
jeweler (~> 1.6.4)
|
40
|
-
mini_exiftool
|
35
|
+
mini_exiftool
|
41
36
|
nokogiri
|
42
37
|
rspec (~> 2.3.0)
|
43
38
|
simplecov
|
data/README.md
CHANGED
@@ -65,6 +65,34 @@ How to use it
|
|
65
65
|
|
66
66
|
generate_garmin_waypoints -y samples/sample_yaml_pois.yml -o file.gpx
|
67
67
|
|
68
|
+
|
69
|
+
Render track with OpenStreetMap
|
70
|
+
---------------------
|
71
|
+
|
72
|
+
You can "convert" your tracks to images using this command.
|
73
|
+
|
74
|
+
How to use it
|
75
|
+
-------------
|
76
|
+
|
77
|
+
1. Please check if you have installed RMagick gem.
|
78
|
+
|
79
|
+
2. Run command.
|
80
|
+
|
81
|
+
gpx2png -g <input GPX file> -s <image size, format: WIDTHxHEIGHT> -o <output PPNG file>
|
82
|
+
|
83
|
+
Example:
|
84
|
+
|
85
|
+
gpx2png -g spec/fixtures/sample.gpx -s 800x600 -o map.png
|
86
|
+
|
87
|
+
3. You can specify zoom.
|
88
|
+
|
89
|
+
gpx2png -g <input GPX file> -z <zoom, best results if between 9 and 15, max 18> -o <output PPNG file>
|
90
|
+
|
91
|
+
Example:
|
92
|
+
|
93
|
+
gpx2png -g spec/fixtures/sample.gpx -z 11 -o map.png
|
94
|
+
|
95
|
+
|
68
96
|
Contributing to gpx2xif
|
69
97
|
-------------------------------
|
70
98
|
|
data/Rakefile
CHANGED
@@ -22,7 +22,7 @@ Jeweler::Tasks.new do |gem|
|
|
22
22
|
gem.email = "bobikx@poczta.fm"
|
23
23
|
gem.authors = ["Aleksander Kwiatkowski"]
|
24
24
|
# dependencies defined in Gemfile
|
25
|
-
gem.executables = ['geotag_all_images', 'geotag_simulate']
|
25
|
+
gem.executables = ['geotag_all_images', 'geotag_simulate', 'generate_garmin_waypoints', 'gpx2png']
|
26
26
|
|
27
27
|
gem.files = FileList[
|
28
28
|
"[A-Z]*", "{bin,generators,lib,test}/**/*"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/bin/geotag_all_images
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
4
|
+
require 'geotagger/geotagger'
|
5
5
|
|
6
6
|
puts "Are you sure? It is evil script which probably eat photos of your dog and family. Uppercase 'yes' and enter if you want to continue."
|
7
7
|
str = gets
|
@@ -12,7 +12,7 @@ time_offset = time_offset.to_i
|
|
12
12
|
|
13
13
|
exit(0) unless str.strip == 'YES'
|
14
14
|
|
15
|
-
g =
|
15
|
+
g = Geotagger::Geotagger.new(verbose: true)
|
16
16
|
g.add_all_files(time_offset)
|
17
17
|
g.match_up
|
18
18
|
g.save!
|
data/bin/geotag_simulate
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require '
|
4
|
+
require 'geotagger/geotagger'
|
5
5
|
|
6
6
|
#puts "Are you sure? It is evil script which probably eat photos of your dog and family. Uppercase 'yes' and enter if you want to continue."
|
7
7
|
#str = gets
|
@@ -11,7 +11,7 @@ puts "Do you want to add offset to image time? Default is 0 seconds."
|
|
11
11
|
time_offset = gets
|
12
12
|
time_offset = time_offset.to_i
|
13
13
|
|
14
|
-
g =
|
14
|
+
g = Geotagger::Geotagger.new(verbose: true)
|
15
15
|
g.add_all_files(time_offset)
|
16
16
|
g.match_up
|
17
17
|
g.simulate
|
data/bin/gpx2png
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gpx2exif'
|
5
|
+
require 'gpx2png/osm'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
options = { }
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: gpx2png [options]"
|
11
|
+
|
12
|
+
opts.on("-g", "--gpx FILE", "Input GPX file") do |v|
|
13
|
+
options[:gpx] = v
|
14
|
+
end
|
15
|
+
opts.on("-z", "--zoom ZOOM", "Set zoom") do |v|
|
16
|
+
options[:zoom] = v
|
17
|
+
end
|
18
|
+
opts.on("-s", "--size WIDTHxHEIGHT", "Set output image size (better result)") do |v|
|
19
|
+
options[:size] = v
|
20
|
+
end
|
21
|
+
opts.on("-o", "--output FILE", "Output image file") do |v|
|
22
|
+
options[:output_file] = v
|
23
|
+
end
|
24
|
+
end.parse!
|
25
|
+
|
26
|
+
unless options[:gpx]
|
27
|
+
puts "Input GPX file needed"
|
28
|
+
@fail = true
|
29
|
+
end
|
30
|
+
unless options[:output_file]
|
31
|
+
puts "Output image file needed"
|
32
|
+
@fail = true
|
33
|
+
end
|
34
|
+
if options[:zoom].nil? and options[:size].nil?
|
35
|
+
puts "Zoom or image size needed"
|
36
|
+
@fail = true
|
37
|
+
end
|
38
|
+
|
39
|
+
unless @fail
|
40
|
+
g = GpxUtils::TrackImporter.new
|
41
|
+
g.add_file(options[:gpx])
|
42
|
+
|
43
|
+
e = Gpx2png::Osm.new
|
44
|
+
e.coords = g.coords
|
45
|
+
|
46
|
+
if options[:size]
|
47
|
+
# constant size
|
48
|
+
options[:size] =~ /(\d+)x(\d+)/
|
49
|
+
e.fixed_size($1.to_i, $1.to_i)
|
50
|
+
else
|
51
|
+
# constant zoom
|
52
|
+
e.zoom = options[:zoom].to_i
|
53
|
+
e.renderer_options = {crop_enabled: true}
|
54
|
+
end
|
55
|
+
|
56
|
+
e.save(options[:output_file])
|
57
|
+
end
|
data/lib/geotagger.rb
ADDED
@@ -3,12 +3,13 @@ require 'mini_exiftool'
|
|
3
3
|
|
4
4
|
$:.unshift(File.dirname(__FILE__))
|
5
5
|
|
6
|
-
module
|
6
|
+
module Geotagger
|
7
7
|
class ExifEditor
|
8
8
|
|
9
|
-
def initialize
|
9
|
+
def initialize(options = {})
|
10
10
|
@images = Array.new
|
11
11
|
@global_time_offset = 0
|
12
|
+
@verbose = options[:verbose]
|
12
13
|
end
|
13
14
|
|
14
15
|
attr_reader :images
|
@@ -20,7 +21,7 @@ module Gpx2exif
|
|
20
21
|
:time => get_photo_time(path) + time_offset + @global_time_offset
|
21
22
|
}
|
22
23
|
@images << i
|
23
|
-
puts "Added file #{path}, time #{i[:time]}"
|
24
|
+
puts "Added file #{path}, time #{i[:time]}" if @verbose
|
24
25
|
end
|
25
26
|
|
26
27
|
def get_photo_time(path)
|
@@ -54,14 +55,13 @@ module Gpx2exif
|
|
54
55
|
photo.save
|
55
56
|
|
56
57
|
photo2 = MiniExiftool.new path
|
57
|
-
puts " - coord saved lat #{photo2['GPSLatitude']} lon #{photo2['GPSLongitude']}"
|
58
|
+
puts " - coord saved lat #{photo2['GPSLatitude']} lon #{photo2['GPSLongitude']}" if @verbose
|
58
59
|
|
59
60
|
# exiftool -GPSMapDatum="WGS-84" -gps:GPSLatitude="34,57,57"
|
60
61
|
# -gps:GPSLatitudeRef="N" -gps:GPSLongitude="83,17,59" -gps:GPSLongitudeRef="W"
|
61
62
|
# -gps:GPSAltitudeRef="0" -GPSAltitude=1426 -gps:GPSMeasureMode=2 -City="RabunBald"
|
62
63
|
# -State="North Carolina" -Country="USA" ~/Desktop/RabunBaldSummit_NC.jpg
|
63
|
-
|
64
64
|
end
|
65
65
|
|
66
66
|
end
|
67
|
-
end
|
67
|
+
end
|
@@ -1,15 +1,21 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'gpx_utils'
|
3
|
+
require 'geotagger/exif_editor'
|
4
|
+
require 'geotagger/track_importer'
|
2
5
|
|
3
6
|
$:.unshift(File.dirname(__FILE__))
|
4
7
|
|
5
|
-
module
|
6
|
-
class
|
8
|
+
module Geotagger
|
9
|
+
class Geotagger
|
7
10
|
|
8
|
-
def initialize
|
11
|
+
def initialize(options = {})
|
12
|
+
@verbose = options[:verbose]
|
9
13
|
@ee = ExifEditor.new
|
10
|
-
@
|
14
|
+
@ti = TrackImporter.new
|
15
|
+
@ti.verbose = @verbose
|
11
16
|
end
|
12
17
|
|
18
|
+
# Add all GPX and images with
|
13
19
|
def add_all_files(time_offset = 0)
|
14
20
|
# add all GPX
|
15
21
|
Dir.glob("**/*.GPX", File::FNM_CASEFOLD).each do |f|
|
@@ -26,7 +32,7 @@ module Gpx2exif
|
|
26
32
|
end
|
27
33
|
|
28
34
|
def add_gpx_file(path)
|
29
|
-
@
|
35
|
+
@ti.add_file(path)
|
30
36
|
end
|
31
37
|
|
32
38
|
def add_image(path, time_offset = 0)
|
@@ -35,10 +41,10 @@ module Gpx2exif
|
|
35
41
|
|
36
42
|
def match_up
|
37
43
|
@ee.images.each do |i|
|
38
|
-
puts "* searching for #{i[:path]}, time #{i[:time]}"
|
39
|
-
i[:coord] = @
|
44
|
+
puts "* searching for #{i[:path]}, time #{i[:time]}" if @verbose
|
45
|
+
i[:coord] = @ti.find_by_time(i[:time])
|
40
46
|
if i[:coord].nil?
|
41
|
-
puts " - not found"
|
47
|
+
puts " - not found" if @verbose
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
@@ -48,7 +54,7 @@ module Gpx2exif
|
|
48
54
|
def save!
|
49
55
|
@ee.images.each do |i|
|
50
56
|
if not i[:coord].nil?
|
51
|
-
puts "! saving for #{i[:path]}"
|
57
|
+
puts "! saving for #{i[:path]}" if @verbose
|
52
58
|
@ee.set_photo_coords_internal(i)
|
53
59
|
end
|
54
60
|
|
@@ -57,7 +63,7 @@ module Gpx2exif
|
|
57
63
|
|
58
64
|
def simulate
|
59
65
|
to_process = @ee.images.select { |i| not i[:coord].nil? }
|
60
|
-
puts "Result: to update #{to_process.size} from #{@ee.images.size}"
|
66
|
+
puts "Result: to update #{to_process.size} from #{@ee.images.size}" if @verbose
|
61
67
|
end
|
62
68
|
|
63
69
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
$:.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
# Simple parsing GPX file
|
7
|
+
module Geotagger
|
8
|
+
class TrackImporter < GpxUtils::TrackImporter
|
9
|
+
|
10
|
+
THRESHOLD = 5*60
|
11
|
+
|
12
|
+
attr_accessor :verbose
|
13
|
+
|
14
|
+
# Only import valid coords
|
15
|
+
def self.coord_valid?(lat, lon, elevation, time)
|
16
|
+
return true if lat and lon and time
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
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
|
27
|
+
end
|
28
|
+
|
29
|
+
return selected_coords.first
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/gpx2exif.rb
CHANGED
data/lib/gpx2png/base.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
$:.unshift(File.dirname(__FILE__))
|
4
|
+
|
5
|
+
module Gpx2png
|
6
|
+
class Base
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@coords = Array.new
|
10
|
+
@zoom = 9
|
11
|
+
@verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(lat, lon)
|
15
|
+
@coords << { lat: lat, lon: lon }
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :zoom, :color, :coords
|
19
|
+
|
20
|
+
# Some math stuff
|
21
|
+
def self.rad2deg(rad)
|
22
|
+
return rad * 180.0 / Math::PI
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.deg2rad(deg)
|
26
|
+
return deg * Math::PI / 180.0
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'chunky_png'
|
3
|
+
require 'net/http'
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
|
7
|
+
$:.unshift(File.dirname(__FILE__))
|
8
|
+
|
9
|
+
module Gpx2png
|
10
|
+
class Gpx2png
|
11
|
+
|
12
|
+
TILE_WIDTH = 256
|
13
|
+
TILE_HEIGHT = 256
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@coords = Array.new
|
17
|
+
@zoom = 9
|
18
|
+
@color = ChunkyPNG::Color.from_hex('#FF0000')
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(lat, lon)
|
22
|
+
@coords << { lat: lat, lon: lon }
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :zoom, :color, :coords
|
26
|
+
|
27
|
+
def dev
|
28
|
+
zoom = 15
|
29
|
+
@coords.collect { |c|
|
30
|
+
{
|
31
|
+
url: self.class.url(zoom, [c[:lat], c[:lon]]),
|
32
|
+
tile: self.class.convert(zoom, [c[:lat], c[:lon]]),
|
33
|
+
return: self.class.reverse_convert(zoom,
|
34
|
+
self.class.convert(zoom, [c[:lat], c[:lon]])
|
35
|
+
),
|
36
|
+
point: self.class.point_on_image(zoom, [c[:lat], c[:lon]])
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_png(filename)
|
42
|
+
download_and_join_tiles
|
43
|
+
@full_image.save(filename)
|
44
|
+
filename
|
45
|
+
end
|
46
|
+
|
47
|
+
# http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#X_and_Y
|
48
|
+
def self.convert(zoom, coord)
|
49
|
+
lat_deg, lon_deg = coord
|
50
|
+
lat_rad = deg2rad(lat_deg)
|
51
|
+
x = (((lon_deg + 180) / 360) * (2 ** zoom)).floor
|
52
|
+
y = ((1 - Math.log(Math.tan(lat_rad) + 1 / Math.cos(lat_rad)) / Math::PI) /2 * (2 ** zoom)).floor
|
53
|
+
|
54
|
+
return [x, y]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.url_convert(zoom, coord, server = 'b.')
|
58
|
+
x, y = convert(zoom, coord)
|
59
|
+
url(zoom, [x, y], server)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.url(zoom, coord, server = 'b.')
|
63
|
+
x, y = coord
|
64
|
+
url = "http://#{server}tile.openstreetmap.org\/#{zoom}\/#{x}\/#{y}.png"
|
65
|
+
return url
|
66
|
+
end
|
67
|
+
|
68
|
+
# top-left corner
|
69
|
+
def self.reverse_convert(zoom, coord)
|
70
|
+
x, y = coord
|
71
|
+
n = 2 ** zoom
|
72
|
+
lon_deg = x.to_f / n.to_f * 360.0 - 180.0
|
73
|
+
lat_deg = rad2deg(Math.atan(Math.sinh(Math::PI * (1.to_f - 2.to_f * y.to_f / n.to_f))))
|
74
|
+
return [lat_deg, lon_deg]
|
75
|
+
end
|
76
|
+
|
77
|
+
# return where you should put point on tile
|
78
|
+
def self.point_on_image(zoom, geo_coord)
|
79
|
+
osm_tile_coord = convert(zoom, geo_coord)
|
80
|
+
top_left_corner = reverse_convert(zoom, osm_tile_coord)
|
81
|
+
bottom_right_corner = reverse_convert(zoom, [
|
82
|
+
osm_tile_coord[0] + 1, osm_tile_coord[1] + 1
|
83
|
+
])
|
84
|
+
|
85
|
+
# some line y = ax + b math
|
86
|
+
|
87
|
+
x_geo = geo_coord[1]
|
88
|
+
# offset
|
89
|
+
x_offset = x_geo - top_left_corner[1]
|
90
|
+
# scale
|
91
|
+
x_distance = (bottom_right_corner[1] - top_left_corner[1])
|
92
|
+
x = (TILE_WIDTH.to_f * (x_offset / x_distance)).round
|
93
|
+
|
94
|
+
y_geo = geo_coord[0]
|
95
|
+
# offset
|
96
|
+
y_offset = y_geo - top_left_corner[0]
|
97
|
+
# scale
|
98
|
+
y_distance = (bottom_right_corner[0] - top_left_corner[0])
|
99
|
+
y = (TILE_HEIGHT.to_f * (y_offset / y_distance)).round
|
100
|
+
|
101
|
+
return { osm_title_coord: osm_tile_coord, pixel_offset: [x, y] }
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
attr_reader :lat_min, :lat_max, :lon_min, :lon_max
|
106
|
+
attr_reader :tile_x_distance, :tile_y_distance
|
107
|
+
|
108
|
+
def download_and_join_tiles
|
109
|
+
@lat_min = @coords.collect { |c| c[:lat] }.min
|
110
|
+
@lat_max = @coords.collect { |c| c[:lat] }.max
|
111
|
+
@lon_min = @coords.collect { |c| c[:lon] }.min
|
112
|
+
@lon_max = @coords.collect { |c| c[:lon] }.max
|
113
|
+
|
114
|
+
@border_tiles = [
|
115
|
+
self.class.convert(@zoom, [@lat_min, @lon_min]),
|
116
|
+
self.class.convert(@zoom, [@lat_max, @lon_max])
|
117
|
+
]
|
118
|
+
|
119
|
+
@tile_x_range = (@border_tiles[0][0])..(@border_tiles[1][0])
|
120
|
+
@tile_y_range = (@border_tiles[1][1])..(@border_tiles[0][1])
|
121
|
+
|
122
|
+
# new image
|
123
|
+
@full_image_x = (1 + @tile_x_range.max - @tile_x_range.min) * TILE_WIDTH
|
124
|
+
@full_image_y = (1 + @tile_y_range.max - @tile_y_range.min) * TILE_HEIGHT
|
125
|
+
puts "Output image dimension #{@full_image_x}x#{@full_image_y}"
|
126
|
+
@full_image = ChunkyPNG::Image.new(
|
127
|
+
@full_image_x,
|
128
|
+
@full_image_y,
|
129
|
+
ChunkyPNG::Color::WHITE
|
130
|
+
)
|
131
|
+
|
132
|
+
# {:x, :y, :blob}
|
133
|
+
@images = Array.new
|
134
|
+
|
135
|
+
@tile_x_range.each do |x|
|
136
|
+
@tile_y_range.each do |y|
|
137
|
+
url = self.class.url(@zoom, [x,y])
|
138
|
+
|
139
|
+
# blob time
|
140
|
+
uri = URI.parse(url)
|
141
|
+
response = Net::HTTP.get_response(uri)
|
142
|
+
blob = response.body
|
143
|
+
image = ChunkyPNG::Image.from_blob(blob)
|
144
|
+
|
145
|
+
@images << {
|
146
|
+
url: url,
|
147
|
+
image: image,
|
148
|
+
x: x,
|
149
|
+
y: y
|
150
|
+
}
|
151
|
+
|
152
|
+
# compose image
|
153
|
+
x_offset = (x - @tile_x_range.min) * TILE_WIDTH
|
154
|
+
y_offset = (y - @tile_y_range.min) * TILE_HEIGHT
|
155
|
+
@full_image.compose!(
|
156
|
+
image,
|
157
|
+
x_offset,
|
158
|
+
y_offset
|
159
|
+
)
|
160
|
+
|
161
|
+
puts "processed #{x - @tile_x_range.min}x#{y - @tile_y_range.min} (max #{@tile_x_range.max - @tile_x_range.min}x#{@tile_y_range.max - @tile_y_range.min})"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# sweet, image is joined
|
166
|
+
|
167
|
+
# add some coords to the map
|
168
|
+
(1...@coords.size).each do |i|
|
169
|
+
lat_from = @coords[i-1][:lat]
|
170
|
+
lon_from = @coords[i-1][:lon]
|
171
|
+
|
172
|
+
lat_to = @coords[i][:lat]
|
173
|
+
lon_to = @coords[i][:lon]
|
174
|
+
|
175
|
+
point_from = self.class.point_on_image(@zoom, [lat_from, lon_from])
|
176
|
+
point_to = self.class.point_on_image(@zoom, [lat_to, lon_to])
|
177
|
+
# { osm_title_coord: osm_tile_coord, pixel_offset: [x, y] }
|
178
|
+
|
179
|
+
# first point
|
180
|
+
bitmap_xa = (point_from[:osm_title_coord][0] - @tile_x_range.min) * TILE_WIDTH + point_from[:pixel_offset][0]
|
181
|
+
bitmap_ya = (point_from[:osm_title_coord][1] - @tile_y_range.min) * TILE_HEIGHT + point_from[:pixel_offset][1]
|
182
|
+
bitmap_xb = (point_to[:osm_title_coord][0] - @tile_x_range.min) * TILE_WIDTH + point_to[:pixel_offset][0]
|
183
|
+
bitmap_yb = (point_to[:osm_title_coord][1] - @tile_y_range.min) * TILE_HEIGHT + point_to[:pixel_offset][1]
|
184
|
+
|
185
|
+
@full_image.line(
|
186
|
+
bitmap_xa, bitmap_ya,
|
187
|
+
bitmap_xb, bitmap_yb,
|
188
|
+
@color
|
189
|
+
)
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def expand_map
|
195
|
+
# TODO expand min and max ranges
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
# Some math stuff
|
200
|
+
def self.rad2deg(rad)
|
201
|
+
return rad * 180.0 / Math::PI
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.deg2rad(deg)
|
205
|
+
return deg * Math::PI / 180.0
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|