nswtopo 2.0.0 → 3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +39 -20
- 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/bin/nswtopo
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# Copyright 2011-
|
3
|
+
# Copyright 2011-2022 Matthew Hollingworth
|
4
4
|
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
|
-
# it under the terms of the GNU General Public License as published by
|
6
|
+
# it under the terms of the GNU Affero General Public License as published by
|
7
7
|
# the Free Software Foundation, either version 3 of the License, or
|
8
8
|
# (at your option) any later version.
|
9
9
|
#
|
10
10
|
# This program is distributed in the hope that it will be useful,
|
11
11
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
-
# GNU General Public License for more details.
|
13
|
+
# GNU Affero General Public License for more details.
|
14
14
|
#
|
15
|
-
# You should have received a copy of the GNU General Public License
|
16
|
-
# along with this program. If not, see <
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
17
17
|
|
18
18
|
begin
|
19
19
|
require 'open3'
|
@@ -30,75 +30,80 @@ begin
|
|
30
30
|
|
31
31
|
GDAL_VERSION = begin
|
32
32
|
stdout, * = Open3.capture3 "gdalinfo", "--version"
|
33
|
-
|
34
|
-
|
33
|
+
/^GDAL (?<version>\d+(?:\.\d+){1,2})/ =~ stdout
|
34
|
+
version || raise
|
35
|
+
rescue Errno::ENOENT, RuntimeError
|
35
36
|
log_abort "GDAL not installed"
|
36
37
|
end
|
37
38
|
|
38
39
|
case
|
39
|
-
when (RUBY_VERSION.split(/\D+/).take(3).map(&:to_i) <=> [
|
40
|
-
log_abort "ruby
|
40
|
+
when (RUBY_VERSION.split(/\D+/).take(3).map(&:to_i) <=> [3,0,4]) < 0
|
41
|
+
log_abort "ruby 3.0.4 or greater required"
|
41
42
|
when !Zlib.const_defined?(:GzipFile)
|
42
43
|
log_abort "ruby with GZIP_SUPPORT required"
|
43
|
-
when (GDAL_VERSION.split(/\D+/).take(3).map(&:to_i) <=> [
|
44
|
-
log_abort "GDAL
|
44
|
+
when (GDAL_VERSION.split(/\D+/).take(3).map(&:to_i) <=> [3,4]) < 0
|
45
|
+
log_abort "GDAL 3.4 or greater required"
|
45
46
|
end
|
46
47
|
|
47
48
|
digits = '\d+(?:_\d+)*'
|
48
49
|
float = "[-+]?(?:#{digits}(?=(.)?)(?:\\.(?:#{digits})?)?|\\.#{digits})(?:[eE][-+]?#{digits})?"
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
50
|
+
pair = "#{float},#{float}"
|
51
|
+
quad = "#{float},#{float},#{float},#{float}"
|
52
|
+
field = '[^\s",]+'
|
53
|
+
|
54
|
+
PositiveInt = Class.new
|
55
|
+
PositiveFloat = Class.new
|
56
|
+
NonNegFloat = Class.new
|
57
|
+
Dimensions = Class.new
|
58
|
+
Inset = Class.new
|
59
|
+
Margins = Class.new
|
60
|
+
CoordList = Class.new
|
61
|
+
Rotation = Class.new
|
62
|
+
Opacity = Class.new
|
63
|
+
DashArray = Class.new
|
64
|
+
Zoom = Class.new
|
65
|
+
ZlibLevel = Class.new
|
66
|
+
FieldList = Class.new
|
67
|
+
|
68
|
+
OptionParser.accept PositiveInt, /\A#{digits}\z/ do |string|
|
63
69
|
raise OptionParser::InvalidArgument, string unless string.to_i.positive?
|
64
70
|
string.to_i
|
65
71
|
end
|
66
72
|
|
67
|
-
OptionParser.accept PositiveFloat,
|
73
|
+
OptionParser.accept PositiveFloat, /\A#{float}\z/ do |string|
|
68
74
|
raise OptionParser::InvalidArgument, string unless string.to_f.positive?
|
69
75
|
string.to_f
|
70
76
|
end
|
71
77
|
|
72
|
-
OptionParser.accept NonNegFloat,
|
78
|
+
OptionParser.accept NonNegFloat, /\A#{float}\z/ do |string|
|
73
79
|
raise OptionParser::InvalidArgument, string if string.to_f.negative?
|
74
80
|
string.to_f
|
75
81
|
end
|
76
82
|
|
77
|
-
OptionParser.accept Dimensions,
|
83
|
+
OptionParser.accept Dimensions, /\A#{pair}\z/ do |string|
|
78
84
|
dimensions = string.split(?,).map(&:to_f)
|
79
85
|
raise OptionParser::InvalidArgument, string unless dimensions.all?(&:positive?)
|
80
86
|
dimensions
|
81
87
|
end
|
82
88
|
|
83
|
-
OptionParser.accept
|
89
|
+
OptionParser.accept Inset, /\A#{quad}(?:,#{quad})*\z/ do |string|
|
90
|
+
string.split(?,).map(&:to_f).each_slice(4).entries
|
91
|
+
end
|
92
|
+
|
93
|
+
OptionParser.accept Margins, /\A#{float}(?:,#{float})?\z/ do |string|
|
84
94
|
margins = string.split(?,).map(&:to_f)
|
85
95
|
raise OptionParser::InvalidArgument, string if margins.any?(&:negative?)
|
86
96
|
margins.one? ? margins * 2 : margins
|
87
97
|
end
|
88
98
|
|
89
|
-
OptionParser.accept CoordList,
|
99
|
+
OptionParser.accept CoordList, /\A#{pair}(?:,#{pair})*\z/ do |string|
|
90
100
|
string.split(?,).map(&:to_f).each_slice(2).to_a
|
91
101
|
end
|
92
102
|
|
93
|
-
OptionParser.accept Rotation,
|
103
|
+
OptionParser.accept Rotation, /\A(?:#{float}|magnetic|auto)\z/ do |string|
|
94
104
|
"magnetic" == string ? string : "auto" == string ? string : string.to_f
|
95
105
|
end
|
96
106
|
|
97
|
-
OptionParser.accept AltitudeAngle, PositiveFloat do |string|
|
98
|
-
raise OptionParser::InvalidArgument, string unless AltitudeAngle === string.to_f
|
99
|
-
string.to_f
|
100
|
-
end
|
101
|
-
|
102
107
|
OptionParser.accept Pathname do |string|
|
103
108
|
path = Pathname(string).expand_path
|
104
109
|
raise OptionParser::InvalidArgument, string unless path.exist?
|
@@ -106,23 +111,37 @@ begin
|
|
106
111
|
end
|
107
112
|
|
108
113
|
OptionParser.accept Colour do |string|
|
109
|
-
string == "none" ? string : Colour.new(string.downcase)
|
114
|
+
string == "none" ? string : Colour.new(string.downcase)
|
110
115
|
rescue Colour::Error
|
111
116
|
raise OptionParser::InvalidArgument, string
|
112
117
|
end
|
113
118
|
|
114
|
-
OptionParser.accept Opacity,
|
119
|
+
OptionParser.accept Opacity, /\A#{float}%?\z/ do |string|
|
115
120
|
opacity = string.end_with?(?%) ? Float(string.chomp ?%) * 0.01 : Float(string)
|
116
121
|
raise OptionParser::InvalidArgument, string unless (0..1) === opacity
|
117
122
|
opacity
|
118
123
|
end
|
119
124
|
|
120
|
-
OptionParser.accept DashArray,
|
125
|
+
OptionParser.accept DashArray, /\A#{float}(?:(?:,#{float})*|(?: #{float})*)\z/ do |string|
|
121
126
|
values = string.split(/[, ]/).map(&:to_f)
|
122
127
|
raise OptionParser::InvalidArgument, string if values.any?(&:negative?)
|
123
128
|
values.join ?\s
|
124
129
|
end
|
125
130
|
|
131
|
+
OptionParser.accept Zoom, /\A(?:#{digits},)?#{digits}\z/ do |string|
|
132
|
+
values = string.split(?,).map(&:to_i)
|
133
|
+
raise OptionParser::InvalidArgument, string unless values.all?(10..20)
|
134
|
+
values
|
135
|
+
end
|
136
|
+
|
137
|
+
OptionParser.accept ZlibLevel, /\A\d\z/ do |string|
|
138
|
+
Integer(string)
|
139
|
+
end
|
140
|
+
|
141
|
+
OptionParser.accept FieldList, /\A#{field}(?:,#{field})*\z/ do |string|
|
142
|
+
string.split ?,
|
143
|
+
end
|
144
|
+
|
126
145
|
ansi = lambda do |string|
|
127
146
|
string.to_s.gsub(/\*([-a-zA-Z0-9]+)\*/) do
|
128
147
|
"\e[1m%s\e[0m" % $1
|
@@ -130,7 +149,7 @@ begin
|
|
130
149
|
"\e[4m%s\e[0m" % $1
|
131
150
|
end.gsub(/~([-a-zA-Z0-9]+)~/) do
|
132
151
|
"\e[3m%s\e[0m" % $1
|
133
|
-
end
|
152
|
+
end.prepend("\r\e[K")
|
134
153
|
end
|
135
154
|
|
136
155
|
plain = lambda do |string|
|
@@ -154,6 +173,7 @@ begin
|
|
154
173
|
controls add rogaine control markers
|
155
174
|
overlay add KML or GPX overlay
|
156
175
|
delete delete map layer
|
176
|
+
move move map layer
|
157
177
|
render render map in various formats
|
158
178
|
layers list available map layers
|
159
179
|
config configure nswtopo
|
@@ -188,15 +208,16 @@ begin
|
|
188
208
|
*nswtopo* *init* - initialise map bounds and scale
|
189
209
|
usage: _nswtopo_ _init_ [~options~] <map.tgz>
|
190
210
|
EOF
|
191
|
-
parser.on "-s", "--scale <scale>", PositiveInt,
|
192
|
-
parser.on "-b", "--bounds <bounds.kml>", Pathname,
|
193
|
-
parser.on "-c", "--coords <x1,y1,...>", CoordList,
|
194
|
-
|
195
|
-
parser.on "-d", "--dimensions <width,height>", Dimensions,
|
196
|
-
parser.on "-
|
197
|
-
|
198
|
-
|
199
|
-
parser.on "-
|
211
|
+
parser.on "-s", "--scale <scale>", PositiveInt, "scale of map (default 25000)"
|
212
|
+
parser.on "-b", "--bounds <bounds.kml>", Pathname, "bounds for map as KML or GPX file"
|
213
|
+
parser.on "-c", "--coords <x1,y1,...>", CoordList, "bounds for map as one or more WGS84",
|
214
|
+
"longitude/latitude pairs"
|
215
|
+
parser.on "-d", "--dimensions <width,height>", Dimensions, "map dimensions in mm"
|
216
|
+
parser.on "-r", "--rotation <rotation>", Rotation, "map rotation angle in clockwise",
|
217
|
+
"degrees, 'auto' or 'magnetic'"
|
218
|
+
parser.on "-m", "--margins <x[,y]>", Margins, "map margins in mm"
|
219
|
+
parser.on "-i", "--inset <x1,y1,x2,y2>", Inset, "map inset coordinates in mm"
|
220
|
+
parser.on "-o", "--overwrite", "overwrite existing map file"
|
200
221
|
|
201
222
|
when "info"
|
202
223
|
parser.banner = <<~EOF
|
@@ -204,72 +225,84 @@ begin
|
|
204
225
|
usage: _nswtopo_ _info_ [~options~] <map.tgz>
|
205
226
|
EOF
|
206
227
|
parser.on "-e", "--empty", "show empty layers"
|
228
|
+
parser.on "-j", "--json", "show map bounds as GeoJSON string"
|
229
|
+
parser.on "-p", "--proj", "show map projection as a proj string"
|
230
|
+
parser.on "-w", "--wkt", "show map projection as a WKT2 string"
|
207
231
|
|
208
232
|
when "add"
|
209
233
|
parser.banner = <<~EOF
|
210
234
|
*nswtopo* *add* - add named map layer
|
211
235
|
usage: _nswtopo_ _add_ [~options~] <map.tgz> <layer> [<layer> ...]
|
212
236
|
EOF
|
213
|
-
parser.on "-r", "--resolution <resolution>", PositiveFloat, "raster
|
237
|
+
parser.on "-r", "--resolution <resolution>", PositiveFloat, "raster resolution in metres per pixel"
|
238
|
+
parser.on "--ppi <ppi>", PositiveFloat, "raster resolution in pixels per inch"
|
239
|
+
parser.on "-o", "--opacity <opacity>", Opacity, "layer opacity (between 0 and 1)"
|
214
240
|
parser.on "-p", "--path <path>", Pathname, "source data path for layer"
|
215
241
|
parser.on "-a", "--after <layer>", "insert after specified layer"
|
216
242
|
parser.on "-b", "--before <layer>", "insert before specified layer"
|
217
243
|
parser.on "-c", "--replace <layer>", "replace specified layer"
|
218
244
|
parser.on "-o", "--overwrite", "overwrite layer if it already exists"
|
245
|
+
parser.on "-s", "--strict", "don't continue if a layer fails"
|
219
246
|
|
220
247
|
when "contours"
|
221
248
|
parser.banner = <<~EOF
|
222
249
|
*nswtopo* *contours* - add contours from elevation data
|
223
250
|
usage: _nswtopo_ _contours_ [~options~] <map.tgz> <dem.zip>
|
224
251
|
EOF
|
225
|
-
parser.on "-i", "--interval
|
226
|
-
parser.on "-x", "--index
|
227
|
-
parser.on "-
|
228
|
-
parser.on "-
|
229
|
-
|
230
|
-
|
231
|
-
parser.on
|
232
|
-
parser.on
|
233
|
-
parser.on "-
|
234
|
-
parser.on "-
|
235
|
-
parser.on "-
|
236
|
-
parser.on
|
237
|
-
parser.on "--stroke
|
238
|
-
parser.on "--
|
252
|
+
parser.on "-i", "--interval <interval>", PositiveInt, "contour interval in metres (default %s)" % NSWTopo::Contour::DEFAULTS["interval"]
|
253
|
+
parser.on "-x", "--index <index>", PositiveInt, "index interval in metres"
|
254
|
+
parser.on "-a", "--auxiliary", "show auxiliary contours"
|
255
|
+
parser.on "-s", "--smooth <radius>", NonNegFloat, "DEM smoothing radius in mm (default %s)" % NSWTopo::Contour::DEFAULTS["smooth"]
|
256
|
+
parser.on "-t", "--thin", "thin intermediate contours in steep areas"
|
257
|
+
parser.on "-d", "--density <density>", PositiveFloat, "thinning threshold (default %s lines/mm)" % NSWTopo::Contour::DEFAULTS["density"]
|
258
|
+
# parser.on "-m", "--min-length <length>", PositiveFloat, "minimum length before contour thinning in mm"
|
259
|
+
parser.on "--no-depression", "don't show or clean depression contours"
|
260
|
+
parser.on "-k", "--knolls <size>", NonNegFloat, "minimum knoll size in mm (default %s)" % NSWTopo::Contour::DEFAULTS["knolls"]
|
261
|
+
parser.on "-a", "--after <layer>", "insert after specified layer"
|
262
|
+
parser.on "-b", "--before <layer>", "insert before specified layer"
|
263
|
+
parser.on "-c", "--replace <layer>", "replace specified layer"
|
264
|
+
parser.on "--stroke <colour>", Colour, "stroke colour (name or RGB triplet)"
|
265
|
+
parser.on "--stroke-width <width>", PositiveFloat, "stroke width in mm (default %s)" % NSWTopo::Contour::DEFAULTS["stroke-width"]
|
266
|
+
parser.on "--fill <colour>", Colour, "label colour (defaults to stroke colour)"
|
267
|
+
parser.on "-r", "--resolution <resolution>", PositiveFloat, "DEM processing resolution in metres"
|
239
268
|
|
240
269
|
when "spot-heights"
|
241
270
|
parser.banner = <<~EOF
|
242
271
|
*nswtopo* *spot-heights* - add spot heights from elevation data
|
243
272
|
usage: _nswtopo_ _spot-heights_ [~options~] <map.tgz> <dem.zip>
|
244
273
|
EOF
|
245
|
-
parser.on "-s", "--smooth
|
246
|
-
parser.on "--spacing
|
247
|
-
parser.on "-p", "--prefer
|
248
|
-
parser.on "-
|
249
|
-
parser.on "-
|
250
|
-
parser.on "-
|
274
|
+
parser.on "-s", "--smooth <radius>", NonNegFloat, "DEM smoothing radius in mm (default %s)" % NSWTopo::Spot::DEFAULTS["smooth"]
|
275
|
+
parser.on "--spacing <spacing>", PositiveFloat, "minimum spot spacing in mm (default %i)" % NSWTopo::Spot::DEFAULTS["spacing"]
|
276
|
+
parser.on "-p", "--prefer <knolls|saddles>", %w[knolls saddles], "preferred spot locations"
|
277
|
+
parser.on "-e", "--extent <extent>", PositiveFloat, "minimum feature extent in mm (default %i)" % NSWTopo::Spot::DEFAULTS["extent"]
|
278
|
+
parser.on "-a", "--after <layer>", "insert after specified layer"
|
279
|
+
parser.on "-b", "--before <layer>", "insert before specified layer"
|
280
|
+
parser.on "-c", "--replace <layer>", "replace specified layer"
|
281
|
+
parser.on "-r", "--resolution <resolution>", PositiveFloat, "DEM processing resolution in metres"
|
251
282
|
|
252
283
|
when "relief"
|
253
284
|
parser.banner = <<~EOF
|
254
285
|
*nswtopo* *relief* - add shaded relief
|
255
286
|
usage: _nswtopo_ _relief_ [~options~] <map.tgz> <dem.zip>
|
256
287
|
EOF
|
257
|
-
parser.on "-r", "--resolution
|
258
|
-
parser.on "-
|
259
|
-
parser.on "-
|
260
|
-
parser.on
|
261
|
-
parser.on "-
|
262
|
-
parser.on "-
|
263
|
-
|
264
|
-
parser.on "-f", "--factor <factor>", PositiveFloat, "exaggeration factor (default %s)" % NSWTopo::Relief::DEFAULTS["factor"]
|
288
|
+
parser.on "-r", "--resolution <resolution>", PositiveFloat, "resolution in metres per pixel"
|
289
|
+
parser.on "-p", "--ppi <ppi>", PositiveFloat, "resolution in pixels per inch"
|
290
|
+
parser.on "-o", "--opacity <opacity>", Opacity, "opacity (default %s)" % NSWTopo::Relief::DEFAULTS["opacity"]
|
291
|
+
parser.on "--shade <colour>", Colour, "shade colour (default %s)" % NSWTopo::Relief::DEFAULTS["shade"]
|
292
|
+
parser.on "-m", "--method <igor|combined>", %w[igor combined], "relief shading method (default %s)" % NSWTopo::Relief::DEFAULTS["method"]
|
293
|
+
parser.on "-z", "--azimuth <azimuth>", Float, "azimuth in degrees (default %i)" % NSWTopo::Relief::DEFAULTS["azimuth"]
|
294
|
+
parser.on "-f", "--factor <factor>", PositiveFloat, "exaggeration factor (default %s)" % NSWTopo::Relief::DEFAULTS["factor"]
|
265
295
|
|
266
296
|
when "grid"
|
267
297
|
parser.banner = <<~EOF
|
268
298
|
*nswtopo* *grid* - add UTM grid
|
269
299
|
usage: _nswtopo_ _grid_ [~options~] <map.tgz>
|
270
300
|
EOF
|
271
|
-
parser.on "-i", "--interval
|
272
|
-
|
301
|
+
parser.on "-i", "--interval <interval>", PositiveFloat, "interval between grid lines in",
|
302
|
+
"metres (default %i)" % NSWTopo::Grid::DEFAULTS["interval"]
|
303
|
+
parser.on "-u", "--unlabeled", "don't add grid labels"
|
304
|
+
parser.on "-b", "--border", "add map border"
|
305
|
+
parser.on "--stroke-width <width>", PositiveFloat, "stroke width in mm (default %s)" % NSWTopo::Grid::DEFAULTS["stroke-width"]
|
273
306
|
|
274
307
|
when "declination"
|
275
308
|
parser.banner = <<~EOF
|
@@ -292,6 +325,7 @@ begin
|
|
292
325
|
parser.on "-s", "--spot", "add spots at centres"
|
293
326
|
parser.on "-c", "--colour <colour>", Colour, "colour of markers and labels",
|
294
327
|
"(name or RGB triplet)"
|
328
|
+
parser.on "-k", "--knockout <width>", NonNegFloat, "symbol knockout width in mm (default %s)" % NSWTopo::Control::DEFAULTS["knockout"]
|
295
329
|
parser.on "-f", "--font-size <font-size>", PositiveFloat, "font size for labels in mm"
|
296
330
|
|
297
331
|
when "overlay"
|
@@ -321,22 +355,30 @@ begin
|
|
321
355
|
usage: _nswtopo_ _delete_ [~options~] <map.tgz> <layer> [<layer> ...]
|
322
356
|
EOF
|
323
357
|
|
358
|
+
when "move"
|
359
|
+
parser.banner = <<~EOF
|
360
|
+
*nswtopo* *move* - move map layer
|
361
|
+
usage: _nswtopo_ _move_ [~options~] <map.tgz> <layer>
|
362
|
+
EOF
|
363
|
+
parser.on "-a", "--after <layer>", "insert after specified layer"
|
364
|
+
parser.on "-b", "--before <layer>", "insert before specified layer"
|
365
|
+
|
324
366
|
when "render"
|
325
367
|
parser.banner = <<~EOF
|
326
368
|
*nswtopo* *render* - render map in various formats
|
327
|
-
usage: _nswtopo_ _render_ [~options~] <map.tgz> [<format-or-path> ...]
|
369
|
+
usage: _nswtopo_ _render_ [~options~] <map.tgz|map.svg> [<format-or-path> ...]
|
328
370
|
formats: #{NSWTopo::Formats.extensions.sort.join ?\s}
|
329
371
|
default: svg
|
330
372
|
EOF
|
331
|
-
parser.on "-p", "--ppi
|
332
|
-
|
333
|
-
parser.on "-z", "--zoom
|
334
|
-
parser.on "-
|
335
|
-
parser.on "-
|
336
|
-
|
337
|
-
|
338
|
-
parser.on "-
|
339
|
-
parser.on "-f", "--force",
|
373
|
+
parser.on "-p", "--ppi <ppi>", PositiveInt, "resolution for raster formats in pixels",
|
374
|
+
"per inch (default %i)" % NSWTopo::Formats::PPI
|
375
|
+
parser.on "-z", "--zoom <zoom>", Zoom, "maximum tile zoom (10-20, default %i)" % NSWTopo::TiledWebMap::DEFAULT_ZOOM
|
376
|
+
parser.on "-b", "--background <colour>", Colour, "background colour (name or RGB triplet)"
|
377
|
+
parser.on "-d", "--dither", "use indexed colour for raster formats"
|
378
|
+
parser.on "-w", "--worldfile", "save additional projection (.prj) and",
|
379
|
+
"world file (.wld) for raster formats"
|
380
|
+
parser.on "-o", "--overwrite", "overwrite existing output files"
|
381
|
+
parser.on "-f", "--force", "force regeneration of cached SVG"
|
340
382
|
|
341
383
|
when "layers"
|
342
384
|
parser.banner = <<~EOF
|
@@ -351,11 +393,46 @@ begin
|
|
351
393
|
EOF
|
352
394
|
parser.on "-d", "--delete <name>", "delete configuration setting"
|
353
395
|
parser.on "-c", "--chrome <path>", Pathname, "set path for Google Chrome"
|
354
|
-
parser.on "-f", "--firefox <path>", Pathname, "set path for Firefox"
|
355
396
|
parser.on "-p", "--path <path>", Pathname, "set path for given layer"
|
356
397
|
parser.on "-r", "--resolution <resolution>", PositiveFloat, "set resolution for given layer"
|
357
398
|
parser.on "--layer-dir <path>", Pathname, "set an extra layer directory"
|
358
399
|
parser.on "--[no-]labelling", "enable or disable map labelling"
|
400
|
+
parser.on "--[no-]debug", "enable or disable label debuging"
|
401
|
+
parser.on "--[no-]gpu", "enable or disable Chrome GPU usage"
|
402
|
+
parser.on "--[no-]versioning", "enable or disable map version checking"
|
403
|
+
parser.on "-z", "--zlib-level <0-9>", ZlibLevel, "set zlib compression level"
|
404
|
+
parser.on "-k", "--knockout <width>", NonNegFloat, "set label knockout width in mm"
|
405
|
+
|
406
|
+
when "scrape"
|
407
|
+
parser.banner = <<~EOF
|
408
|
+
*nswtopo* *scrape* - scrape data from an ArcGIS REST endpoint
|
409
|
+
usage: _nswtopo_ _scrape_ [~options~] <url> <path>
|
410
|
+
EOF
|
411
|
+
parser.on "-l", "--layer <layer>", String, "name of ArcGIS service layer"
|
412
|
+
parser.on "-i", "--id <id>", Integer, "id number of layer"
|
413
|
+
parser.on "-w", "--where <where>", String, "filtering clause"
|
414
|
+
parser.on "-c", "--coords <x0,y0,x1,y1>", CoordList, "WGS84 coordinates of bounding box corners"
|
415
|
+
parser.on "-n", "--name <name>", /^\w+$/, "name of saved layer"
|
416
|
+
parser.on "-f", "--fields <field,...>", FieldList, "comma-separated list of fields"
|
417
|
+
parser.on "-d", "--decode", "convert coded values where possible"
|
418
|
+
parser.on "-e", "--epsg <number>", PositiveInt, "EPSG number for reprojection"
|
419
|
+
parser.on "-p", "--paginate <number>", PositiveInt, "number of records per request"
|
420
|
+
parser.on "--concat", "collect all features before saving"
|
421
|
+
parser.on "-u", "--unique <field>", String, "field for counting map-only features"
|
422
|
+
|
423
|
+
when "inspect"
|
424
|
+
parser.banner = <<~EOF
|
425
|
+
*nswtopo* *inspect* - inspect data from an ArcGIS REST endpoint or local data source
|
426
|
+
usage: _nswtopo_ inspect [~options~] <url-or-path>
|
427
|
+
EOF
|
428
|
+
parser.on "-l", "--layer <layer>", String, "name of layer"
|
429
|
+
parser.on "-i", "--id <id>", Integer, "id number of layer"
|
430
|
+
parser.on "-w", "--where <where>", String, "filtering clause"
|
431
|
+
parser.on "-c", "--coords <x0,y0,x1,y1>", CoordList, "WGS84 coordinates of bounding box corners"
|
432
|
+
parser.on "-f", "--fields <field,...>", FieldList, "comma-separated list of fields"
|
433
|
+
parser.on "-d", "--decode", "convert coded values where possible"
|
434
|
+
parser.on "--codes", "show coded values for an ArcGIS Layer"
|
435
|
+
parser.on "--countwise", "sort fields by count instead of value"
|
359
436
|
|
360
437
|
when nil
|
361
438
|
raise OptionParser::MissingArgument, "no command specified"
|
@@ -381,50 +458,84 @@ begin
|
|
381
458
|
case command
|
382
459
|
when "layers"
|
383
460
|
raise OptionParser::NeedlessArgument, ARGV if ARGV.any?
|
384
|
-
NSWTopo.layers options
|
461
|
+
NSWTopo.layers **options
|
385
462
|
exit
|
386
463
|
when "config"
|
387
464
|
layer = ARGV.shift
|
388
465
|
raise OptionParser::NeedlessArgument, ARGV if ARGV.any?
|
389
466
|
NSWTopo.config *layer, **options
|
390
467
|
exit
|
468
|
+
when "scrape"
|
469
|
+
url, path = ARGV.shift, ARGV.shift
|
470
|
+
raise OptionParser::MissingArgument, "no URL specified" unless url
|
471
|
+
raise OptionParser::MissingArgument, "no path specified" unless path
|
472
|
+
raise OptionParser::NeedlessArgument, ARGV if ARGV.any?
|
473
|
+
path = Pathname(path).expand_path
|
474
|
+
raise OptionParser::InvalidArgument, "invalid path: #{path}" unless path.parent.directory?
|
475
|
+
raise OptionParser::InvalidOption, "can't specify both --id and --layer" if options[:id] && options[:layer]
|
476
|
+
NSWTopo.scrape url, path, **options
|
477
|
+
exit
|
478
|
+
when "inspect"
|
479
|
+
url_or_path = ARGV.shift
|
480
|
+
raise OptionParser::MissingArgument, "no URL or path specified" unless url_or_path
|
481
|
+
raise OptionParser::NeedlessArgument, ARGV if ARGV.any?
|
482
|
+
[%i[where codes], %i[fields codes], %i[decode codes], %i[id layer]].each do |flags|
|
483
|
+
raise OptionParser::InvalidOption, "can't have --%s with --%s" % flags if options.values_at(*flags).all?
|
484
|
+
end
|
485
|
+
raise OptionParser::InvalidOption, "--countwise requires --fields" if options[:countwise] && !options[:fields]
|
486
|
+
raise OptionParser::InvalidOption, "--decode requires --fields" if options[:decode] && !options[:fields]
|
487
|
+
NSWTopo.inspect url_or_path, **options
|
488
|
+
exit
|
489
|
+
when "add", "relief"
|
490
|
+
raise OptionParser::InvalidOption, "can't specify both --resolution and --ppi" if options[:resolution] && options[:ppi]
|
391
491
|
end
|
392
492
|
|
393
493
|
raise OptionParser::MissingArgument, "no map path specified" if ARGV.empty?
|
394
494
|
tgz_path = Pathname(ARGV.shift)
|
395
495
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
command = command.tr ?-, ?_
|
407
|
-
arity, error = NSWTopo.method(command).arity, nil
|
496
|
+
in_path = case command
|
497
|
+
when "init"
|
498
|
+
raise "already a directory: #{tgz_path}" if tgz_path.directory?
|
499
|
+
raise "file already exists: #{tgz_path}" if !options.delete(:overwrite) && tgz_path.exist?
|
500
|
+
raise "no such directory: #{tgz_path.parent}" unless tgz_path.parent.directory?
|
501
|
+
else
|
502
|
+
raise "no such file: #{tgz_path}" unless tgz_path.exist?
|
503
|
+
raise "not a file: #{tgz_path}" unless tgz_path.file?
|
504
|
+
tgz_path
|
505
|
+
end
|
408
506
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
507
|
+
command = command.tr ?-, ?_
|
508
|
+
ARGV.prepend in_path.basename(".tgz").basename(".tar.gz").basename(".svg") if "render" == command
|
509
|
+
|
510
|
+
nil.tap do |partial_failure|
|
511
|
+
NSWTopo::Archive.open(in_path: in_path, out_path: tgz_path) do |archive|
|
512
|
+
NSWTopo.method(command).parameters.group_by(&:first).inject [archive] do |args, (type, params)|
|
513
|
+
case type
|
514
|
+
when :req
|
515
|
+
raise OptionParser::MissingArgument if ARGV.length < params.length - args.length
|
516
|
+
args.concat ARGV.shift(params.length - args.length)
|
517
|
+
when :rest
|
518
|
+
args.concat ARGV.shift(ARGV.length)
|
519
|
+
else args
|
520
|
+
end
|
521
|
+
end.tap do |args|
|
522
|
+
raise OptionParser::NeedlessArgument, ARGV.join(?\s) if ARGV.any?
|
523
|
+
NSWTopo.send command, *args, **options
|
524
|
+
rescue NSWTopo::PartialFailureError => partial_failure
|
416
525
|
end
|
417
|
-
|
418
|
-
NSWTopo.send command, *args
|
419
|
-
rescue NSWTopo::PartialFailureError => error
|
420
526
|
end
|
421
|
-
raise
|
527
|
+
raise partial_failure if partial_failure
|
528
|
+
rescue NSWTopo::Archive::Invalid
|
529
|
+
raise "unrecognised map file: #{in_path}" unless "render" == command
|
530
|
+
raise OptionParser::InvalidOption, "can't specify --force without a map file" if options[:force]
|
531
|
+
in_path, tgz_path, options = nil, nil, options.merge(svg_path: in_path)
|
532
|
+
retry
|
422
533
|
end
|
423
534
|
rescue OptionParser::ParseError => error
|
424
535
|
warn ansi[command_parser] if $stderr.tty?
|
425
536
|
log_abort error.message
|
426
537
|
rescue Interrupt
|
427
|
-
|
538
|
+
log_abort "interrupted"
|
428
539
|
rescue RuntimeError => error
|
429
540
|
log_abort error.message
|
430
541
|
end
|
data/docs/README.md
CHANGED
@@ -10,20 +10,9 @@ Help screens are available describing usage for each commands. Use the `--help`
|
|
10
10
|
$ nswtopo init --help
|
11
11
|
```
|
12
12
|
|
13
|
-
# Configuration
|
14
|
-
|
15
|
-
An important initial step is to configure the location of *Google Chrome* on your PC. Chrome is required for rendering the map in most formats. Use the *configure* command to set the path:
|
16
|
-
|
17
|
-
```
|
18
|
-
$ nswtopo config --chrome "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
19
|
-
nswtopo: configuration updated
|
20
|
-
```
|
21
|
-
|
22
|
-
Use forward slashes for paths, even on Windows.
|
23
|
-
|
24
13
|
# Map Files
|
25
14
|
|
26
|
-
Most commands need a map file to work on. Name this file anything you want. A `.tgz` extension is suggested, as the file is in *gzipped tar* archive format. All map contents are contained within the file
|
15
|
+
Most commands need a map file to work on. Name this file anything you want. A `.tgz` extension is suggested, as the file is in *gzipped tar* archive format. All map contents are contained within the file.
|
27
16
|
|
28
17
|
# Example
|
29
18
|
|
data/docs/add.md
CHANGED
@@ -10,7 +10,7 @@ The forward-slash character is used indicate the nested folder structure of thes
|
|
10
10
|
|
11
11
|
Some layers, such as vegetation layers, require a dataset to be present on your computer. Specify the location of the dataset with the `--path` option. The path can be absolute, or relative to the working directory.
|
12
12
|
|
13
|
-
Raster layers (vegetation, shaded relief) typically
|
13
|
+
Raster layers (vegetation, shaded relief) are typically imported at the data's native resolution. If desired, you can choose a different value using the `--resolution` option, for a data resolution in metres per pixel, or the `--ppi` option for output resolution in pixels per inch.
|
14
14
|
|
15
15
|
For repeated use, it's easier to set the path or resolution for a layer in a permanent configuration file. Use the *config* command for this task.
|
16
16
|
|
data/docs/config.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Description
|
2
2
|
|
3
|
-
Configure and view permanent *nswtopo* settings using the *config* command. For example, to set the *Google Chrome* path
|
3
|
+
Configure and view permanent *nswtopo* settings using the *config* command. For example, to manually set the *Google Chrome* path:
|
4
4
|
|
5
5
|
```
|
6
6
|
$ nswtopo config --chrome "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
data/docs/contours.md
CHANGED
@@ -12,6 +12,8 @@ Choose a contour interval in metres using the `--interval` option. A five metre
|
|
12
12
|
|
13
13
|
Noise in raw elevation data usually produces unsuitably rough contour lines. Some smoothing of the DEM removes most such artefacts. A default smoothing radius of 0.2mm is applied, configurable with the `--smooth` option. Increase the radius to produce smoother contours at the expense of detail.
|
14
14
|
|
15
|
+
DEM tiles are normally processed at their maximumum native resolution. Change this using the `--resolution` option. A reduced resolution (say 5 metres) can markedly improve processing speed for 1- and 2-metre tiles.
|
16
|
+
|
15
17
|
# Layer Position
|
16
18
|
|
17
19
|
Use an `--after`, `--before` or `--replace` option to insert the contours in an appropriate layer position. You will most likely want to replace an existing contour layer:
|
@@ -21,7 +23,7 @@ $ nswtopo contours --replace nsw.topographic.contours map.tgz DATA_25994.zip
|
|
21
23
|
```
|
22
24
|
# Style
|
23
25
|
|
24
|
-
Contours are rendered in brown at a thickness of 0.08mm. Change line colour with `--stroke`, thickness with `--stroke-width` and label colour with `--fill`. Colour can be an *RGB triplet* (e.g.
|
26
|
+
Contours are rendered in brown at a thickness of 0.08mm. Change line colour with `--stroke`, thickness with `--stroke-width` and label colour with `--fill`. Colour can be an *RGB triplet* (e.g. *#800080*) or *web colour* name (e.g. *purple*).
|
25
27
|
|
26
28
|
# Contour Thinning
|
27
29
|
A small contour interval can produce very dense contours in steep terrain. An advanced `--thin` option is available to selectively remove contours in steep areas such as cliffsides. It emulates a manual contour thinning technique. An index multiple of eight (e.g. 5m contours with 40m index contours) produces the most aesthetic results.
|
data/docs/init.md
CHANGED
@@ -36,3 +36,11 @@ $ nswtopo init --dimensions 210,297 --coords 148.387,-36.148 map.tgz
|
|
36
36
|
# Map Scale
|
37
37
|
|
38
38
|
A 1:25000 scale is conventional and should work well for most purposes. Change to a smaller or larger scale using the `--scale` option.
|
39
|
+
|
40
|
+
# Map Inset
|
41
|
+
|
42
|
+
Use the `--inset` option to remove a rectangular inset from the map. Specify the inset location as two opposing corner coordinates in millimetres. For example, to remove a 50mm × 100mm inset from the map's top-left corner:
|
43
|
+
|
44
|
+
```
|
45
|
+
$ nswtopo init --bounds bounds.kml --inset 0,0,50,100 map.tgz
|
46
|
+
```
|