mapsnatcher 0.1
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 +7 -0
- data/LICENSE +19 -0
- data/README.md +26 -0
- data/VERSION +1 -0
- data/bin/mapsnatcher +192 -0
- data/mapsnatcher.gemspec +25 -0
- 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}"
|
data/mapsnatcher.gemspec
ADDED
@@ -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: []
|