mapsnatcher 0.1.2 → 0.2
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.
- 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: []
|