mapsnatcher 0.1.2 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -2
  3. data/VERSION +1 -1
  4. data/bin/mapsnatcher +100 -52
  5. data/mapsnatcher.gemspec +2 -1
  6. metadata +17 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15abc2e7d72ccee7fbc1d86065724bde5ac5eac4bd3312638e4dade03ea4bed2
4
- data.tar.gz: 9aaa5ef18519acaeae3c08f1149cab60ea39a86258b1307b2b5f3d109a191200
3
+ metadata.gz: 569c978c8a3045d6d9b2203105cfaad3178d58dec9781094a86cd7b11b39d1d1
4
+ data.tar.gz: c587c7ef882865ca5ae803a3ed631286b70c4fa356a022a2b25f293c7d0c1e85
5
5
  SHA512:
6
- metadata.gz: 54d9e64a64d4fbcd84e30cad8ebcb379fe93cc8062e02f2fb030f377c8f617bead2a5c781b49f3fe4cd8bd9287c872ecb30605099ab5e92abab01a0d1fa64176
7
- data.tar.gz: ca3d48e227460f0cbd8628f4b0a2f24268313dd2508452a4e0d266c2a4e0d937da22c069eee9da1f0c7f56b5302cc477d93c9db13de147501a4001d92c8f2090
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 "http://mapwebsite.com/0/XXX/YYY.png"```
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 "http://mapwebsite.com/0/XXX/YYY.png"```
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.2
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
- if !File.exists? TEMP_DIR
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
- FileUtils.rm_f(Dir.glob("#{TEMP_DIR}/*")) if STDIN.gets.chomp =~ /n.*/i
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
- save_tiles = false
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...\r"
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 "Invalid coordinate! Try again."
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 "Checking X value: #{x_low} \r"
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 "Checking X value: #{x_high} \r"
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 "Checking Y value: #{y_low} \r"
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 "Checking Y value: #{y_high} \r"
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...\r"
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 "\nError: Boundaries incorrect."
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 "Image found, #{format} size #{final_size[:x]}x#{final_size[:y]}"
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.exists? temp
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
- print "Enter file name for map: "
197
- filename = STDIN.gets.chomp
198
- print "Saving, this may take a minute..."
199
- stitch.append(true).write filename
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
- if save_tiles
203
- File.rename TEMP_DIR, TILES_DIR
204
- puts "Tiles saved to: #{TILES_DIR.match /\w+/}/"
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 = "Downloads and combines tiles from QGIS maps into an image."
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.1.2
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-28 00:00:00.000000000 Z
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: Downloads and combines tiles from QGIS maps into an image.
75
+ summary: QGIS map downloading and tiling.
62
76
  test_files: []