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.
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: []