nswtopo 2.0.0.pre.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +674 -0
  3. data/bin/nswtopo +430 -0
  4. data/docs/README.md +78 -0
  5. data/docs/add.md +49 -0
  6. data/docs/config.md +24 -0
  7. data/docs/contours.md +37 -0
  8. data/docs/controls.md +9 -0
  9. data/docs/declination.md +15 -0
  10. data/docs/delete.md +15 -0
  11. data/docs/grid.md +5 -0
  12. data/docs/info.md +5 -0
  13. data/docs/init.md +38 -0
  14. data/docs/layers.md +11 -0
  15. data/docs/overlay.md +37 -0
  16. data/docs/relief.md +22 -0
  17. data/docs/render.md +43 -0
  18. data/docs/spot-heights.md +23 -0
  19. data/lib/nswtopo/archive.rb +93 -0
  20. data/lib/nswtopo/avl_tree.rb +128 -0
  21. data/lib/nswtopo/config.rb +73 -0
  22. data/lib/nswtopo/dither.rb +31 -0
  23. data/lib/nswtopo/font/chrome.rb +59 -0
  24. data/lib/nswtopo/font/generic.rb +25 -0
  25. data/lib/nswtopo/font.rb +43 -0
  26. data/lib/nswtopo/formats/kmz.rb +149 -0
  27. data/lib/nswtopo/formats/mbtiles.rb +64 -0
  28. data/lib/nswtopo/formats/pdf.rb +31 -0
  29. data/lib/nswtopo/formats/svg.rb +69 -0
  30. data/lib/nswtopo/formats/svgz.rb +13 -0
  31. data/lib/nswtopo/formats/zip.rb +40 -0
  32. data/lib/nswtopo/formats.rb +76 -0
  33. data/lib/nswtopo/geometry/overlap.rb +78 -0
  34. data/lib/nswtopo/geometry/r_tree.rb +47 -0
  35. data/lib/nswtopo/geometry/segment.rb +27 -0
  36. data/lib/nswtopo/geometry/straight_skeleton/collapse.rb +21 -0
  37. data/lib/nswtopo/geometry/straight_skeleton/interior_node.rb +17 -0
  38. data/lib/nswtopo/geometry/straight_skeleton/node.rb +50 -0
  39. data/lib/nswtopo/geometry/straight_skeleton/nodes.rb +295 -0
  40. data/lib/nswtopo/geometry/straight_skeleton/split.rb +33 -0
  41. data/lib/nswtopo/geometry/straight_skeleton/vertex.rb +9 -0
  42. data/lib/nswtopo/geometry/straight_skeleton.rb +6 -0
  43. data/lib/nswtopo/geometry/vector.rb +91 -0
  44. data/lib/nswtopo/geometry/vector_sequence.rb +179 -0
  45. data/lib/nswtopo/geometry.rb +8 -0
  46. data/lib/nswtopo/gis/arcgis_server/connection.rb +52 -0
  47. data/lib/nswtopo/gis/arcgis_server.rb +155 -0
  48. data/lib/nswtopo/gis/dem.rb +70 -0
  49. data/lib/nswtopo/gis/esri_hdr.rb +77 -0
  50. data/lib/nswtopo/gis/gdal_glob.rb +41 -0
  51. data/lib/nswtopo/gis/geojson/collection.rb +94 -0
  52. data/lib/nswtopo/gis/geojson/line_string.rb +11 -0
  53. data/lib/nswtopo/gis/geojson/multi_line_string.rb +63 -0
  54. data/lib/nswtopo/gis/geojson/multi_point.rb +12 -0
  55. data/lib/nswtopo/gis/geojson/multi_polygon.rb +167 -0
  56. data/lib/nswtopo/gis/geojson/point.rb +9 -0
  57. data/lib/nswtopo/gis/geojson/polygon.rb +11 -0
  58. data/lib/nswtopo/gis/geojson.rb +89 -0
  59. data/lib/nswtopo/gis/gps/gpx.rb +22 -0
  60. data/lib/nswtopo/gis/gps/kml.rb +66 -0
  61. data/lib/nswtopo/gis/gps.rb +20 -0
  62. data/lib/nswtopo/gis/projection.rb +56 -0
  63. data/lib/nswtopo/gis/shapefile.rb +24 -0
  64. data/lib/nswtopo/gis/world_file.rb +19 -0
  65. data/lib/nswtopo/gis.rb +9 -0
  66. data/lib/nswtopo/help_formatter.rb +59 -0
  67. data/lib/nswtopo/helpers/array.rb +30 -0
  68. data/lib/nswtopo/helpers/colour.rb +176 -0
  69. data/lib/nswtopo/helpers/concurrently.rb +27 -0
  70. data/lib/nswtopo/helpers/dir.rb +7 -0
  71. data/lib/nswtopo/helpers/hash.rb +15 -0
  72. data/lib/nswtopo/helpers/tar_writer.rb +11 -0
  73. data/lib/nswtopo/helpers.rb +6 -0
  74. data/lib/nswtopo/layer/arcgis_raster.rb +73 -0
  75. data/lib/nswtopo/layer/contour.rb +233 -0
  76. data/lib/nswtopo/layer/control.rb +94 -0
  77. data/lib/nswtopo/layer/declination.rb +53 -0
  78. data/lib/nswtopo/layer/feature.rb +87 -0
  79. data/lib/nswtopo/layer/grid.rb +120 -0
  80. data/lib/nswtopo/layer/import.rb +25 -0
  81. data/lib/nswtopo/layer/labels/fence.rb +20 -0
  82. data/lib/nswtopo/layer/labels.rb +630 -0
  83. data/lib/nswtopo/layer/overlay.rb +53 -0
  84. data/lib/nswtopo/layer/raster.rb +63 -0
  85. data/lib/nswtopo/layer/relief.rb +143 -0
  86. data/lib/nswtopo/layer/spot.rb +171 -0
  87. data/lib/nswtopo/layer/vector.rb +263 -0
  88. data/lib/nswtopo/layer/vegetation.rb +73 -0
  89. data/lib/nswtopo/layer.rb +78 -0
  90. data/lib/nswtopo/log.rb +28 -0
  91. data/lib/nswtopo/map.rb +296 -0
  92. data/lib/nswtopo/os.rb +75 -0
  93. data/lib/nswtopo/safely.rb +13 -0
  94. data/lib/nswtopo/version.rb +4 -0
  95. data/lib/nswtopo/zip.rb +15 -0
  96. data/lib/nswtopo.rb +249 -0
  97. metadata +142 -0
