nswtopo 2.0.0.pre.beta1 → 3.0
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 +4 -4
- data/COPYING +70 -83
- data/bin/nswtopo +227 -116
- data/docs/README.md +1 -12
- data/docs/add.md +1 -1
- data/docs/config.md +1 -1
- data/docs/contours.md +3 -1
- data/docs/init.md +8 -0
- data/docs/inspect.md +103 -0
- data/docs/move.md +9 -0
- data/docs/render.md +16 -7
- data/docs/scrape.md +67 -0
- data/docs/spot-heights.md +6 -2
- data/lib/nswtopo/archive.rb +50 -41
- data/lib/nswtopo/chrome.rb +227 -0
- data/lib/nswtopo/commands/add.rb +106 -0
- data/lib/nswtopo/commands/config.rb +38 -0
- data/lib/nswtopo/commands/inspect.rb +74 -0
- data/lib/nswtopo/commands/layers.rb +22 -0
- data/lib/nswtopo/commands/scrape.rb +79 -0
- data/lib/nswtopo/commands.rb +57 -0
- data/lib/nswtopo/dither.rb +5 -3
- data/lib/nswtopo/font.rb +46 -21
- data/lib/nswtopo/formats/gemf.rb +42 -0
- data/lib/nswtopo/formats/kmz.rb +26 -24
- data/lib/nswtopo/formats/mbtiles.rb +5 -41
- data/lib/nswtopo/formats/pdf.rb +82 -17
- data/lib/nswtopo/formats/svg.rb +114 -45
- data/lib/nswtopo/formats/svgz.rb +2 -2
- data/lib/nswtopo/formats/zip.rb +33 -23
- data/lib/nswtopo/formats.rb +77 -32
- data/lib/nswtopo/geometry/overlap.rb +1 -32
- data/lib/nswtopo/geometry/r_tree.rb +16 -10
- data/lib/nswtopo/geometry/segment.rb +3 -3
- data/lib/nswtopo/geometry/straight_skeleton/nodes.rb +5 -6
- data/lib/nswtopo/geometry/vector_sequence.rb +7 -6
- data/lib/nswtopo/gis/arcgis/connection.rb +56 -0
- data/lib/nswtopo/gis/arcgis/layer/map.rb +163 -0
- data/lib/nswtopo/gis/arcgis/layer/query.rb +87 -0
- data/lib/nswtopo/gis/arcgis/layer/renderer.rb +66 -0
- data/lib/nswtopo/gis/arcgis/layer/statistics.rb +15 -0
- data/lib/nswtopo/gis/arcgis/layer.rb +201 -0
- data/lib/nswtopo/gis/arcgis/service.rb +57 -0
- data/lib/nswtopo/gis/arcgis.rb +3 -0
- data/lib/nswtopo/gis/dem.rb +13 -12
- data/lib/nswtopo/gis/esri_hdr.rb +8 -2
- data/lib/nswtopo/gis/geojson/collection.rb +45 -21
- data/lib/nswtopo/gis/geojson/multi_line_string.rb +2 -24
- data/lib/nswtopo/gis/geojson/multi_polygon.rb +2 -53
- data/lib/nswtopo/gis/geojson/polygon.rb +15 -0
- data/lib/nswtopo/gis/geojson.rb +12 -3
- data/lib/nswtopo/gis/gps/kml.rb +25 -19
- data/lib/nswtopo/gis/gps.rb +2 -0
- data/lib/nswtopo/gis/projection.rb +35 -24
- data/lib/nswtopo/gis/shapefile.rb +89 -16
- data/lib/nswtopo/gis.rb +1 -2
- data/lib/nswtopo/helpers/array.rb +0 -11
- data/lib/nswtopo/helpers/colour.rb +34 -14
- data/lib/nswtopo/layer/arcgis_raster.rb +44 -48
- data/lib/nswtopo/layer/colour_mask.rb +5 -0
- data/lib/nswtopo/layer/contour.rb +35 -28
- data/lib/nswtopo/layer/control.rb +2 -7
- data/lib/nswtopo/layer/declination.rb +9 -9
- data/lib/nswtopo/layer/feature.rb +36 -22
- data/lib/nswtopo/layer/grid.rb +30 -27
- data/lib/nswtopo/layer/import.rb +1 -21
- data/lib/nswtopo/layer/labels/barrier.rb +39 -0
- data/lib/nswtopo/layer/labels.rb +551 -383
- data/lib/nswtopo/layer/mask_render.rb +37 -0
- data/lib/nswtopo/layer/overlay.rb +2 -2
- data/lib/nswtopo/layer/raster.rb +31 -41
- data/lib/nswtopo/layer/raster_import.rb +17 -0
- data/lib/nswtopo/layer/raster_render.rb +15 -0
- data/lib/nswtopo/layer/relief.rb +27 -95
- data/lib/nswtopo/layer/spot.rb +63 -62
- data/lib/nswtopo/layer/vector/cutout.rb +15 -0
- data/lib/nswtopo/layer/vector/knockout.rb +16 -0
- data/lib/nswtopo/layer/vector.rb +121 -89
- data/lib/nswtopo/layer/vegetation.rb +39 -34
- data/lib/nswtopo/layer.rb +30 -16
- data/lib/nswtopo/map.rb +202 -109
- data/lib/nswtopo/os.rb +5 -27
- data/lib/nswtopo/tiled_web_map.rb +54 -0
- data/lib/nswtopo/tree_indenter.rb +27 -0
- data/lib/nswtopo/version.rb +27 -2
- data/lib/nswtopo.rb +6 -199
- metadata +41 -22
- data/lib/nswtopo/font/chrome.rb +0 -59
- data/lib/nswtopo/font/generic.rb +0 -25
- data/lib/nswtopo/gis/arcgis_server/connection.rb +0 -52
- data/lib/nswtopo/gis/arcgis_server.rb +0 -155
- data/lib/nswtopo/gis/geojson/multi_point.rb +0 -12
- data/lib/nswtopo/gis/world_file.rb +0 -19
- data/lib/nswtopo/layer/labels/fence.rb +0 -20
data/lib/nswtopo.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'date'
|
|
2
|
+
require 'time'
|
|
2
3
|
require 'open3'
|
|
3
4
|
require 'uri'
|
|
4
5
|
require 'net/http'
|
|
@@ -17,11 +18,7 @@ require 'ostruct'
|
|
|
17
18
|
require 'forwardable'
|
|
18
19
|
require 'rubygems/package'
|
|
19
20
|
require 'zlib'
|
|
20
|
-
|
|
21
|
-
require 'pty'
|
|
22
|
-
require 'expect'
|
|
23
|
-
rescue LoadError
|
|
24
|
-
end
|
|
21
|
+
require 'io/nonblock'
|
|
25
22
|
|
|
26
23
|
require_relative 'nswtopo/helpers'
|
|
27
24
|
require_relative 'nswtopo/avl_tree'
|
|
@@ -31,216 +28,26 @@ require_relative 'nswtopo/safely'
|
|
|
31
28
|
require_relative 'nswtopo/os'
|
|
32
29
|
require_relative 'nswtopo/dither'
|
|
33
30
|
require_relative 'nswtopo/zip'
|
|
31
|
+
require_relative 'nswtopo/chrome'
|
|
34
32
|
require_relative 'nswtopo/font'
|
|
35
33
|
require_relative 'nswtopo/archive'
|
|
36
34
|
require_relative 'nswtopo/gis'
|
|
35
|
+
require_relative 'nswtopo/tiled_web_map'
|
|
37
36
|
require_relative 'nswtopo/formats'
|
|
38
37
|
require_relative 'nswtopo/map'
|
|
39
38
|
require_relative 'nswtopo/layer'
|
|
40
39
|
require_relative 'nswtopo/version'
|
|
41
40
|
require_relative 'nswtopo/config'
|
|
41
|
+
require_relative 'nswtopo/commands'
|
|
42
|
+
require_relative 'nswtopo/tree_indenter'
|
|
42
43
|
|
|
43
44
|
module NSWTopo
|
|
44
45
|
PartialFailureError = Class.new RuntimeError
|
|
45
46
|
extend self, Log
|
|
46
47
|
|
|
47
|
-
def init(archive, options)
|
|
48
|
-
puts Map.init(archive, options)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
48
|
def layer_dirs
|
|
52
49
|
@layer_dirs ||= Array(Config["layer-dir"]).map(&Pathname.method(:new)) << Pathname.pwd
|
|
53
50
|
end
|
|
54
|
-
|
|
55
|
-
def info(archive, options)
|
|
56
|
-
puts Map.load(archive).info(options)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def add(archive, *layers, options)
|
|
60
|
-
create_options = {
|
|
61
|
-
after: Layer.sanitise(options.delete :after),
|
|
62
|
-
before: Layer.sanitise(options.delete :before),
|
|
63
|
-
replace: Layer.sanitise(options.delete :replace),
|
|
64
|
-
overwrite: options.delete(:overwrite)
|
|
65
|
-
}
|
|
66
|
-
map = Map.load archive
|
|
67
|
-
|
|
68
|
-
Enumerator.new do |yielder|
|
|
69
|
-
while layers.any?
|
|
70
|
-
layer, basedir = layers.shift
|
|
71
|
-
path = Pathname(layer).expand_path(*basedir)
|
|
72
|
-
case layer
|
|
73
|
-
when /^controls\.(gpx|kml)$/i
|
|
74
|
-
yielder << [path.basename(path.extname).to_s, "type" => "Control", "path" => path]
|
|
75
|
-
when /\.(gpx|kml)$/i
|
|
76
|
-
yielder << [path.basename(path.extname).to_s, "type" => "Overlay", "path" => path]
|
|
77
|
-
when /\.(tiff?|png|jpg)$/i
|
|
78
|
-
yielder << [path.basename(path.extname).to_s, "type" => "Import", "path" => path]
|
|
79
|
-
when "contours"
|
|
80
|
-
yielder << [layer, "type" => "Contour"]
|
|
81
|
-
when "spot-heights"
|
|
82
|
-
yielder << [layer, "type" => "Spot"]
|
|
83
|
-
when "relief"
|
|
84
|
-
yielder << [layer, "type" => "Relief"]
|
|
85
|
-
when "grid"
|
|
86
|
-
yielder << [layer, "type" => "Grid"]
|
|
87
|
-
when "declination"
|
|
88
|
-
yielder << [layer, "type" => "Declination"]
|
|
89
|
-
when "controls"
|
|
90
|
-
yielder << [layer, "type" => "Control"]
|
|
91
|
-
when /\.yml$/i
|
|
92
|
-
basedir ||= path.parent
|
|
93
|
-
raise "couldn't find '#{layer}'" unless path.file?
|
|
94
|
-
case contents = YAML.load(path.read)
|
|
95
|
-
when Array
|
|
96
|
-
contents.reverse.map do |item|
|
|
97
|
-
Pathname(item.to_s)
|
|
98
|
-
end.each do |relative_path|
|
|
99
|
-
raise "#{relative_path} is not a relative path" unless relative_path.relative?
|
|
100
|
-
layers.prepend [Pathname(relative_path).expand_path(path.parent).relative_path_from(basedir).to_s, basedir]
|
|
101
|
-
end
|
|
102
|
-
when Hash
|
|
103
|
-
name = path.sub_ext("").relative_path_from(basedir).descend.map(&:basename).join(?.)
|
|
104
|
-
yielder << [name, contents.merge("source" => path)]
|
|
105
|
-
else
|
|
106
|
-
raise "couldn't parse #{path}"
|
|
107
|
-
end
|
|
108
|
-
else
|
|
109
|
-
path = Pathname("#{layer}.yml")
|
|
110
|
-
raise "#{layer} is not a relative path" unless path.relative?
|
|
111
|
-
basedir ||= layer_dirs.find do |root|
|
|
112
|
-
path.expand_path(root).file?
|
|
113
|
-
end
|
|
114
|
-
layers.prepend [path.to_s, basedir]
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
rescue YAML::Exception
|
|
118
|
-
raise "couldn't parse #{path}"
|
|
119
|
-
end.map do |name, params|
|
|
120
|
-
params.merge! options.transform_keys(&:to_s)
|
|
121
|
-
params.merge! Config[name] if Config[name]
|
|
122
|
-
Layer.new(name, map, params)
|
|
123
|
-
end.tap do |layers|
|
|
124
|
-
raise OptionParser::MissingArgument, "no layers specified" unless layers.any?
|
|
125
|
-
unless layers.one?
|
|
126
|
-
raise OptionParser::InvalidArgument, "can't specify resolution when adding multiple layers" if options[:resolution]
|
|
127
|
-
raise OptionParser::InvalidArgument, "can't specify data path when adding multiple layers" if options[:path]
|
|
128
|
-
end
|
|
129
|
-
map.add *layers, create_options
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def contours(archive, dem_path, options)
|
|
134
|
-
add archive, "contours", options.merge(path: Pathname(dem_path))
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def spot_heights(archive, dem_path, options)
|
|
138
|
-
add archive, "spot-heights", options.merge(path: Pathname(dem_path))
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def relief(archive, dem_path, options)
|
|
142
|
-
add archive, "relief", options.merge(path: Pathname(dem_path))
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def grid(archive, options)
|
|
146
|
-
add archive, "grid", options
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def declination(archive, options)
|
|
150
|
-
add archive, "declination", options
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def controls(archive, gps_path, options)
|
|
154
|
-
add archive, "controls", options.merge(path: Pathname(gps_path))
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def overlay(archive, gps_path, options)
|
|
158
|
-
raise OptionParser::InvalidArgument, gps_path unless gps_path =~ /\.(gpx|kml)$/i
|
|
159
|
-
add archive, gps_path, options.merge(path: Pathname(gps_path))
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def delete(archive, *names, options)
|
|
163
|
-
map = Map.load archive
|
|
164
|
-
names.map do |name|
|
|
165
|
-
Layer.sanitise name
|
|
166
|
-
end.uniq.map do |name|
|
|
167
|
-
name[?*] ? %r[^#{name.gsub(?., '\.').gsub(?*, '.*')}$] : name
|
|
168
|
-
end.tap do |names|
|
|
169
|
-
map.delete *names
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def render(archive, *formats, options)
|
|
174
|
-
overwrite = options.delete :overwrite
|
|
175
|
-
formats << "svg" if formats.empty?
|
|
176
|
-
formats.map do |format|
|
|
177
|
-
Pathname(Formats === format ? "#{archive.basename}.#{format}" : format)
|
|
178
|
-
end.uniq.each do |path|
|
|
179
|
-
format = path.extname.delete_prefix(?.)
|
|
180
|
-
raise "unrecognised format: #{path}" if format.empty?
|
|
181
|
-
raise "unrecognised format: #{format}" unless Formats === format
|
|
182
|
-
raise "file already exists: #{path}" if path.exist? && !overwrite
|
|
183
|
-
raise "non-existent directory: #{path.parent}" unless path.parent.directory?
|
|
184
|
-
end.tap do |paths|
|
|
185
|
-
Map.load(archive).render *paths, options
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def layers(state: nil, indent: "")
|
|
190
|
-
layer_dirs.grep_v(Pathname.pwd).flat_map do |directory|
|
|
191
|
-
Array(state).inject(directory, &:/).glob("*")
|
|
192
|
-
end.sort.each do |path|
|
|
193
|
-
case
|
|
194
|
-
when path.directory?
|
|
195
|
-
next if path.glob("**/*.yml").none?
|
|
196
|
-
puts [indent, path.basename.sub_ext("")].join
|
|
197
|
-
layers state: [*state, path.basename], indent: " " + indent
|
|
198
|
-
when path.sub_ext("").directory?
|
|
199
|
-
when path.extname == ".yml"
|
|
200
|
-
puts [indent, path.basename.sub_ext("")].join
|
|
201
|
-
end
|
|
202
|
-
end.tap do |paths|
|
|
203
|
-
log_warn "no layers installed" if paths.none?
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def config(layer = nil, **options)
|
|
208
|
-
chrome, firefox, path, resolution, layer_dir, labelling, delete = options.values_at :chrome, :firefox, :path, :resolution, :"layer-dir", :labelling, :delete
|
|
209
|
-
raise "not a directory: %s" % layer_dir if layer_dir && !layer_dir.directory?
|
|
210
|
-
raise "chrome path is not an executable" if chrome && !chrome.executable?
|
|
211
|
-
raise "firefox path is not an executable" if firefox && !firefox.executable?
|
|
212
|
-
Config.store("chrome", chrome.to_s) if chrome
|
|
213
|
-
Config.store("firefox", firefox.to_s) if firefox
|
|
214
|
-
Config.store("labelling", labelling) unless labelling.nil?
|
|
215
|
-
Config.store("layer-dir", layer_dir.to_s) if layer_dir
|
|
216
|
-
|
|
217
|
-
layer = Layer.sanitise layer
|
|
218
|
-
case
|
|
219
|
-
when !layer
|
|
220
|
-
raise OptionParser::InvalidArgument, "no layer name specified for path" if path
|
|
221
|
-
raise OptionParser::InvalidArgument, "no layer name specified for resolution" if resolution
|
|
222
|
-
when path || resolution
|
|
223
|
-
Config.store(layer, "path", path.to_s) if path
|
|
224
|
-
Config.store(layer, "resolution", resolution) if resolution
|
|
225
|
-
end
|
|
226
|
-
Config.delete(*layer, delete) if delete
|
|
227
|
-
|
|
228
|
-
if options.empty?
|
|
229
|
-
puts Config.to_str.each_line.drop(1)
|
|
230
|
-
log_neutral "no configuration yet" if Config.empty?
|
|
231
|
-
else
|
|
232
|
-
Config.save
|
|
233
|
-
log_success "configuration updated"
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def with_browser
|
|
238
|
-
browser_name, browser_path = Config.slice("chrome", "firefox").first
|
|
239
|
-
raise "please configure a path for google chrome" unless browser_name
|
|
240
|
-
yield browser_name, Pathname.new(browser_path)
|
|
241
|
-
rescue Errno::ENOENT
|
|
242
|
-
raise "invalid %s path: %s" % [browser_name, browser_path]
|
|
243
|
-
end
|
|
244
51
|
end
|
|
245
52
|
|
|
246
53
|
begin
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: nswtopo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: '3.0'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthew Hollingworth
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-08-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description:
|
|
14
|
-
email:
|
|
13
|
+
description:
|
|
14
|
+
email:
|
|
15
15
|
executables:
|
|
16
16
|
- nswtopo
|
|
17
17
|
extensions: []
|
|
@@ -29,20 +29,29 @@ files:
|
|
|
29
29
|
- docs/grid.md
|
|
30
30
|
- docs/info.md
|
|
31
31
|
- docs/init.md
|
|
32
|
+
- docs/inspect.md
|
|
32
33
|
- docs/layers.md
|
|
34
|
+
- docs/move.md
|
|
33
35
|
- docs/overlay.md
|
|
34
36
|
- docs/relief.md
|
|
35
37
|
- docs/render.md
|
|
38
|
+
- docs/scrape.md
|
|
36
39
|
- docs/spot-heights.md
|
|
37
40
|
- lib/nswtopo.rb
|
|
38
41
|
- lib/nswtopo/archive.rb
|
|
39
42
|
- lib/nswtopo/avl_tree.rb
|
|
43
|
+
- lib/nswtopo/chrome.rb
|
|
44
|
+
- lib/nswtopo/commands.rb
|
|
45
|
+
- lib/nswtopo/commands/add.rb
|
|
46
|
+
- lib/nswtopo/commands/config.rb
|
|
47
|
+
- lib/nswtopo/commands/inspect.rb
|
|
48
|
+
- lib/nswtopo/commands/layers.rb
|
|
49
|
+
- lib/nswtopo/commands/scrape.rb
|
|
40
50
|
- lib/nswtopo/config.rb
|
|
41
51
|
- lib/nswtopo/dither.rb
|
|
42
52
|
- lib/nswtopo/font.rb
|
|
43
|
-
- lib/nswtopo/font/chrome.rb
|
|
44
|
-
- lib/nswtopo/font/generic.rb
|
|
45
53
|
- lib/nswtopo/formats.rb
|
|
54
|
+
- lib/nswtopo/formats/gemf.rb
|
|
46
55
|
- lib/nswtopo/formats/kmz.rb
|
|
47
56
|
- lib/nswtopo/formats/mbtiles.rb
|
|
48
57
|
- lib/nswtopo/formats/pdf.rb
|
|
@@ -63,8 +72,14 @@ files:
|
|
|
63
72
|
- lib/nswtopo/geometry/vector.rb
|
|
64
73
|
- lib/nswtopo/geometry/vector_sequence.rb
|
|
65
74
|
- lib/nswtopo/gis.rb
|
|
66
|
-
- lib/nswtopo/gis/
|
|
67
|
-
- lib/nswtopo/gis/
|
|
75
|
+
- lib/nswtopo/gis/arcgis.rb
|
|
76
|
+
- lib/nswtopo/gis/arcgis/connection.rb
|
|
77
|
+
- lib/nswtopo/gis/arcgis/layer.rb
|
|
78
|
+
- lib/nswtopo/gis/arcgis/layer/map.rb
|
|
79
|
+
- lib/nswtopo/gis/arcgis/layer/query.rb
|
|
80
|
+
- lib/nswtopo/gis/arcgis/layer/renderer.rb
|
|
81
|
+
- lib/nswtopo/gis/arcgis/layer/statistics.rb
|
|
82
|
+
- lib/nswtopo/gis/arcgis/service.rb
|
|
68
83
|
- lib/nswtopo/gis/dem.rb
|
|
69
84
|
- lib/nswtopo/gis/esri_hdr.rb
|
|
70
85
|
- lib/nswtopo/gis/gdal_glob.rb
|
|
@@ -72,7 +87,6 @@ files:
|
|
|
72
87
|
- lib/nswtopo/gis/geojson/collection.rb
|
|
73
88
|
- lib/nswtopo/gis/geojson/line_string.rb
|
|
74
89
|
- lib/nswtopo/gis/geojson/multi_line_string.rb
|
|
75
|
-
- lib/nswtopo/gis/geojson/multi_point.rb
|
|
76
90
|
- lib/nswtopo/gis/geojson/multi_polygon.rb
|
|
77
91
|
- lib/nswtopo/gis/geojson/point.rb
|
|
78
92
|
- lib/nswtopo/gis/geojson/polygon.rb
|
|
@@ -81,7 +95,6 @@ files:
|
|
|
81
95
|
- lib/nswtopo/gis/gps/kml.rb
|
|
82
96
|
- lib/nswtopo/gis/projection.rb
|
|
83
97
|
- lib/nswtopo/gis/shapefile.rb
|
|
84
|
-
- lib/nswtopo/gis/world_file.rb
|
|
85
98
|
- lib/nswtopo/help_formatter.rb
|
|
86
99
|
- lib/nswtopo/helpers.rb
|
|
87
100
|
- lib/nswtopo/helpers/array.rb
|
|
@@ -92,6 +105,7 @@ files:
|
|
|
92
105
|
- lib/nswtopo/helpers/tar_writer.rb
|
|
93
106
|
- lib/nswtopo/layer.rb
|
|
94
107
|
- lib/nswtopo/layer/arcgis_raster.rb
|
|
108
|
+
- lib/nswtopo/layer/colour_mask.rb
|
|
95
109
|
- lib/nswtopo/layer/contour.rb
|
|
96
110
|
- lib/nswtopo/layer/control.rb
|
|
97
111
|
- lib/nswtopo/layer/declination.rb
|
|
@@ -99,24 +113,31 @@ files:
|
|
|
99
113
|
- lib/nswtopo/layer/grid.rb
|
|
100
114
|
- lib/nswtopo/layer/import.rb
|
|
101
115
|
- lib/nswtopo/layer/labels.rb
|
|
102
|
-
- lib/nswtopo/layer/labels/
|
|
116
|
+
- lib/nswtopo/layer/labels/barrier.rb
|
|
117
|
+
- lib/nswtopo/layer/mask_render.rb
|
|
103
118
|
- lib/nswtopo/layer/overlay.rb
|
|
104
119
|
- lib/nswtopo/layer/raster.rb
|
|
120
|
+
- lib/nswtopo/layer/raster_import.rb
|
|
121
|
+
- lib/nswtopo/layer/raster_render.rb
|
|
105
122
|
- lib/nswtopo/layer/relief.rb
|
|
106
123
|
- lib/nswtopo/layer/spot.rb
|
|
107
124
|
- lib/nswtopo/layer/vector.rb
|
|
125
|
+
- lib/nswtopo/layer/vector/cutout.rb
|
|
126
|
+
- lib/nswtopo/layer/vector/knockout.rb
|
|
108
127
|
- lib/nswtopo/layer/vegetation.rb
|
|
109
128
|
- lib/nswtopo/log.rb
|
|
110
129
|
- lib/nswtopo/map.rb
|
|
111
130
|
- lib/nswtopo/os.rb
|
|
112
131
|
- lib/nswtopo/safely.rb
|
|
132
|
+
- lib/nswtopo/tiled_web_map.rb
|
|
133
|
+
- lib/nswtopo/tree_indenter.rb
|
|
113
134
|
- lib/nswtopo/version.rb
|
|
114
135
|
- lib/nswtopo/zip.rb
|
|
115
136
|
homepage: https://github.com/mholling/nswtopo
|
|
116
137
|
licenses:
|
|
117
138
|
- GPL-3.0
|
|
118
139
|
metadata: {}
|
|
119
|
-
post_install_message:
|
|
140
|
+
post_install_message:
|
|
120
141
|
rdoc_options: []
|
|
121
142
|
require_paths:
|
|
122
143
|
- lib
|
|
@@ -124,19 +145,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
124
145
|
requirements:
|
|
125
146
|
- - ">="
|
|
126
147
|
- !ruby/object:Gem::Version
|
|
127
|
-
version:
|
|
148
|
+
version: 3.0.4
|
|
128
149
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
150
|
requirements:
|
|
130
|
-
- - "
|
|
151
|
+
- - ">="
|
|
131
152
|
- !ruby/object:Gem::Version
|
|
132
|
-
version:
|
|
153
|
+
version: '0'
|
|
133
154
|
requirements:
|
|
134
|
-
- GDAL >=
|
|
135
|
-
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
rubygems_version: 2.7.6
|
|
139
|
-
signing_key:
|
|
155
|
+
- GDAL >= v3.4
|
|
156
|
+
- Google Chrome >= v112
|
|
157
|
+
rubygems_version: 3.2.33
|
|
158
|
+
signing_key:
|
|
140
159
|
specification_version: 4
|
|
141
160
|
summary: A vector topographic mapping tool
|
|
142
161
|
test_files: []
|
data/lib/nswtopo/font/chrome.rb
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
module NSWTopo
|
|
2
|
-
module Font
|
|
3
|
-
module Chrome
|
|
4
|
-
ATTRIBUTES = %w[font-family font-variant font-style font-weight font-size letter-spacing word-spacing]
|
|
5
|
-
|
|
6
|
-
def command(string)
|
|
7
|
-
@input.puts string
|
|
8
|
-
lines, match = @output.expect(/(\{.*)\n/, 1)
|
|
9
|
-
response = JSON.parse match
|
|
10
|
-
raise "unexpected chrome error: %s" % response.dig("exceptionDetails", "exception", "description") if response["exceptionDetails"]
|
|
11
|
-
response.fetch("result").dig("value")
|
|
12
|
-
rescue TypeError, JSON::ParserError, KeyError
|
|
13
|
-
raise "unexpected chrome error"
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def start_chrome
|
|
17
|
-
chrome_path = Config["chrome"]
|
|
18
|
-
svg = <<~XML
|
|
19
|
-
<?xml version='1.0' encoding='UTF-8'?>
|
|
20
|
-
<svg version='1.1' baseProfile='full' xmlns='http://www.w3.org/2000/svg' width='1mm' height='1mm' viewBox='0 0 1 1'>
|
|
21
|
-
<rect id='mm' width='1' height='1' stroke='none' />
|
|
22
|
-
<text id='text' />
|
|
23
|
-
</svg>
|
|
24
|
-
XML
|
|
25
|
-
@output, @input, @pid = PTY.spawn chrome_path, "--headless", "--disable-gpu", "--repl", "data:image/svg+xml;base64,#{Base64.encode64 svg}"
|
|
26
|
-
ObjectSpace.define_finalizer self, Proc.new { @input.puts "quit" }
|
|
27
|
-
command %Q[text = document.getElementById("text")]
|
|
28
|
-
@mm = command %Q[document.getElementById("mm").getBoundingClientRect().width]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def self.extended(instance)
|
|
32
|
-
instance.start_chrome
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def validate(family)
|
|
36
|
-
return unless family
|
|
37
|
-
@families ||= Set[]
|
|
38
|
-
@families.add?(family) || return
|
|
39
|
-
command %Q[text.textContent="abcdefghijklmnopqrstuvwxyz"]
|
|
40
|
-
["font-family:#{family}", nil].map do |style|
|
|
41
|
-
command %Q[text.setAttribute("style", "#{style}")]
|
|
42
|
-
command %Q[text.getBoundingClientRect().width]
|
|
43
|
-
end.inject(&:==) || return
|
|
44
|
-
log_neutral "font '#{family}' doesn't appear to be available"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def glyph_length(string, attributes)
|
|
48
|
-
style = attributes.slice(*ATTRIBUTES).map do |pair|
|
|
49
|
-
pair.join ?:
|
|
50
|
-
end.join(?;)
|
|
51
|
-
style << ";white-space:pre" if ?\s == string
|
|
52
|
-
validate attributes["font-family"]
|
|
53
|
-
command %Q[text.setAttribute("style", #{style.inspect})]
|
|
54
|
-
command %Q[text.textContent=#{string.inspect}]
|
|
55
|
-
command(%Q[text.getBoundingClientRect().width]) / @mm
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
data/lib/nswtopo/font/generic.rb
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module NSWTopo
|
|
2
|
-
module Font
|
|
3
|
-
module Generic
|
|
4
|
-
WIDTHS = {
|
|
5
|
-
?A => 0.732, ?B => 0.678, ?C => 0.682, ?D => 0.740, ?E => 0.583, ?F => 0.558, ?G => 0.728, ?H => 0.761, ?I => 0.256, ?J => 0.331, ?K => 0.641, ?L => 0.542, ?M => 0.843,
|
|
6
|
-
?N => 0.740, ?O => 0.769, ?P => 0.649, ?Q => 0.769, ?R => 0.690, ?S => 0.620, ?T => 0.599, ?U => 0.728, ?V => 0.695, ?W => 1.108, ?X => 0.649, ?Y => 0.637, ?Z => 0.591,
|
|
7
|
-
?a => 0.595, ?b => 0.595, ?c => 0.492, ?d => 0.595, ?e => 0.542, ?f => 0.335, ?g => 0.599, ?h => 0.583, ?i => 0.236, ?j => 0.289, ?k => 0.521, ?l => 0.236, ?m => 0.876,
|
|
8
|
-
?n => 0.583, ?o => 0.571, ?p => 0.595, ?q => 0.595, ?r => 0.360, ?s => 0.492, ?t => 0.347, ?u => 0.575, ?v => 0.529, ?w => 0.864, ?x => 0.533, ?y => 0.529, ?z => 0.513,
|
|
9
|
-
?0 => 0.595, ?1 => 0.595, ?2 => 0.595, ?3 => 0.595, ?4 => 0.595, ?5 => 0.595, ?6 => 0.595, ?7 => 0.595, ?8 => 0.595, ?9 => 0.595, ?! => 0.227, ?" => 0.422, ?# => 0.604,
|
|
10
|
-
?$ => 0.595, ?% => 0.934, ?& => 0.678, ?' => 0.219, ?( => 0.314, ?) => 0.314, ?* => 0.451, ?+ => 0.595, ?, => 0.227, ?- => 0.426, ?. => 0.227, ?/ => 0.331, ?\\ => 0.327,
|
|
11
|
-
?[ => 0.314, ?] => 0.314, ?^ => 0.595, ?_ => 0.500, ?` => 0.310, ?: => 0.227, ?; => 0.227, ?< => 0.595, ?= => 0.595, ?> => 0.595, ?? => 0.442, ?@ => 0.930, ?\s => 0.265,
|
|
12
|
-
}
|
|
13
|
-
WIDTHS.default = WIDTHS[?M]
|
|
14
|
-
|
|
15
|
-
def glyph_length(string, attributes)
|
|
16
|
-
font_size, letter_spacing, word_spacing = attributes.values_at("font-size", "letter-spacing", "word-spacing").map(&:to_f)
|
|
17
|
-
string.chars.each_cons(2).inject(WIDTHS[string[0]] * font_size) do |sum, pair|
|
|
18
|
-
next sum + WIDTHS[pair[1]] * font_size + letter_spacing unless pair[0] == ?\s
|
|
19
|
-
next sum + WIDTHS[pair[1]] * font_size + letter_spacing + word_spacing unless pair[1] == ?\s
|
|
20
|
-
sum
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
module NSWTopo
|
|
2
|
-
module ArcGISServer
|
|
3
|
-
class Connection
|
|
4
|
-
def initialize(http, service_path)
|
|
5
|
-
@http, @service_path, @headers = http, service_path, { "User-Agent" => "Ruby/#{RUBY_VERSION}", "Referer" => "%s://%s" % [http.use_ssl? ? "https" : "http", http.address] }
|
|
6
|
-
http.max_retries = 0
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def repeatedly_request(request)
|
|
10
|
-
intervals ||= 5.times.map(&1.4142.method(:**))
|
|
11
|
-
response = @http.request(request)
|
|
12
|
-
response.error! unless Net::HTTPSuccess === response
|
|
13
|
-
yield response
|
|
14
|
-
rescue *ERRORS, Error => error
|
|
15
|
-
interval = intervals.shift
|
|
16
|
-
interval ? sleep(interval) : raise(error)
|
|
17
|
-
retry
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def get(relative_path, **query, &block)
|
|
21
|
-
path = Pathname(@service_path).join(relative_path).to_s
|
|
22
|
-
path << ?? << URI.encode_www_form(query) unless query.empty?
|
|
23
|
-
request = Net::HTTP::Get.new(path, @headers)
|
|
24
|
-
repeatedly_request(request, &block)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def post(relative_path, **query, &block)
|
|
28
|
-
path = Pathname(@service_path).join(relative_path).to_s
|
|
29
|
-
request = Net::HTTP::Post.new(path, @headers)
|
|
30
|
-
request.body = URI.encode_www_form(query)
|
|
31
|
-
repeatedly_request(request, &block)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def process_json(response)
|
|
35
|
-
JSON.parse(response.body).tap do |result|
|
|
36
|
-
# raise Error, result["error"].values_at("message", "details").compact.join(?\n) if result["error"]
|
|
37
|
-
raise Error, result["error"]["message"] if result["error"]
|
|
38
|
-
end
|
|
39
|
-
rescue JSON::ParserError
|
|
40
|
-
raise Error, "unexpected ArcGIS response format"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def get_json(relative_path = "", **query)
|
|
44
|
-
get relative_path, query.merge(f: "json"), &method(:process_json)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def post_json(relative_path = "", **query)
|
|
48
|
-
post relative_path, query.merge(f: "json"), &method(:process_json)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
require_relative 'arcgis_server/connection'
|
|
2
|
-
|
|
3
|
-
module NSWTopo
|
|
4
|
-
module ArcGISServer
|
|
5
|
-
Error = Class.new RuntimeError
|
|
6
|
-
ERRORS = [Timeout::Error, Errno::ENETUNREACH, Errno::ETIMEDOUT, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError]
|
|
7
|
-
SERVICE = /^(?:MapServer|FeatureServer|ImageServer)$/
|
|
8
|
-
|
|
9
|
-
def self.check_uri(url)
|
|
10
|
-
uri = URI.parse url
|
|
11
|
-
return unless URI::HTTP === uri
|
|
12
|
-
instance, (id, *) = uri.path.split(?/).slice_after(SERVICE).take(2)
|
|
13
|
-
return unless instance.last =~ SERVICE
|
|
14
|
-
return unless !id || id =~ /^\d+$/
|
|
15
|
-
return uri, instance.join(?/), id
|
|
16
|
-
rescue URI::Error
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def self.===(string)
|
|
20
|
-
uri, service_path, id = check_uri string
|
|
21
|
-
uri != nil
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def self.start(url, &block)
|
|
25
|
-
uri, service_path, id = check_uri url
|
|
26
|
-
raise "invalid ArcGIS server URL: %s" % url unless uri
|
|
27
|
-
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 600) do |http|
|
|
28
|
-
connection = Connection.new http, service_path
|
|
29
|
-
service = connection.get_json
|
|
30
|
-
projection = case
|
|
31
|
-
when wkt = service.dig("spatialReference", "wkt") then Projection.new(wkt)
|
|
32
|
-
when wkid = service.dig("spatialReference", "latestWkid") then Projection.new("EPSG:#{wkid}")
|
|
33
|
-
when wkid = service.dig("spatialReference", "wkid") then Projection.new("EPSG:#{wkid == 102100 ? 3857 : wkid}")
|
|
34
|
-
else raise Error, "no spatial reference found: #{uri}"
|
|
35
|
-
end
|
|
36
|
-
yield connection, service, projection, *id
|
|
37
|
-
end
|
|
38
|
-
rescue *ERRORS => error
|
|
39
|
-
raise Error, error.message
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def arcgis_layer(url, where: nil, layer: nil, per_page: nil, margin: {})
|
|
43
|
-
ArcGISServer.start url do |connection, service, projection, id|
|
|
44
|
-
id = service["layers"].find do |info|
|
|
45
|
-
layer.to_s == info["name"]
|
|
46
|
-
end&.dig("id") if layer
|
|
47
|
-
id ? nil : layer ? raise("no such ArcGIS layer: %s" % layer) : raise("not an ArcGIS layer url: %s" % url)
|
|
48
|
-
|
|
49
|
-
layer = connection.get_json id.to_s
|
|
50
|
-
query_path = "#{id}/query"
|
|
51
|
-
max_record_count, fields, types, type_id_field, geometry_type, capabilities = layer.values_at "maxRecordCount", "fields", "types", "typeIdField", "geometryType", "capabilities"
|
|
52
|
-
raise Error, "no query capability available: #{url}" unless capabilities =~ /Query|Data/
|
|
53
|
-
|
|
54
|
-
if type_id_field && types
|
|
55
|
-
type_id_field = fields.find do |field|
|
|
56
|
-
field.values_at("alias", "name").include? type_id_field
|
|
57
|
-
end&.fetch("name")
|
|
58
|
-
type_values = types.map do |type|
|
|
59
|
-
type.values_at "id", "name"
|
|
60
|
-
end.to_h
|
|
61
|
-
subtype_coded_values = types.map do |type|
|
|
62
|
-
type.values_at "id", "domains"
|
|
63
|
-
end.map do |id, domains|
|
|
64
|
-
coded_values = domains.map do |name, domain|
|
|
65
|
-
[name, domain["codedValues"]]
|
|
66
|
-
end.select(&:last).map do |name, pairs|
|
|
67
|
-
values = pairs.map do |pair|
|
|
68
|
-
pair.values_at "code", "name"
|
|
69
|
-
end.to_h
|
|
70
|
-
[name, values]
|
|
71
|
-
end.to_h
|
|
72
|
-
[id, coded_values]
|
|
73
|
-
end.to_h
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
coded_values = fields.map do |field|
|
|
77
|
-
[field["name"], field.dig("domain", "codedValues")]
|
|
78
|
-
end.select(&:last).map do |name, pairs|
|
|
79
|
-
values = pairs.map do |pair|
|
|
80
|
-
pair.values_at "code", "name"
|
|
81
|
-
end.to_h
|
|
82
|
-
[name, values]
|
|
83
|
-
end.to_h
|
|
84
|
-
|
|
85
|
-
geometry = { rings: @map.bounding_box(margin).reproject_to(projection).coordinates.map(&:reverse) }.to_json
|
|
86
|
-
where = Array(where).map { |clause| "(#{clause})"}.join " AND "
|
|
87
|
-
query = { geometry: geometry, geometryType: "esriGeometryPolygon", returnIdsOnly: true, where: where }
|
|
88
|
-
|
|
89
|
-
object_ids = connection.get_json(query_path, query)["objectIds"]
|
|
90
|
-
next GeoJSON::Collection.new projection unless object_ids
|
|
91
|
-
|
|
92
|
-
features = Enumerator.new do |yielder|
|
|
93
|
-
per_page, total = [*per_page, *max_record_count, 500].min, object_ids.length
|
|
94
|
-
while object_ids.any?
|
|
95
|
-
yield total - object_ids.length, total if block_given? && total > 0
|
|
96
|
-
yielder << begin
|
|
97
|
-
connection.get_json query_path, outFields: ?*, objectIds: object_ids.take(per_page).join(?,)
|
|
98
|
-
rescue Error => error
|
|
99
|
-
(per_page /= 2) > 0 ? retry : raise(error)
|
|
100
|
-
end
|
|
101
|
-
object_ids.shift per_page
|
|
102
|
-
end
|
|
103
|
-
end.inject [] do |features, page|
|
|
104
|
-
features += page["features"]
|
|
105
|
-
end.map do |feature|
|
|
106
|
-
next unless geometry = feature["geometry"]
|
|
107
|
-
attributes = feature.fetch "attributes", {}
|
|
108
|
-
|
|
109
|
-
values = attributes.map do |name, value|
|
|
110
|
-
case
|
|
111
|
-
when type_id_field == name
|
|
112
|
-
type_values[value]
|
|
113
|
-
when decode = subtype_coded_values&.dig(attributes[type_id_field], name)
|
|
114
|
-
decode[value]
|
|
115
|
-
when decode = coded_values.dig(name)
|
|
116
|
-
decode[value]
|
|
117
|
-
when %w[null Null NULL <null> <Null> <NULL>].include?(value)
|
|
118
|
-
nil
|
|
119
|
-
else value
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
attributes = attributes.keys.zip(values).to_h
|
|
123
|
-
|
|
124
|
-
case geometry_type
|
|
125
|
-
when "esriGeometryPoint"
|
|
126
|
-
point = geometry.values_at "x", "y"
|
|
127
|
-
next unless point.all?
|
|
128
|
-
next GeoJSON::Point.new point, attributes
|
|
129
|
-
when "esriGeometryMultipoint"
|
|
130
|
-
points = geometry["points"]
|
|
131
|
-
next unless points&.any?
|
|
132
|
-
next GeoJSON::MultiPoint.new points.transpose.take(2).transpose, attributes
|
|
133
|
-
when "esriGeometryPolyline"
|
|
134
|
-
raise Error, "ArcGIS curve geometries not supported" if geometry.key? "curvePaths"
|
|
135
|
-
paths = geometry["paths"]
|
|
136
|
-
next unless paths&.any?
|
|
137
|
-
next GeoJSON::LineString.new paths[0], attributes if paths.one?
|
|
138
|
-
next GeoJSON::MultiLineString.new paths, attributes
|
|
139
|
-
when "esriGeometryPolygon"
|
|
140
|
-
raise Error, "ArcGIS curve geometries not supported" if geometry.key? "curveRings"
|
|
141
|
-
rings = geometry["rings"]
|
|
142
|
-
next unless rings&.any?
|
|
143
|
-
rings.each(&:reverse!) unless rings[0].anticlockwise?
|
|
144
|
-
next GeoJSON::Polygon.new rings, attributes if rings.one?
|
|
145
|
-
next GeoJSON::MultiPolygon.new rings.slice_before(&:anticlockwise?).to_a, attributes
|
|
146
|
-
else
|
|
147
|
-
raise Error, "unsupported ArcGIS geometry type: #{geometry_type}"
|
|
148
|
-
end
|
|
149
|
-
end.compact
|
|
150
|
-
|
|
151
|
-
GeoJSON::Collection.new projection, features
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|