google-maps-stitch-bin 0.1.0
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.
- 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
|