data/bin/nswtopo ADDED
@@ -0,0 +1,430 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright 2011-2019 Matthew Hollingworth
4
+ #
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
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ begin
19
+ require 'open3'
20
+ require 'optparse'
21
+ require 'pathname'
22
+ require 'etc'
23
+ require 'yaml'
24
+ require 'rdoc'
25
+ require_relative '../lib/nswtopo.rb'
26
+ require_relative '../lib/nswtopo/help_formatter.rb'
27
+
28
+ extend NSWTopo::Log
29
+ Thread::report_on_exception = false
30
+
31
+ GDAL_VERSION = begin
32
+ stdout, * = Open3.capture3 "gdalinfo", "--version"
33
+ stdout[/\d+(?:\.\d+){1,2}/]
34
+ rescue Errno::ENOENT
35
+ log_abort "GDAL not installed"
36
+ end
37
+
38
+ case
39
+ when (RUBY_VERSION.split(/\D+/).take(3).map(&:to_i) <=> [2,5]) < 0
40
+ log_abort "ruby 2.5 or greater required"
41
+ when !Zlib.const_defined?(:GzipFile)
42
+ log_abort "ruby with GZIP_SUPPORT required"
43
+ when (GDAL_VERSION.split(/\D+/).take(3).map(&:to_i) <=> [2,3]) < 0
44
+ log_abort "GDAL 2.3 or greater required"
45
+ end
46
+
47
+ digits = '\d+(?:_\d+)*'
48
+ float = "[-+]?(?:#{digits}(?=(.)?)(?:\\.(?:#{digits})?)?|\\.#{digits})(?:[eE][-+]?#{digits})?"
49
+ coords = "#{float},#{float}"
50
+
51
+ PositiveInt = /\A#{digits}\z/
52
+ PositiveFloat = /\A#{float}\z/
53
+ NonNegFloat = 0..Float::INFINITY
54
+ Dimensions = /\A#{float},#{float}\z/
55
+ Margins = /\A#{float}(?:,#{float})?\z/
56
+ CoordList = /\A#{coords}(?:,#{coords})*\z/
57
+ Rotation = /\A(?:#{float}|magnetic|auto)\z/
58
+ AltitudeAngle = 0..90
59
+ Opacity = /\A#{float}%?\z/
60
+ DashArray = /\A#{float}(?:(?:,#{float})*|(?: #{float})*)\z/
61
+
62
+ OptionParser.accept PositiveInt, PositiveInt do |string|
63
+ raise OptionParser::InvalidArgument, string unless string.to_i.positive?
64
+ string.to_i
65
+ end
66
+
67
+ OptionParser.accept PositiveFloat, PositiveFloat do |string|
68
+ raise OptionParser::InvalidArgument, string unless string.to_f.positive?
69
+ string.to_f
70
+ end
71
+
72
+ OptionParser.accept NonNegFloat, PositiveFloat do |string|
73
+ raise OptionParser::InvalidArgument, string if string.to_f.negative?
74
+ string.to_f
75
+ end
76
+
77
+ OptionParser.accept Dimensions, Dimensions do |string|
78
+ dimensions = string.split(?,).map(&:to_f)
79
+ raise OptionParser::InvalidArgument, string unless dimensions.all?(&:positive?)
80
+ dimensions
81
+ end
82
+
83
+ OptionParser.accept Margins, Margins do |string|
84
+ margins = string.split(?,).map(&:to_f)
85
+ raise OptionParser::InvalidArgument, string if margins.any?(&:negative?)
86
+ margins.one? ? margins * 2 : margins
87
+ end
88
+
89
+ OptionParser.accept CoordList, CoordList do |string|
90
+ string.split(?,).map(&:to_f).each_slice(2).to_a
91
+ end
92
+
93
+ OptionParser.accept Rotation, Rotation do |string|
94
+ "magnetic" == string ? string : "auto" == string ? string : string.to_f
95
+ end
96
+
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
+ OptionParser.accept Pathname do |string|
103
+ path = Pathname(string).expand_path
104
+ raise OptionParser::InvalidArgument, string unless path.exist?
105
+ path
106
+ end
107
+
108
+ OptionParser.accept Colour do |string|
109
+ string == "none" ? string : Colour.new(string.downcase).to_s
110
+ rescue Colour::Error
111
+ raise OptionParser::InvalidArgument, string
112
+ end
113
+
114
+ OptionParser.accept Opacity, Opacity do |string|
115
+ opacity = string.end_with?(?%) ? Float(string.chomp ?%) * 0.01 : Float(string)
116
+ raise OptionParser::InvalidArgument, string unless (0..1) === opacity
117
+ opacity
118
+ end
119
+
120
+ OptionParser.accept DashArray, DashArray do |string|
121
+ values = string.split(/[, ]/).map(&:to_f)
122
+ raise OptionParser::InvalidArgument, string if values.any?(&:negative?)
123
+ values.join ?\s
124
+ end
125
+
126
+ ansi = lambda do |string|
127
+ string.to_s.gsub(/\*([-a-zA-Z0-9]+)\*/) do
128
+ "\e[1m%s\e[0m" % $1
129
+ end.gsub(/_([-a-zA-Z0-9]+)_/) do
130
+ "\e[4m%s\e[0m" % $1
131
+ end.gsub(/~([-a-zA-Z0-9]+)~/) do
132
+ "\e[3m%s\e[0m" % $1
133
+ end
134
+ end
135
+
136
+ plain = lambda do |string|
137
+ string.to_s.gsub(/\*([-a-zA-Z0-9]+)\*/) { $1 }.gsub(/_([-a-zA-Z0-9]+)_/) { $1 }.gsub(/~([-a-zA-Z0-9]+)~/) { $1 }
138
+ end
139
+
140
+ base_parser = OptionParser.new do |parser|
141
+ parser.separator " options:"
142
+ parser.banner = <<~EOF
143
+ *nswtopo* - download and create vector topographic maps
144
+ usage: _nswtopo_ [~options~] [<command> ...]
145
+ commands:
146
+ init initialise map bounds and scale
147
+ info display map layers and metadata
148
+ add add named map layer
149
+ contours add contours from elevation data
150
+ spot-heights add spot heights from elevation data
151
+ relief add shaded relief
152
+ grid add UTM grid
153
+ declination add magnetic declination lines
154
+ controls add rogaine control markers
155
+ overlay add KML or GPX overlay
156
+ delete delete map layer
157
+ render render map in various formats
158
+ layers list available map layers
159
+ config configure nswtopo
160
+ command help: _nswtopo_ <command> --help
161
+ EOF
162
+ parser.on "-v", "--version", "show version information" do
163
+ puts NSWTopo::VERSION
164
+ exit
165
+ end
166
+ parser.on "-q", "--quiet", "suppress non-error output" do
167
+ $stdout = File.open(File::NULL, "w")
168
+ end
169
+ parser.on "-c", "--config <path>", Pathname, "load extra configuration" do |path|
170
+ NSWTopo::Config.extra_path = path
171
+ end
172
+ parser.on "-h", "--help", "show general help" do
173
+ puts $stdout.tty? ? ansi[parser] : plain[parser]
174
+ doc = Pathname(__dir__).parent / "docs" / "README.md"
175
+ puts nil, RDoc::Markdown.parse(doc.read encoding: Encoding::UTF_8).accept(HelpFormatter.new $stdout.tty?)
176
+ exit
177
+ end
178
+ end
179
+ base_parser.order!
180
+
181
+ command, options = ARGV.shift, Hash.new
182
+ command_parser = OptionParser.new do |parser|
183
+ parser.separator " options:"
184
+
185
+ case command
186
+ when "init"
187
+ parser.banner = <<~EOF
188
+ *nswtopo* *init* - initialise map bounds and scale
189
+ usage: _nswtopo_ _init_ [~options~] <map.tgz>
190
+ EOF
191
+ parser.on "-s", "--scale <scale>", PositiveInt, "scale of map (default 25000)"
192
+ parser.on "-b", "--bounds <bounds.kml>", Pathname, "bounds for map as KML or GPX file"
193
+ parser.on "-c", "--coords <x1,y1,...>", CoordList, "bounds for map as one or more WGS84",
194
+ "longitude/latitude pairs"
195
+ parser.on "-d", "--dimensions <width,height>", Dimensions, "dimensions of map in mm"
196
+ parser.on "-m", "--margins <x[,y]>", Margins, "map margins in mm"
197
+ parser.on "-r", "--rotation <rotation>", Rotation, "map rotation angle in clockwise",
198
+ "degrees, 'auto' or 'magnetic'"
199
+ parser.on "-o", "--overwrite", "overwrite existing map file"
200
+
201
+ when "info"
202
+ parser.banner = <<~EOF
203
+ *nswtopo* *info* - display map layers and metadata
204
+ usage: _nswtopo_ _info_ [~options~] <map.tgz>
205
+ EOF
206
+ parser.on "-e", "--empty", "show empty layers"
207
+
208
+ when "add"
209
+ parser.banner = <<~EOF
210
+ *nswtopo* *add* - add named map layer
211
+ usage: _nswtopo_ _add_ [~options~] <map.tgz> <layer> [<layer> ...]
212
+ EOF
213
+ parser.on "-r", "--resolution <resolution>", PositiveFloat, "raster layer resolution in metres"
214
+ parser.on "-p", "--path <path>", Pathname, "source data path for layer"
215
+ parser.on "-a", "--after <layer>", "insert after specified layer"
216
+ parser.on "-b", "--before <layer>", "insert before specified layer"
217
+ parser.on "-c", "--replace <layer>", "replace specified layer"
218
+ parser.on "-o", "--overwrite", "overwrite layer if it already exists"
219
+
220
+ when "contours"
221
+ parser.banner = <<~EOF
222
+ *nswtopo* *contours* - add contours from elevation data
223
+ usage: _nswtopo_ _contours_ [~options~] <map.tgz> <dem.zip>
224
+ EOF
225
+ parser.on "-i", "--interval <interval>", PositiveInt, "contour interval in metres (default %s)" % NSWTopo::Contour::DEFAULTS["interval"]
226
+ parser.on "-x", "--index <index>", PositiveInt, "index interval in metres"
227
+ parser.on "-s", "--smooth <radius>", NonNegFloat, "DEM smoothing radius in mm (default %s)" % NSWTopo::Contour::DEFAULTS["smooth"]
228
+ parser.on "-t", "--thin", "thin intermediate contours in steep areas"
229
+ # parser.on "-d", "--density <density>", PositiveFloat, "maximum lines/mm before thinning occurs"
230
+ # parser.on "-m", "--min-length <length>", PositiveFloat, "minimum length before contour thinning in mm"
231
+ parser.on "--no-depression", "don't show or clean depression contours"
232
+ parser.on "-k", "--knolls <size>", NonNegFloat, "minimum knoll size in mm (default %s)" % NSWTopo::Contour::DEFAULTS["knolls"]
233
+ parser.on "-a", "--after <layer>", "insert after specified layer"
234
+ parser.on "-b", "--before <layer>", "insert before specified layer"
235
+ parser.on "-c", "--replace <layer>", "replace specified layer"
236
+ parser.on "--stroke <colour>", Colour, "stroke colour (name or RGB triplet)"
237
+ parser.on "--stroke-width <width>", PositiveFloat, "stroke width in mm"
238
+ parser.on "--fill <colour>", Colour, "label colour (defaults to stroke colour)"
239
+
240
+ when "spot-heights"
241
+ parser.banner = <<~EOF
242
+ *nswtopo* *spot-heights* - add spot heights from elevation data
243
+ usage: _nswtopo_ _spot-heights_ [~options~] <map.tgz> <dem.zip>
244
+ EOF
245
+ parser.on "-s", "--smooth <radius>", NonNegFloat, "DEM smoothing radius in mm (default %s)" % NSWTopo::Spot::DEFAULTS["smooth"]
246
+ parser.on "--spacing <spacing>", PositiveFloat, "minimum spot spacing in mm (default %i)" % NSWTopo::Spot::DEFAULTS["spacing"]
247
+ parser.on "-p", "--prefer <knolls|saddles>", %w[knolls saddles], "preferred spot locations"
248
+ parser.on "-a", "--after <layer>", "insert after specified layer"
249
+ parser.on "-b", "--before <layer>", "insert before specified layer"
250
+ parser.on "-c", "--replace <layer>", "replace specified layer"
251
+
252
+ when "relief"
253
+ parser.banner = <<~EOF
254
+ *nswtopo* *relief* - add shaded relief
255
+ usage: _nswtopo_ _relief_ [~options~] <map.tgz> <dem.zip>
256
+ EOF
257
+ parser.on "-r", "--resolution <resolution>", PositiveFloat, "resolution in metres (default %i)" % NSWTopo::Relief::DEFAULTS["resolution"]
258
+ parser.on "-o", "--opacity <opacity>", Opacity, "opacity (default %s)" % NSWTopo::Relief::DEFAULTS["opacity"]
259
+ parser.on "-a", "--altitude <altitude>", AltitudeAngle, "altitude angle in degrees (default %i)" % NSWTopo::Relief::DEFAULTS["altitude"]
260
+ parser.on "-z", "--azimuth <azimuth>", Float, "azimuth in degrees (default %i)" % NSWTopo::Relief::DEFAULTS["azimuth"]
261
+ parser.on "-s", "--sources <sources>", PositiveInt, "number of light sources (default %i)" % NSWTopo::Relief::DEFAULTS["sources"]
262
+ parser.on "-y", "--yellow <fraction>", Opacity, "yellow illumination as a fraction",
263
+ "of shading (default %s)" % NSWTopo::Relief::DEFAULTS["yellow"]
264
+ parser.on "-f", "--factor <factor>", PositiveFloat, "exaggeration factor (default %s)" % NSWTopo::Relief::DEFAULTS["factor"]
265
+
266
+ when "grid"
267
+ parser.banner = <<~EOF
268
+ *nswtopo* *grid* - add UTM grid
269
+ usage: _nswtopo_ _grid_ [~options~] <map.tgz>
270
+ EOF
271
+ parser.on "-i", "--interval <interval>", PositiveFloat, "interval between grid lines in",
272
+ "metres (default %i)" % NSWTopo::Grid::DEFAULTS["interval"]
273
+
274
+ when "declination"
275
+ parser.banner = <<~EOF
276
+ *nswtopo* *declination* - add magnetic declination lines
277
+ usage: _nswtopo_ _declination_ [~options~] <map.tgz>
278
+ EOF
279
+ parser.on "-a", "--angle <angle>", Float, "magnetic declination in clockwise degrees",
280
+ "(calculated automatically by default)"
281
+ parser.on "-s", "--spacing <spacing>", PositiveFloat, "spacing of lines in mm (default %i)" % NSWTopo::Declination::DEFAULTS["spacing"]
282
+ parser.on "-o", "--offset <offset>", Float, "rightwards offset of lines in mm"
283
+ parser.on "-r", "--arrows <arrows>", PositiveFloat, "spacing of arrows in mm (default %i)" % NSWTopo::Declination::DEFAULTS["arrows"]
284
+ parser.on "--stroke <colour>", Colour, "stroke colour (name or RGB triplet)"
285
+
286
+ when "controls"
287
+ parser.banner = <<~EOF
288
+ *nswtopo* *controls* - add rogaine control markers
289
+ usage: _nswtopo_ _controls_ [~options~] <map.tgz> <controls.gpx>
290
+ EOF
291
+ parser.on "-d", "--diameter <diameter>", PositiveFloat, "diameter of markers in mm (default %s)" % NSWTopo::Control::DEFAULTS["diameter"]
292
+ parser.on "-s", "--spot", "add spots at centres"
293
+ parser.on "-c", "--colour <colour>", Colour, "colour of markers and labels",
294
+ "(name or RGB triplet)"
295
+ parser.on "-f", "--font-size <font-size>", PositiveFloat, "font size for labels in mm"
296
+
297
+ when "overlay"
298
+ parser.banner = <<~EOF
299
+ *nswtopo* *overlay* - add KML or GPX overlay
300
+ usage: _nswtopo_ _overlay_ [~options~] <map.tgz> <overlay.kml>
301
+ EOF
302
+ parser.on "--opacity <opacity>", Opacity, "layer opacity (between 0 and 1)"
303
+ parser.on "--stroke <colour>", Colour, "stroke colour (name or RGB triplet)"
304
+ parser.on "--stroke-width <width>", PositiveFloat, "stroke width in mm"
305
+ parser.on "--stroke-opacity <opacity>", Opacity, "stroke opacity"
306
+ parser.on "--stroke-dasharray <mm,...>", DashArray, "stroke dash sequence in mm"
307
+ parser.on "--stroke-linecap <butt|round|square>", %w[butt round square],
308
+ "stroke linecap value"
309
+ parser.on "--fill <colour>", Colour, "polygon fill colour"
310
+ parser.on "--fill-opacity <opacity>", Opacity, "polygon fill opacity"
311
+ parser.on "-s", "--simplify", "apply track simplification"
312
+ parser.on "-t", "--tolerance <metres>", PositiveFloat, "track simplifiction tolerance in metres",
313
+ "(scale-appropriate value used by default)"
314
+ parser.on "-a", "--after <layer>", "insert after specified layer"
315
+ parser.on "-b", "--before <layer>", "insert before specified layer"
316
+ parser.on "-c", "--replace <layer>", "replace specified layer"
317
+
318
+ when "delete"
319
+ parser.banner = <<~EOF
320
+ *nswtopo* *delete* - delete map layers
321
+ usage: _nswtopo_ _delete_ [~options~] <map.tgz> <layer> [<layer> ...]
322
+ EOF
323
+
324
+ when "render"
325
+ parser.banner = <<~EOF
326
+ *nswtopo* *render* - render map in various formats
327
+ usage: _nswtopo_ _render_ [~options~] <map.tgz> [<format-or-path> ...]
328
+ formats: #{NSWTopo::Formats.extensions.sort.join ?\s}
329
+ default: svg
330
+ EOF
331
+ parser.on "-p", "--ppi <ppi>", PositiveInt, "resolution for raster formats in pixels",
332
+ "per inch (default %i)" % NSWTopo::Formats::PPI
333
+ parser.on "-z", "--zoom <zoom>", Integer, "maximum mbtiles zoom level (default %i)" % NSWTopo::Formats::Mbtiles::ZOOM
334
+ parser.on "-d", "--dither", "use indexed colour for raster formats"
335
+ parser.on "-w", "--worldfile", "save additional projection (.prj) and",
336
+ "world file (.wld) for raster formats"
337
+ parser.on "-o", "--overwrite", "overwrite existing output files"
338
+ parser.on "-e", "--external <map.svg>", Pathname, "render from externally edited SVG"
339
+ parser.on "-f", "--force", "force regeneration of cached SVG"
340
+
341
+ when "layers"
342
+ parser.banner = <<~EOF
343
+ *nswtopo* *layers* - list available map layers
344
+ usage: _nswtopo_ _layers_ [~options~]
345
+ EOF
346
+
347
+ when "config"
348
+ parser.banner = <<~EOF
349
+ *nswtopo* *config* - configure nswtopo
350
+ usage: _nswtopo_ _config_ [~options~] [<layer>]
351
+ EOF
352
+ parser.on "-d", "--delete <name>", "delete configuration setting"
353
+ parser.on "-c", "--chrome <path>", Pathname, "set path for Google Chrome"
354
+ parser.on "-f", "--firefox <path>", Pathname, "set path for Firefox"
355
+ parser.on "-p", "--path <path>", Pathname, "set path for given layer"
356
+ parser.on "-r", "--resolution <resolution>", PositiveFloat, "set resolution for given layer"
357
+ parser.on "--layer-dir <path>", Pathname, "set an extra layer directory"
358
+ parser.on "--[no-]labelling", "enable or disable map labelling"
359
+
360
+ when nil
361
+ raise OptionParser::MissingArgument, "no command specified"
362
+
363
+ else
364
+ raise OptionParser::InvalidArgument, command
365
+ end
366
+
367
+ parser.on "-h", "--help", "show help for this command" do
368
+ puts $stdout.tty? ? ansi[parser] : plain[parser]
369
+ doc = Pathname(__dir__).parent / "docs" / "#{command}.md"
370
+ puts nil, RDoc::Markdown.parse(doc.read encoding: Encoding::UTF_8).accept(HelpFormatter.new $stdout.tty?)
371
+ rescue Errno::ENOENT
372
+ ensure
373
+ exit
374
+ end
375
+ rescue OptionParser::ParseError => error
376
+ warn ansi[base_parser] if $stderr.tty?
377
+ raise error.message
378
+ end
379
+ command_parser.parse! into: options
380
+
381
+ case command
382
+ when "layers"
383
+ raise OptionParser::NeedlessArgument, ARGV if ARGV.any?
384
+ NSWTopo.layers options
385
+ exit
386
+ when "config"
387
+ layer = ARGV.shift
388
+ raise OptionParser::NeedlessArgument, ARGV if ARGV.any?
389
+ NSWTopo.config *layer, **options
390
+ exit
391
+ end
392
+
393
+ raise OptionParser::MissingArgument, "no map path specified" if ARGV.empty?
394
+ tgz_path = Pathname(ARGV.shift)
395
+
396
+ begin
397
+ in_path = case command
398
+ when "init"
399
+ raise "#{tgz_path} already exists" if !options.delete(:overwrite) && tgz_path.exist?
400
+ else
401
+ raise "no such file #{tgz_path}" unless tgz_path.exist?
402
+ raise "#{tgz_path} is not a file" unless tgz_path.file?
403
+ tgz_path
404
+ end
405
+
406
+ command = command.tr ?-, ?_
407
+ arity, error = NSWTopo.method(command).arity, nil
408
+
409
+ NSWTopo::Archive.open(tgz_path, *in_path) do |archive|
410
+ args = [archive, *ARGV, options]
411
+ case
412
+ when arity >= 0 && args.length > arity
413
+ raise OptionParser::NeedlessArgument, ARGV.last(args.length - arity).join(?\s)
414
+ when arity >= 0 ? args.length < arity : args.length + arity + 1 < 0
415
+ raise OptionParser::MissingArgument
416
+ end
417
+
418
+ NSWTopo.send command, *args
419
+ rescue NSWTopo::PartialFailureError => error
420
+ end
421
+ raise error.message if error
422
+ end
423
+ rescue OptionParser::ParseError => error
424
+ warn ansi[command_parser] if $stderr.tty?
425
+ log_abort error.message
426
+ rescue Interrupt
427
+ abort $stderr.tty? ? "\r\e[K\e[31mnswtopo:\e[0m interrupted" : "nswtopo: interrupted"
428
+ rescue RuntimeError => error
429
+ log_abort error.message
430
+ end
data/docs/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # Description
2
+
3
+ Use *nswtopo* to create vector topographic maps of NSW and other states. Various *commands* allow you to initialise your map, add layers and render outputs in a number of formats. Pre-designed map layers download topographic data from internet maps servers and local sources.
4
+
5
+ # Commands
6
+
7
+ Help screens are available describing usage for each commands. Use the `--help` option with the command:
8
+
9
+ ```
10
+ $ nswtopo init --help
11
+ ```
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
+ # Map Files
25
+
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, so a separate directory per map is not necessary.
27
+
28
+ # Example
29
+
30
+ A typical map creation sequence might look as follows. We initialise the map from a bounds file, add several layers and finally produce an output SVG:
31
+
32
+ ```
33
+ $ nswtopo init -b bounds.kml map.tgz
34
+ scale: 1:25000
35
+ dimensions: 433mm × 509mm
36
+ extent: 10.8km × 12.7km
37
+ area: 138.0km²
38
+ rotation: 0.0°
39
+ ```
40
+
41
+ ```
42
+ $ nswtopo add map.tgz nsw/vegetation-spot5
43
+ nswtopo: added layer: nsw.vegetation-spot5
44
+ ```
45
+
46
+ ```
47
+ $ nswtopo add map.tgz nsw/topographic
48
+ nswtopo: added layer: nsw.topographic.plantation-horticulture
49
+ nswtopo: added layer: nsw.topographic.urban-areas
50
+ ...
51
+ nswtopo: added layer: nsw.topographic.spot-heights
52
+ ```
53
+
54
+ ```
55
+ $ nswtopo declination map.tgz
56
+ nswtopo: added layer: declination
57
+ ```
58
+
59
+ ```
60
+ $ nswtopo add map.tgz controls.gpx
61
+ nswtopo: added layer: controls
62
+ ```
63
+
64
+ ```
65
+ $ nswtopo relief map.tgz DATA_25994.zip
66
+ nswtopo: added layer: relief
67
+ ```
68
+
69
+ ```
70
+ $ nswtopo contours -i 5 -x 50 --replace nsw.topographic.contours map.tgz DATA_25994.zip
71
+ nswtopo: added layer: contours
72
+ ```
73
+
74
+ ```
75
+ $ nswtopo render map.tgz svg
76
+ nswtopo: created map.svg
77
+ ```
78
+
data/docs/add.md ADDED
@@ -0,0 +1,49 @@
1
+ # Description
2
+
3
+ Use this command to add named layers to the map from among those distributed with *nswtopo*. Refer to the website for a description of the various layers. Use the `nswtopo layers` command to display a list of available layers.
4
+
5
+ Layers are arranged hierarchically, with some layers being shorthand for a collection of other layers. For example, the `nsw/topographic` layer contains a large number of component layers, such as `nsw/topographic/roads`, `nsw/topographic/watercourses` etc.
6
+
7
+ The forward-slash character is used indicate the nested folder structure of these layers. However, once added to the map, these layers are renamed with periods (`nsw.topographic.watercourses`), and the use of period and slash characters is interchangeable.
8
+
9
+ # Options
10
+
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
+
13
+ Raster layers (vegetation, shaded relief) typically have an appropriate image resolution set for that data. If desired, you can choose a different value using the `--resolution` option. Resolution is in metres per pixel, indicating the dataset quality rather than an output resolution such as pixels per inch.
14
+
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
+
17
+ # Positioning Layers
18
+
19
+ By default, layers are added to the map in an appropriate position for the type: vegetation and aerial layers first, followed by topographic feature layers, overlays, shaded relief, grid, declination and controls.
20
+
21
+ To instead select a specific position for the new layer, use the `--after` or `--before` option with an existing layer name. For example, to insert a KML overlay between existing topographic layers:
22
+
23
+ ```
24
+ $ nswtopo add --after nsw.topographic.urban-areas map.tgz new-suburb.kml
25
+ ```
26
+
27
+ # Other Layers
28
+
29
+ While *grid*, *declination*, overlay and *controls* layers each have a dedicated command, it's possible to add them directly if you don't need to change default settings:
30
+
31
+ ```
32
+ $ nswtopo add map.tgz out-of-bounds.kml grid controls.gpx
33
+ ```
34
+
35
+ Georeferenced rasters (e.g. GeoTIFFs) can also be added directly:
36
+
37
+ ```
38
+ $ nswtopo add map.tgz underlay.tif
39
+ ```
40
+
41
+ For advanced users, custom layer definitions can added by referencing the `.yml` definition file:
42
+
43
+ ```
44
+ $ nswtopo add --after nsw.topographic.water-areas map.tgz bathymetry.yml
45
+ ```
46
+
47
+ # Failed Layers
48
+
49
+ Map servers can sometimes be uncooperative, resulting in layers which fail to download. In this event, simply run the `add` command again to retry the failed layers. Existing layers will not be re-downloaded.
data/docs/config.md ADDED
@@ -0,0 +1,24 @@
1
+ # Description
2
+
3
+ Configure and view permanent *nswtopo* settings using the *config* command. For example, to set the *Google Chrome* path for rendering maps:
4
+
5
+ ```
6
+ $ nswtopo config --chrome "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
7
+ nswtopo: configuration updated
8
+ ```
9
+
10
+ To set the path for a vegetation dataset:
11
+
12
+ ```
13
+ $ nswtopo config --path ~/SPOT5/s5hgps_nsw_y20082012_bcvl0.tif nsw.vegetation-spot5
14
+ nswtopo: configuration updated
15
+ ```
16
+
17
+ To review your current configuration:
18
+
19
+ ```
20
+ $ nswtopo config
21
+ chrome: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
22
+ nsw.vegetation-spot5:
23
+ path: "/Users/matthew/SPOT5/s5hgps_nsw_y20082012_bcvl0.tif"
24
+ ```
data/docs/contours.md ADDED
@@ -0,0 +1,37 @@
1
+ # Description
2
+
3
+ Generate contour lines directly from a Digital Elevation Model (DEM) with the *contours* command. Any DEM in a planar projection can be used, but high-resolution data is needed for good results.
4
+
5
+ # Obtaining the DEM
6
+ Use the *ELVIS* website [http://elevation.fsdf.org.au] to download DEM tiles for any NSW location. The NSW 2-metre and 5-metre tiles are ideal. 1-metre NSW and ACT tiles also work but are more detailed than necessary. (Do not download Geoscience Australia tiles or point-cloud data.)
7
+
8
+ DEM tiles from the ELVIS website are delivered as doubly-zipped files. It's not necessary to unzip the download, although unzipping the first level to a folder will improve processing time.
9
+
10
+ # Contour Configuration
11
+ Choose a contour interval in metres using the `--interval` option. A five metre interval is recommended as it conveys excellent detail and is not too dense for 1:25000 maps in most areas. Specify an index contour interval with the `--index` option.
12
+
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
+
15
+ # Layer Position
16
+
17
+ 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:
18
+
19
+ ```
20
+ $ nswtopo contours --replace nsw.topographic.contours map.tgz DATA_25994.zip
21
+ ```
22
+ # Style
23
+
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. *800080*) or *web colour* name (e.g. *purple*).
25
+
26
+ # Contour Thinning
27
+ 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.
28
+
29
+ (*GDAL* with *SpatiaLite* and *GEOS* support is required to perform contour thinning.)
30
+
31
+ # Knolls & Depressions
32
+
33
+ Contours generated from a DEM can include depression artefacts, most noticeabley at pinch-points in flat or closed-in watercourses. These do not usually represent true depression contours, which are rare. Any isolated depression contours are automatically detected and removed. All nested depression contours are retained and rendered as true depression contours. This behaviour can be disabled with the `--no-depression` option.
34
+
35
+ Contours for tiny knolls are also removed. Use the `--knolls` option to specify the minimum size for knolls to be retained.
36
+
37
+ When creating contours from a DEM, it's recommended to also generate a new spot heights layer. Use the *spot-heights* command to generate them using the same DEM.
data/docs/controls.md ADDED
@@ -0,0 +1,9 @@
1
+ # Description
2
+
3
+ Add rogaine controls to a map with the *controls* command. Collect your control waypoints in a GPX or KML file. *Google Earth* is useful for managing and naming your waypoints during course-setting.
4
+
5
+ # Naming
6
+ Any waypoint with a two- or three-digit name will be identified and marked as a control. Name a waypoint as *HH* or *ANC* to identify it as the hash house or all-night cafe. Suffix any control name with *W*, or have a separate waypoint so named, to identify it as a water drop.
7
+
8
+ # Styling
9
+ Controls are marked and labelled in the conventional style. You can change aspects of this style with the `--diameter`, `--colour` and `--font-size` options. The `--spot` option adds a centre spot for each control.
@@ -0,0 +1,15 @@
1
+ # Description
2
+
3
+ Use the *declination* command to add lines of magnetic declination to the map. These are useful primarily in rogaining maps, where they facilitate compass bearings.
4
+
5
+ # Declination Angle
6
+
7
+ The magnetic declination angle is obtained from the NOAA online calculator using the World Magnetic Model. An accuracy of ±0.5° is typical. In the event that the calculator is offline, you can provide a declination angle manually using the `--angle` option.
8
+
9
+ # Appearance
10
+
11
+ Declination lines are spaced at one-kilometre intervals, or according to the `--spacing` option if passed. Small directional arrows are provided periodically along each line at 160mm intervals.
12
+
13
+ The `--offset` option shifts the lines along the horizontal, if fine-tuning is required.
14
+
15
+ Change colour using the `--stroke` option, with an *RGB triplet* (e.g. *800080*) or *web colour* name (e.g. *purple*).
data/docs/delete.md ADDED
@@ -0,0 +1,15 @@
1
+ # Description
2
+
3
+ Use the *delete* command to delete layers from the map. Specify the name of the layer or layers you wish to delete:
4
+
5
+ ```
6
+ $ nswtopo delete map.tgz nsw.relief grid
7
+ ```
8
+
9
+ When deleting multiple layers, a form of wildcard is also available:
10
+
11
+ ```
12
+ $ nswtopo delete map.tgz "nsw.topographic.*"
13
+ ```
14
+
15
+ Use the *info* command to see the names of layers currently in the map file.