google-maps-stitch-bin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/tiler +82 -0
- data/lib/tiler/tile.rb +37 -0
- data/lib/tiler/tiles.rb +81 -0
- data/lib/tiler.rb +102 -0
- data/spec/lib/tiler_spec.rb +36 -0
- data/spec/spec_helper.rb +20 -0
- metadata +70 -0
data/bin/tiler
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'tiler'
|
7
|
+
|
8
|
+
###require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "tiler"))
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
|
12
|
+
optparse = OptionParser.new do |opts|
|
13
|
+
|
14
|
+
opts.banner = "Usage: tiler.rb [options] ..."
|
15
|
+
|
16
|
+
opts.on('--start_lat LAT', Float, 'Latitude of startpoint (top left)' ) do |start_lat|
|
17
|
+
options[:start_lat] = start_lat
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on('--start_lon LON', Float, 'Longitude of startpoint (top left)' ) do |start_lon|
|
21
|
+
options[:start_lon] = start_lon
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on('--end_lat LAT', Float, 'Latitude of endpoint (bottom right)' ) do |end_lat|
|
25
|
+
options[:end_lat] = end_lat
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('--end_lon LON', Float, 'Longitude of endoint (bottom right)' ) do |end_lon|
|
29
|
+
options[:end_lon] = end_lon
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('--zoom ZOOM_LEVEL', Integer, 'Zoom Level in Google maps') do |zoom|
|
33
|
+
if (1..18).include?(zoom)
|
34
|
+
options[:zoom] = zoom
|
35
|
+
else
|
36
|
+
raise OptionParser::InvalidArgument
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on('--source SOURCE', String, '"map" or "sattelite". Default is "sattelite"') do |source|
|
41
|
+
if ["map", "sattelite"].include?(source)
|
42
|
+
options[:source] = source
|
43
|
+
else
|
44
|
+
raise OptionParser::InvalidArgument
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on('--output FILEPATH', String, 'select output file') do |output|
|
49
|
+
options[:output] = output
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
opts.on( '-h', '--help', 'Display this help' ) do
|
54
|
+
puts opts
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
begin
|
61
|
+
optparse.parse!
|
62
|
+
mandatory_opts = [:output, :start_lat, :start_lon, :end_lat, :end_lon, :zoom ]
|
63
|
+
missing_opts = mandatory_opts.select { |param| options[param].nil? }
|
64
|
+
|
65
|
+
if !missing_opts.empty?
|
66
|
+
puts
|
67
|
+
puts "Missing options: #{missing_opts.join(', ')}"
|
68
|
+
puts
|
69
|
+
puts optparse
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption, OptionParser::MissingArgument => e
|
74
|
+
puts
|
75
|
+
puts e.inspect
|
76
|
+
puts
|
77
|
+
puts optparse
|
78
|
+
exit
|
79
|
+
end
|
80
|
+
|
81
|
+
tiler = Tiler.new(options)
|
82
|
+
tiler.run
|
data/lib/tiler/tile.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
class Tile
|
3
|
+
|
4
|
+
attr_reader :x, :y, :z
|
5
|
+
|
6
|
+
attr_accessor :file
|
7
|
+
|
8
|
+
def initialize(args)
|
9
|
+
@x = args[:x]
|
10
|
+
@y = args[:y]
|
11
|
+
@z = args[:z]
|
12
|
+
@source = args[:source] || "sattelite"
|
13
|
+
end
|
14
|
+
|
15
|
+
def remote_url
|
16
|
+
"https://khms0.google.com/kh/v=143&src=app&x=#{x}&y=#{y}&z=#{z.to_s}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def download
|
20
|
+
open(remote_url) do |image|
|
21
|
+
file = Tempfile.new("x_#{x}_y_#{y}_z_#{z}")
|
22
|
+
file.write(image.read)
|
23
|
+
puts "downloaded #{remote_url} to #{file.path}"
|
24
|
+
@file = file
|
25
|
+
@local_file_name = file.path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def local_file_name
|
30
|
+
@local_file_name
|
31
|
+
end
|
32
|
+
|
33
|
+
def downloaded?
|
34
|
+
!@local_file_name.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/tiler/tiles.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
class TilesCollection
|
2
|
+
|
3
|
+
attr_accessor :zoom, :source, :output_file
|
4
|
+
attr_reader :start, :end
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@zoom = args[:zoom]
|
8
|
+
@source = args[:source] || "sattelite"
|
9
|
+
@start_x = args[:start_x]
|
10
|
+
@start_y = args[:start_y]
|
11
|
+
@end_x = args[:end_x]
|
12
|
+
@end_y = args[:end_y]
|
13
|
+
@output = args[:output]
|
14
|
+
save_tiles
|
15
|
+
end
|
16
|
+
|
17
|
+
def tile(x,y)
|
18
|
+
@tiles[y][x]
|
19
|
+
end
|
20
|
+
|
21
|
+
def rows
|
22
|
+
@start_y.upto(@end_y)
|
23
|
+
end
|
24
|
+
|
25
|
+
def columns
|
26
|
+
@start_x.upto(@end_x)
|
27
|
+
end
|
28
|
+
|
29
|
+
def all
|
30
|
+
@tiles
|
31
|
+
end
|
32
|
+
|
33
|
+
def flatten
|
34
|
+
rows.map do |y|
|
35
|
+
columns.map do |x|
|
36
|
+
tile(x,y)
|
37
|
+
end
|
38
|
+
end.flatten
|
39
|
+
end
|
40
|
+
|
41
|
+
def save_tile(x,y,tile)
|
42
|
+
@tiles[y] = {} unless @tiles[y]
|
43
|
+
@tiles[y][x] = tile
|
44
|
+
end
|
45
|
+
|
46
|
+
def save_tiles
|
47
|
+
@tiles = {}
|
48
|
+
rows.map do |y|
|
49
|
+
columns.map do |x|
|
50
|
+
save_tile(x,y,Tile.new(x: x, y: y, z: @zoom))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def download
|
56
|
+
flatten.each{ |tile| tile.download }
|
57
|
+
end
|
58
|
+
|
59
|
+
def cleanup
|
60
|
+
flatten.each do |tile|
|
61
|
+
tile.file.close
|
62
|
+
tile.file.unlink
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def stitch
|
67
|
+
output_data = Magick::ImageList.new
|
68
|
+
columns.each do |column|
|
69
|
+
col = Magick::ImageList.new
|
70
|
+
rows.each do |row|
|
71
|
+
begin
|
72
|
+
col.push(Magick::Image.read(tile(column,row).local_file_name).first)
|
73
|
+
rescue
|
74
|
+
debugger
|
75
|
+
end
|
76
|
+
end
|
77
|
+
output_data.push(col.append(true))
|
78
|
+
end
|
79
|
+
output_data.append(false).write(@output)
|
80
|
+
end
|
81
|
+
end
|
data/lib/tiler.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'simple_mercator_location'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'tiler/tile'
|
4
|
+
require 'tiler/tiles'
|
5
|
+
require 'RMagick'
|
6
|
+
require 'debugger'
|
7
|
+
|
8
|
+
|
9
|
+
class Tiler
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_accessor :zoom,
|
14
|
+
:source,
|
15
|
+
:output,
|
16
|
+
:start_x,
|
17
|
+
:start_y,
|
18
|
+
:end_x,
|
19
|
+
:end_y,
|
20
|
+
:start_lat,
|
21
|
+
:start_lon,
|
22
|
+
:end_lat,
|
23
|
+
:end_lon
|
24
|
+
|
25
|
+
|
26
|
+
def_delegator :tiles, :download
|
27
|
+
def_delegator :tiles, :stitch
|
28
|
+
def_delegator :tiles, :cleanup
|
29
|
+
|
30
|
+
|
31
|
+
def initialize(args = {})
|
32
|
+
set_rectangle(args)
|
33
|
+
args = defaults.merge(args)
|
34
|
+
|
35
|
+
self.zoom = args[:zoom]
|
36
|
+
self.source = args[:source]
|
37
|
+
self.output = args[:output]
|
38
|
+
end
|
39
|
+
|
40
|
+
def defaults
|
41
|
+
{ zoom: 1, source: "sattelite", start_x: 0, start_y: 0, end_x: 0, end_y: 0}
|
42
|
+
end
|
43
|
+
|
44
|
+
def tiles
|
45
|
+
@tiles ||= TilesCollection.new(zoom: zoom, source: source, start_x: start_x, start_y: start_y, end_x: end_x, end_y: end_y, output: output)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run
|
49
|
+
download
|
50
|
+
stitch
|
51
|
+
cleanup
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def set_rectangle(args)
|
57
|
+
if given_lat_lon?(args)
|
58
|
+
set_lat_lon(args)
|
59
|
+
set_x_y_from_lat_lon(args)
|
60
|
+
elsif given_x_y?(args)
|
61
|
+
set_x_y(args)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_x_y_from_lat_lon(args)
|
66
|
+
start = SimpleMercatorLocation.new(lat: args[:start_lat], lon: args[:start_lon], zoom: args[:zoom])
|
67
|
+
finish = SimpleMercatorLocation.new(lat: args[:end_lat], lon: args[:end_lon], zoom: args[:zoom])
|
68
|
+
self.start_x = start.to_tile.first
|
69
|
+
self.start_y = start.to_tile.last
|
70
|
+
self.end_x = finish.to_tile.first
|
71
|
+
self.end_y = finish.to_tile.last
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_x_y(args)
|
75
|
+
self.start_x = args[:start_x]
|
76
|
+
self.start_y = args[:start_y]
|
77
|
+
self.end_x = args[:end_x]
|
78
|
+
self.end_y = args[:end_y]
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_lat_lon(args)
|
82
|
+
self.start_lat = args[:start_lat]
|
83
|
+
self.start_lon = args[:start_lon]
|
84
|
+
self.end_lat = args[:end_lat]
|
85
|
+
self.end_lon = args[:end_lon]
|
86
|
+
end
|
87
|
+
|
88
|
+
def given_x_y?(args)
|
89
|
+
args.has_key?(:start_x) && args[:start_x].is_a?(Integer) &&
|
90
|
+
args.has_key?(:start_y) && args[:start_y].is_a?(Integer) &&
|
91
|
+
args.has_key?(:end_x) && args[:end_x].is_a?(Integer) &&
|
92
|
+
args.has_key?(:end_y) && args[:end_y].is_a?(Integer)
|
93
|
+
end
|
94
|
+
|
95
|
+
def given_lat_lon?(args)
|
96
|
+
args.has_key?(:start_lat) && args[:start_lat].is_a?(Float) &&
|
97
|
+
args.has_key?(:start_lon) && args[:start_lon].is_a?(Float) &&
|
98
|
+
args.has_key?(:end_lat) && args[:end_lat].is_a?(Float) &&
|
99
|
+
args.has_key?(:end_lon) && args[:end_lon].is_a?(Float)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Tiler do
|
4
|
+
|
5
|
+
|
6
|
+
describe :initialize do
|
7
|
+
context "called without args" do
|
8
|
+
it "should not throw an error" do
|
9
|
+
expect{Tiler.new}.to_not raise_error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "called with args" do
|
14
|
+
it "sets instance variables" do
|
15
|
+
t = Tiler.new(zoom: 4)
|
16
|
+
expect(t.zoom).to eql 4
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe :run do
|
22
|
+
let(:tiler) { Tiler.new }
|
23
|
+
|
24
|
+
before :each do
|
25
|
+
tiler.stub :download
|
26
|
+
tiler.stub :stitch
|
27
|
+
tiler.stub :cleanup
|
28
|
+
tiler.stub :create_collection
|
29
|
+
end
|
30
|
+
|
31
|
+
it "calls Tiler#download" do
|
32
|
+
expect(tiler).to receive(:download).exactly(1).times
|
33
|
+
tiler.run
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "awesome_print"
|
3
|
+
require "debugger"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.color_enabled = true
|
7
|
+
config.filter_run :focus => true
|
8
|
+
config.run_all_when_everything_filtered = true
|
9
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def timed(name)
|
13
|
+
start = Time.now
|
14
|
+
puts "\n[STARTED: #{name}]"
|
15
|
+
yield if block_given?
|
16
|
+
finish = Time.now
|
17
|
+
puts "[FINISHED: #{name} in #{(finish - start) * 1000} milliseconds]"
|
18
|
+
end
|
19
|
+
|
20
|
+
require "tiler"
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: google-maps-stitch-bin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Roman Lehnert
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-12-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.5'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.5'
|
30
|
+
description: ''
|
31
|
+
email: roman.lehnert@googlemail.com
|
32
|
+
executables:
|
33
|
+
- tiler
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/tiler/tile.rb
|
38
|
+
- lib/tiler/tiles.rb
|
39
|
+
- lib/tiler.rb
|
40
|
+
- spec/lib/tiler_spec.rb
|
41
|
+
- spec/spec_helper.rb
|
42
|
+
- bin/tiler
|
43
|
+
homepage: ''
|
44
|
+
licenses:
|
45
|
+
- MIT
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.8.25
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Get images from google maps
|
68
|
+
test_files:
|
69
|
+
- spec/lib/tiler_spec.rb
|
70
|
+
- spec/spec_helper.rb
|