mapsnatcher 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +26 -0
  4. data/VERSION +1 -0
  5. data/bin/mapsnatcher +192 -0
  6. data/mapsnatcher.gemspec +25 -0
  7. metadata +62 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 81899844de71e7d8e994867d291b9bf3effee5eac3af6a07686c9b9ee201660d
4
+ data.tar.gz: 98a6fc9902a46b0a8afc38df9471c805a0a0555066cb516d357a1ad68da76db2
5
+ SHA512:
6
+ metadata.gz: de0a27be646d2d197f4342c8525a5e5625074eb2d6e47c412e7d9e46dbfc50e795d877b2b621148658784caa60a47c76929cbcc9f4d4442029376ba85818f143
7
+ data.tar.gz: b72ac47c23bd90ffeb8744af6ba569142e85a967dd55822bc07cc1130815be07894fce94b94f24aa65fbf0d8d32a3e554adbf3975c20946154c139528a033b9e
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Vinny Diehl
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # `mapsnatcher`
2
+
3
+ `mapsnatcher` is a bot to automatically download and splice together tiles from
4
+ QGIS maps. This simple system uses a URL such as
5
+ `http://mapwebsite.com/z/x/y.png`, where `z` is the zoom level, and `x` and `y`
6
+ are the coordinates of the tile.
7
+
8
+ You can use a tool such as
9
+ [ChromeCacheView](https://www.nirsoft.net/utils/chrome_cache_view.html) to find
10
+ these URLs easily.
11
+
12
+ ## Usage
13
+
14
+ Usage is as follows:
15
+
16
+ ```mapsnatcher "http://mapwebsite.com/0/XXX/YYY.png"```
17
+
18
+ Place `XXX` and `YYY` (case-insensitive) where the respective coordinate
19
+ numbers would be in the URL. You will then be prompted to enter the X and Y
20
+ boundaries that you would like to capture.
21
+
22
+ ```mapsnatcher -b "http://mapwebsite.com/0/XXX/YYY.png"```
23
+
24
+ If you are unsure of the boundaries and would like to capture the entire map,
25
+ add the `-b` argument and you will only need to enter one valid coordinate;
26
+ `mapsnatcher` will do the rest.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1
data/bin/mapsnatcher ADDED
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "fileutils"
4
+ require "yaml"
5
+ require "open-uri"
6
+ require "net/http"
7
+ require "rmagick"
8
+ include Magick
9
+
10
+ TEMP_DIR = "./.ms.tmp"
11
+
12
+ # False if URL returns a 200
13
+ #
14
+ # @param url [String] the url to be checked
15
+ # @return [Boolean] whether or not the URL is invalid
16
+ def invalid?(url)
17
+ uri = URI(url)
18
+ !Net::HTTP.start(uri.host, 443, use_ssl: true){ |http| break http.head uri.path }.instance_of? Net::HTTPOK
19
+ end
20
+
21
+ # Parses ARGV input into usable URL with given X and Y values
22
+ #
23
+ # @param x [Integer] x-coordinate
24
+ # @param y [Integer] y-coordinate
25
+ # @return usable URL for map tile
26
+ def build_url(url, x, y)
27
+ url.gsub(/xxx/i, x.to_s).gsub(/yyy/i, y.to_s)
28
+ end
29
+
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
+ exit 0
38
+ end
39
+
40
+ if %w[-v --version].include? ARGV.first
41
+ puts "mapsnatcher #{File.read(File.expand_path("../../VERSION", __FILE__)).strip}"
42
+ exit 0
43
+ end
44
+
45
+ # Load/create temp directory
46
+ if !File.exists? TEMP_DIR
47
+ Dir.mkdir TEMP_DIR
48
+ elsif !Dir.empty? TEMP_DIR
49
+ print "It looks like mapsnatcher didn't close properly. Use backed up files? [Y/n] "
50
+ FileUtils.rm_f(Dir.glob("#{TEMP_DIR}/*")) if STDIN.gets.chomp =~ /n.*/i
51
+ puts
52
+ end
53
+
54
+ if ARGV.include? "-b"
55
+ # Automatic boundary mode
56
+
57
+ url = (ARGV - ["-b"]).first
58
+
59
+ # CLI input function
60
+ #
61
+ # @return [Array] user input coordinates
62
+ def get_coords
63
+ print "Enter valid X coordinate [0]: "
64
+ x_in = STDIN.gets.chomp.to_i
65
+ print "Enter valid Y coordinate [0]: "
66
+ y_in = STDIN.gets.chomp.to_i
67
+
68
+ print "\nLoading...\r"
69
+
70
+ [x_in, y_in]
71
+ end
72
+
73
+ valid_x, valid_y = get_coords
74
+
75
+ while invalid? build_url(url, valid_x, valid_y)
76
+ puts "Invalid coordinate! Try again."
77
+ puts
78
+ valid_x, valid_y = get_coords
79
+ end
80
+
81
+ x_low = x_high = valid_x
82
+ until invalid? build_url(url, x_low - 1, valid_y)
83
+ print "Checking X value: #{x_low} \r"
84
+ x_low -= 1
85
+ end
86
+ until invalid? build_url(url, x_high + 1, valid_y)
87
+ print "Checking X value: #{x_high} \r"
88
+ x_high += 1
89
+ end
90
+
91
+ y_low = y_high = valid_y
92
+ until invalid? build_url(url, valid_x, y_low - 1)
93
+ print "Checking Y value: #{y_low} \r"
94
+ y_low -= 1
95
+ end
96
+ until invalid? build_url(url, valid_x, y_high + 1)
97
+ print "Checking Y value: #{y_high} \r"
98
+ y_high += 1
99
+ end
100
+
101
+ puts "Found Coordinate Range: [#{x_low}-#{x_high}], [#{y_low}-#{y_high}]"
102
+
103
+ x_range, y_range = (x_low..x_high), (y_low..y_high)
104
+ else
105
+ # User input coordinate mode
106
+
107
+ url = ARGV.first
108
+
109
+ # CLI input function
110
+ #
111
+ # @return [Array] user input coordinate ranges
112
+ def get_coords
113
+ print "Start of X range [0]: "
114
+ x_start = STDIN.gets.chomp.to_i
115
+ print "End of X range: "
116
+ x_range_in = (x_start..STDIN.gets.chomp.to_i)
117
+
118
+ print "Start of Y range [0]: "
119
+ y_start = STDIN.gets.chomp.to_i
120
+ print "End of Y range: "
121
+ y_range_in = (y_start..STDIN.gets.chomp.to_i)
122
+
123
+ print "\nLoading...\r"
124
+
125
+ [x_range_in, y_range_in]
126
+ end
127
+
128
+ x_range, y_range = get_coords
129
+
130
+ while invalid?(build_url url, x_range.first, y_range.first) or
131
+ invalid?(build_url url, x_range.first, y_range.last) or
132
+ invalid?(build_url url, x_range.last, y_range.first) or
133
+ invalid?(build_url url, x_range.last, y_range.last)
134
+ puts "\nError: Boundaries incorrect."
135
+ puts "Use `mapsnatcher -b` for help finding the boundaries of the image."
136
+ puts
137
+ x_range, y_range = get_coords
138
+ end
139
+ end
140
+
141
+ # Use the tile at 0, 0 to calculate final image size
142
+ sample = Image.from_blob(URI.open(build_url url, x_range.first, y_range.first).read).first
143
+ final_size = {x: sample.columns * x_range.size, y: sample.rows * y_range.size}
144
+ puts "Image found, #{sample.format} size #{final_size[:x]}x#{final_size[:y]}"
145
+ puts
146
+
147
+ dl_counter = 0
148
+ dl_total = x_range.size * y_range.size
149
+
150
+ stitch = ImageList.new
151
+ y_range.each do |y|
152
+ row = ImageList.new
153
+ x_range.each do |x|
154
+ print "Downloading... [#{dl_counter += 1}/#{dl_total}]\r"
155
+
156
+ temp = "#{TEMP_DIR}/#{x}_#{y}.tmp"
157
+ if File.exists? temp
158
+ # Load the backup file
159
+ blob = YAML.load(File.read temp)
160
+ else
161
+ blob = URI.open(build_url url, x, y).read
162
+ File.open(temp, "w") { |f| f.write(YAML.dump blob) }
163
+ end
164
+
165
+ row.push Image.from_blob(blob).first
166
+ end
167
+
168
+ stitch.push row.append(false)
169
+ end
170
+
171
+ puts "\nDone!"
172
+
173
+ # Warnings for incompatible formats for large image sizes
174
+ JPEG_MAX = 65535
175
+ WEBP_MAX = 16383
176
+
177
+ if final_size.values.max > JPEG_MAX
178
+ puts "\nWarning! Image dimensions greater than #{JPEG_MAX}px, JPG and WEBP"
179
+ puts "formats are unavailable."
180
+ puts
181
+ elsif final_size.values.max > WEBP_MAX
182
+ puts "\nWarning! Image dimensions greater than #{WEBP_MAX}px, WEBP"
183
+ puts "format is unavailable."
184
+ puts
185
+ end
186
+
187
+ print "Enter file name for map: "
188
+ filename = STDIN.gets.chomp
189
+ print "Saving, this may take a minute..."
190
+ stitch.append(true).write filename
191
+ FileUtils.rm_rf TEMP_DIR
192
+ puts "\nImage written to: #{filename}"
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "mapsnatcher"
3
+ gem.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
4
+
5
+ gem.author = "Vinny Diehl"
6
+ gem.email = "vinny.diehl@gmail.com"
7
+ gem.homepage = "https://github.com/vinnydiehl/mapsnatcher"
8
+
9
+ gem.license = "MIT"
10
+
11
+ gem.summary = "Downloads and combines tiles from QGIS maps into an image."
12
+ gem.description = "Downloads and combines tiles from QGIS maps into an image."
13
+
14
+ gem.bindir = "bin"
15
+ gem.executables = %w[mapsnatcher]
16
+
17
+ gem.test_files = Dir["spec/**/*"]
18
+ gem.files = Dir["bin/**/*"] + gem.test_files + %w[
19
+ LICENSE README.md VERSION mapsnatcher.gemspec
20
+ ]
21
+
22
+ gem.required_ruby_version = "~> 3.0"
23
+
24
+ gem.add_dependency "rmagick", "~> 5.1"
25
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mapsnatcher
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Vinny Diehl
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rmagick
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.1'
27
+ description: Downloads and combines tiles from QGIS maps into an image.
28
+ email: vinny.diehl@gmail.com
29
+ executables:
30
+ - mapsnatcher
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE
35
+ - README.md
36
+ - VERSION
37
+ - bin/mapsnatcher
38
+ - mapsnatcher.gemspec
39
+ homepage: https://github.com/vinnydiehl/mapsnatcher
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - "~>"
50
+ - !ruby/object:Gem::Version
51
+ version: '3.0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.3.5
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: Downloads and combines tiles from QGIS maps into an image.
62
+ test_files: []