mapsnatcher 0.1.2 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -2
- data/VERSION +1 -1
- data/bin/mapsnatcher +100 -52
- data/mapsnatcher.gemspec +2 -1
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 569c978c8a3045d6d9b2203105cfaad3178d58dec9781094a86cd7b11b39d1d1
|
4
|
+
data.tar.gz: c587c7ef882865ca5ae803a3ed631286b70c4fa356a022a2b25f293c7d0c1e85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 290990c16ada0ff0482988e41b980decfa448e8f945ffea7e7e3fc741969d60ae2680cf5ccc01135f95fa12be7deab1579b7e0154cbde7e4fb02d419624feff0
|
7
|
+
data.tar.gz: a2eba1fa908f53eb2e9fe5238f09cfa87ac792d23e92b16808db75c8872728a46ea8e4a441b62ea24123872c8e676ea20a33cea51ae3ae7821e45f006a74815f
|
data/README.md
CHANGED
@@ -27,16 +27,27 @@ policies in `/etc/ImageMagick-6/policy.xml`.
|
|
27
27
|
|
28
28
|
Usage is as follows:
|
29
29
|
|
30
|
-
```mapsnatcher
|
30
|
+
```mapsnatcher http://mapwebsite.com/0/xxx/yyy.png```
|
31
31
|
|
32
32
|
Place `XXX` and `YYY` (case-insensitive) where the respective coordinate
|
33
33
|
numbers would be in the URL. You will then be prompted to enter the X and Y
|
34
34
|
boundaries that you would like to capture.
|
35
35
|
|
36
|
-
```mapsnatcher -b
|
36
|
+
```mapsnatcher -b http://mapwebsite.com/0/xxx/yyy.png```
|
37
37
|
|
38
38
|
If you are unsure of the boundaries and would like to capture the entire map,
|
39
39
|
add the `-b` argument and you will only need to enter one valid coordinate;
|
40
40
|
`mapsnatcher` will do the rest.
|
41
41
|
|
42
42
|
If you would like to save the tiles, add the `-s` argument.
|
43
|
+
|
44
|
+
Coordinates can be passed into `mapsnatcher` as arguments:
|
45
|
+
|
46
|
+
```mapsnatcher http://mapwebsite.com/0/xxx/yyy.png -x 200-300 -y 200-250```
|
47
|
+
```mapsnatcher http://mapwebsite.com/0/xxx/yyy.png -b -x 200 -y 200```
|
48
|
+
|
49
|
+
And the output file may be specified with `-o`:
|
50
|
+
|
51
|
+
```mapsnatcher http://mapwebsite.com/0/xxx/yyy.png -o map.jpg```
|
52
|
+
|
53
|
+
If coordinates and output are not specified, you will be prompted for them.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2
|
data/bin/mapsnatcher
CHANGED
@@ -1,14 +1,66 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require "optimist"
|
3
4
|
require "fileutils"
|
4
5
|
require "open-uri"
|
5
6
|
require "net/http"
|
7
|
+
require "yaml"
|
6
8
|
require "rmagick"
|
7
9
|
include Magick
|
8
10
|
|
9
11
|
TEMP_DIR = "./.ms.tmp"
|
12
|
+
COORDS_FILE = "#{TEMP_DIR}/coords"
|
10
13
|
TILES_DIR = "./tiles"
|
11
14
|
|
15
|
+
opts = Optimist::options do
|
16
|
+
version "mapsnatcher #{File.read(File.expand_path("../../VERSION", __FILE__)).strip}"
|
17
|
+
banner self.version
|
18
|
+
banner "Usage:"
|
19
|
+
banner " mapsnatcher [options] http://mapwebsite.com/0/xxx/yyy.png"
|
20
|
+
banner " Place XXX and YYY in place of where the respective coordinate numbers would be"
|
21
|
+
banner " in the URL. The first number is typically the zoom level, which is constant."
|
22
|
+
banner "\n Inputs may be given through the CLI, or with flags:"
|
23
|
+
banner " mapsnatcher <url> -x 0-500 -y 0-300"
|
24
|
+
banner " mapsnatcher -b <url> -x 200 -y 200"
|
25
|
+
banner "\nOptions:"
|
26
|
+
opt :bounds, "find outer boundaries of the map automatically"
|
27
|
+
opt :save, "save tiles to a directory ./tiles/"
|
28
|
+
opt :version, "display version number"
|
29
|
+
opt :help, "display this message"
|
30
|
+
banner "I/O:"
|
31
|
+
opt :x, "x-coordinate range, (or x-coordinate if using -b)", type: :string
|
32
|
+
opt :y, "y-coordinate range, (or y-coordinate if using -b)", type: :string
|
33
|
+
opt :output, "output file name & location", type: :string
|
34
|
+
educate_on_error
|
35
|
+
end
|
36
|
+
|
37
|
+
x_range = y_range = nil
|
38
|
+
xy_in = opts.values_at(:x, :y)
|
39
|
+
using_xy_in = opts[:x] or opts[:y] # or, if missing one it'll throw this:
|
40
|
+
if using_xy_in
|
41
|
+
if xy_in.map { |o| o =~ /\A\d+#{/-\d+/ unless opts[:bounds]}\z/ }.include? nil
|
42
|
+
puts "Error: Invalid coordinate input."
|
43
|
+
puts
|
44
|
+
Optimist::educate
|
45
|
+
end
|
46
|
+
|
47
|
+
# Parse X/Y input into something we can use later
|
48
|
+
if opts[:bounds]
|
49
|
+
xy_in.map! &:to_i
|
50
|
+
else
|
51
|
+
x_range, y_range = xy_in.map { |r| Range.new *r.split("-").map(&:to_i) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Optimist::educate if ARGV.empty?
|
56
|
+
if ARGV.first !~ /\A#{URI::regexp(["http", "https"])}\z/
|
57
|
+
puts "Error: Invalid URL."
|
58
|
+
puts
|
59
|
+
Optimist::educate
|
60
|
+
end
|
61
|
+
|
62
|
+
url = ARGV.first
|
63
|
+
|
12
64
|
# False if URL returns a 200
|
13
65
|
#
|
14
66
|
# @param url [String] the url to be checked
|
@@ -27,42 +79,27 @@ def build_url(url, x, y)
|
|
27
79
|
url.gsub(/xxx/i, x.to_s).gsub(/yyy/i, y.to_s)
|
28
80
|
end
|
29
81
|
|
30
|
-
if ARGV.empty? or %w[-h --help].include? ARGV.first
|
31
|
-
puts 'Usage: mapsnatcher "http://mapwebsite.com/0/XXX/YYY.png"'
|
32
|
-
puts
|
33
|
-
puts " Place XXX and YYY in place of where the respective coordinate numbers would be"
|
34
|
-
puts " in the URL. The first number is typically the zoom level, which is constant."
|
35
|
-
puts
|
36
|
-
puts " [-b] find outer bounds of map automatically"
|
37
|
-
puts " [-s] save tiles to a directory ./tiles/"
|
38
|
-
exit 0
|
39
|
-
end
|
40
|
-
|
41
|
-
if %w[-v --version].include? ARGV.first
|
42
|
-
puts "mapsnatcher #{File.read(File.expand_path("../../VERSION", __FILE__)).strip}"
|
43
|
-
exit 0
|
44
|
-
end
|
45
|
-
|
46
82
|
# Load/create temp directory
|
47
|
-
|
83
|
+
using_saved_xy = false
|
84
|
+
if !File.exist? TEMP_DIR
|
48
85
|
Dir.mkdir TEMP_DIR
|
49
86
|
elsif !Dir.empty? TEMP_DIR
|
50
87
|
print "It looks like mapsnatcher didn't close properly. Use backed up files? [Y/n] "
|
51
|
-
|
88
|
+
if STDIN.gets.chomp =~ /n.*/i
|
89
|
+
FileUtils.rm_f(Dir.glob("#{TEMP_DIR}/*"))
|
90
|
+
elsif [x_range, y_range].include?(nil) and File.exist? COORDS_FILE
|
91
|
+
print "Use coordinate range from last time? [Y/n] "
|
92
|
+
if STDIN.gets.chomp !~ /n.*/i
|
93
|
+
x_range, y_range = YAML.load_file(COORDS_FILE, permitted_classes: [Range])
|
94
|
+
using_saved_xy = true
|
95
|
+
end
|
96
|
+
end
|
52
97
|
puts
|
53
98
|
end
|
54
99
|
|
55
|
-
|
56
|
-
if (args = ARGV).include? "-s"
|
57
|
-
args -= ["-s"]
|
58
|
-
save_tiles = true
|
59
|
-
end
|
60
|
-
|
61
|
-
if args.include? "-b"
|
100
|
+
if opts[:bounds]
|
62
101
|
# Automatic boundary mode
|
63
102
|
|
64
|
-
url = (args - ["-b"]).first
|
65
|
-
|
66
103
|
# CLI input function
|
67
104
|
#
|
68
105
|
# @return [Array] user input coordinates
|
@@ -72,47 +109,44 @@ if args.include? "-b"
|
|
72
109
|
print "Enter valid Y coordinate [0]: "
|
73
110
|
y_in = STDIN.gets.chomp.to_i
|
74
111
|
|
75
|
-
print "\nLoading
|
112
|
+
print "\nLoading..."
|
76
113
|
|
77
114
|
[x_in, y_in]
|
78
115
|
end
|
79
116
|
|
80
|
-
valid_x, valid_y = get_coords
|
117
|
+
valid_x, valid_y = using_xy_in ? xy_in : get_coords
|
81
118
|
|
119
|
+
print "\rLoading..."
|
82
120
|
while invalid? build_url(url, valid_x, valid_y)
|
83
|
-
puts "
|
121
|
+
puts "\rInvalid coordinate! Try again."
|
84
122
|
puts
|
85
123
|
valid_x, valid_y = get_coords
|
86
124
|
end
|
87
125
|
|
88
126
|
x_low = x_high = valid_x
|
89
127
|
until invalid? build_url(url, x_low - 1, valid_y)
|
90
|
-
print "
|
128
|
+
print "\r\e[KChecking X value: #{x_low}\r"
|
91
129
|
x_low -= 1
|
92
130
|
end
|
93
131
|
until invalid? build_url(url, x_high + 1, valid_y)
|
94
|
-
print "
|
132
|
+
print "\r\e[KChecking X value: #{x_high}\r"
|
95
133
|
x_high += 1
|
96
134
|
end
|
97
135
|
|
98
136
|
y_low = y_high = valid_y
|
99
137
|
until invalid? build_url(url, valid_x, y_low - 1)
|
100
|
-
print "
|
138
|
+
print "\r\e[KChecking Y value: #{y_low}\r"
|
101
139
|
y_low -= 1
|
102
140
|
end
|
103
141
|
until invalid? build_url(url, valid_x, y_high + 1)
|
104
|
-
print "
|
142
|
+
print "\r\e[KChecking Y value: #{y_high}\r"
|
105
143
|
y_high += 1
|
106
144
|
end
|
107
145
|
|
108
|
-
puts "Found Coordinate Range: [#{x_low}-#{x_high}], [#{y_low}-#{y_high}]"
|
109
|
-
|
110
146
|
x_range, y_range = (x_low..x_high), (y_low..y_high)
|
111
147
|
else
|
112
148
|
# User input coordinate mode
|
113
149
|
|
114
|
-
url = args.first
|
115
|
-
|
116
150
|
# CLI input function
|
117
151
|
#
|
118
152
|
# @return [Array] user input coordinate ranges
|
@@ -127,29 +161,35 @@ else
|
|
127
161
|
print "End of Y range: "
|
128
162
|
y_range_in = (y_start..STDIN.gets.chomp.to_i)
|
129
163
|
|
130
|
-
print "\nLoading
|
164
|
+
print "\nLoading..."
|
131
165
|
|
132
166
|
[x_range_in, y_range_in]
|
133
167
|
end
|
134
168
|
|
135
|
-
x_range, y_range = get_coords
|
169
|
+
x_range, y_range = get_coords unless using_xy_in
|
136
170
|
|
171
|
+
print "\rLoading..."
|
137
172
|
while invalid?(build_url url, x_range.first, y_range.first) or
|
138
173
|
invalid?(build_url url, x_range.first, y_range.last) or
|
139
174
|
invalid?(build_url url, x_range.last, y_range.first) or
|
140
175
|
invalid?(build_url url, x_range.last, y_range.last)
|
141
|
-
puts "\
|
176
|
+
puts "\rError: Boundaries incorrect."
|
142
177
|
puts "Use `mapsnatcher -b` for help finding the boundaries of the image."
|
143
178
|
puts
|
144
179
|
x_range, y_range = get_coords
|
145
180
|
end
|
146
181
|
end
|
147
182
|
|
183
|
+
puts "\rUsing #{'Saved ' if using_saved_xy}Coordinate Range: [#{x_range.first}-#{x_range.last}], [#{y_range.first}-#{y_range.last}]"
|
184
|
+
|
185
|
+
File.open(COORDS_FILE, "w") { |f| f.write [x_range, y_range].to_yaml }
|
186
|
+
|
148
187
|
# Use the tile at 0, 0 to calculate final image size
|
188
|
+
print "\rChecking dimensions..."
|
149
189
|
sample = Image.from_blob(URI.open(build_url url, x_range.first, y_range.first).read).first
|
150
190
|
final_size = {x: sample.columns * x_range.size, y: sample.rows * y_range.size}
|
151
191
|
format = sample.format
|
152
|
-
puts "
|
192
|
+
puts "\rImage found, #{format} size #{final_size[:x]}x#{final_size[:y]}"
|
153
193
|
puts
|
154
194
|
|
155
195
|
dl_counter = 0
|
@@ -158,11 +198,12 @@ dl_total = x_range.size * y_range.size
|
|
158
198
|
stitch = ImageList.new
|
159
199
|
y_range.each do |y|
|
160
200
|
row = ImageList.new
|
201
|
+
|
161
202
|
x_range.each do |x|
|
162
203
|
print "Downloading... [#{dl_counter += 1}/#{dl_total}]\r"
|
163
204
|
|
164
|
-
temp = "#{TEMP_DIR}/#{x}_#{y}.#{format.downcase}"
|
165
|
-
if File.
|
205
|
+
temp = "#{TEMP_DIR}/#{x}_#{y}.#{format == "JPEG" ? "jpg" : format.downcase}"
|
206
|
+
if File.exist? temp
|
166
207
|
# Load the backup file
|
167
208
|
img = Image.read(temp).first
|
168
209
|
else
|
@@ -177,6 +218,8 @@ y_range.each do |y|
|
|
177
218
|
stitch.push row.append(false)
|
178
219
|
end
|
179
220
|
|
221
|
+
thread = Thread.new { stitch = stitch.append(true) }
|
222
|
+
|
180
223
|
puts "\nDone!"
|
181
224
|
|
182
225
|
# Warnings for incompatible formats for large image sizes
|
@@ -193,15 +236,20 @@ elsif final_size.values.max > WEBP_MAX
|
|
193
236
|
puts
|
194
237
|
end
|
195
238
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
puts "\nImage written to: #{filename}"
|
239
|
+
unless filename = opts[:output]
|
240
|
+
print "Enter file name for map: "
|
241
|
+
filename = STDIN.gets.chomp
|
242
|
+
end
|
201
243
|
|
202
|
-
|
203
|
-
|
204
|
-
|
244
|
+
print "Saving, this may take a minute..."
|
245
|
+
thread.join # Wait for ImageMagick thread to finish joining rows
|
246
|
+
stitch.write filename
|
247
|
+
puts "\n\nImage written to: #{filename}"
|
248
|
+
|
249
|
+
if opts[:save]
|
250
|
+
FileUtils.rm COORDS_FILE
|
251
|
+
FileUtils.mv TEMP_DIR, TILES_DIR
|
252
|
+
puts "Tiles saved to: #{TILES_DIR.match(/\w+/)}/"
|
205
253
|
else
|
206
254
|
FileUtils.rm_rf TEMP_DIR
|
207
255
|
end
|
data/mapsnatcher.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
|
|
8
8
|
|
9
9
|
gem.license = "MIT"
|
10
10
|
|
11
|
-
gem.summary = "
|
11
|
+
gem.summary = "QGIS map downloading and tiling."
|
12
12
|
gem.description = "Downloads and combines tiles from QGIS maps into an image."
|
13
13
|
|
14
14
|
gem.bindir = "bin"
|
@@ -22,4 +22,5 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.required_ruby_version = "~> 3.0"
|
23
23
|
|
24
24
|
gem.add_dependency "rmagick", "~> 5.1"
|
25
|
+
gem.add_dependency "optimist", "~> 3.0"
|
25
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mapsnatcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vinny Diehl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rmagick
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: optimist
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
27
41
|
description: Downloads and combines tiles from QGIS maps into an image.
|
28
42
|
email: vinny.diehl@gmail.com
|
29
43
|
executables:
|
@@ -58,5 +72,5 @@ requirements: []
|
|
58
72
|
rubygems_version: 3.3.5
|
59
73
|
signing_key:
|
60
74
|
specification_version: 4
|
61
|
-
summary:
|
75
|
+
summary: QGIS map downloading and tiling.
|
62
76
|
test_files: []
|