mapsnatcher 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|