Module: NSWTopo
- Extended by:
- NSWTopo, Log
- Included in:
- NSWTopo
- Defined in:
- lib/nswtopo.rb,
lib/nswtopo/os.rb,
lib/nswtopo/log.rb,
lib/nswtopo/map.rb,
lib/nswtopo/svg.rb,
lib/nswtopo/zip.rb,
lib/nswtopo/font.rb,
lib/nswtopo/layer.rb,
lib/nswtopo/chrome.rb,
lib/nswtopo/config.rb,
lib/nswtopo/dither.rb,
lib/nswtopo/safely.rb,
lib/nswtopo/archive.rb,
lib/nswtopo/formats.rb,
lib/nswtopo/gis/dem.rb,
lib/nswtopo/gis/gps.rb,
lib/nswtopo/version.rb,
lib/nswtopo/commands.rb,
lib/nswtopo/layer/grid.rb,
lib/nswtopo/layer/spot.rb,
lib/nswtopo/formats/kmz.rb,
lib/nswtopo/formats/pdf.rb,
lib/nswtopo/formats/svg.rb,
lib/nswtopo/formats/zip.rb,
lib/nswtopo/gis/geojson.rb,
lib/nswtopo/gis/gps/gpx.rb,
lib/nswtopo/gis/gps/kml.rb,
lib/nswtopo/commands/add.rb,
lib/nswtopo/formats/gemf.rb,
lib/nswtopo/formats/svgz.rb,
lib/nswtopo/gis/esri_hdr.rb,
lib/nswtopo/layer/import.rb,
lib/nswtopo/layer/labels.rb,
lib/nswtopo/layer/raster.rb,
lib/nswtopo/layer/relief.rb,
lib/nswtopo/gis/gdal_glob.rb,
lib/nswtopo/gis/shapefile.rb,
lib/nswtopo/layer/contour.rb,
lib/nswtopo/layer/control.rb,
lib/nswtopo/layer/feature.rb,
lib/nswtopo/layer/overlay.rb,
lib/nswtopo/tiled_web_map.rb,
lib/nswtopo/tree_indenter.rb,
lib/nswtopo/gis/projection.rb,
lib/nswtopo/commands/config.rb,
lib/nswtopo/commands/layers.rb,
lib/nswtopo/commands/scrape.rb,
lib/nswtopo/formats/mbtiles.rb,
lib/nswtopo/commands/inspect.rb,
lib/nswtopo/gis/arcgis/layer.rb,
lib/nswtopo/layer/vegetation.rb,
lib/nswtopo/gis/geojson/point.rb,
lib/nswtopo/layer/colour_mask.rb,
lib/nswtopo/layer/declination.rb,
lib/nswtopo/layer/mask_render.rb,
lib/nswtopo/gis/arcgis/service.rb,
lib/nswtopo/layer/labels/label.rb,
lib/nswtopo/gis/geojson/polygon.rb,
lib/nswtopo/layer/arcgis_raster.rb,
lib/nswtopo/layer/raster_import.rb,
lib/nswtopo/layer/raster_render.rb,
lib/nswtopo/layer/vector_render.rb,
lib/nswtopo/gis/arcgis/layer/map.rb,
lib/nswtopo/gis/arcgis/connection.rb,
lib/nswtopo/layer/labels/barriers.rb,
lib/nswtopo/gis/arcgis/layer/query.rb,
lib/nswtopo/gis/geojson/collection.rb,
lib/nswtopo/gis/geojson/line_string.rb,
lib/nswtopo/gis/geojson/multi_point.rb,
lib/nswtopo/layer/labels/convex_hull.rb,
lib/nswtopo/gis/arcgis/layer/renderer.rb,
lib/nswtopo/gis/geojson/multi_polygon.rb,
lib/nswtopo/layer/labels/convex_hulls.rb,
lib/nswtopo/layer/vector_render/cutout.rb,
lib/nswtopo/gis/arcgis/layer/statistics.rb,
lib/nswtopo/layer/vector_render/knockout.rb,
lib/nswtopo/gis/geojson/multi_line_string.rb
Defined Under Namespace
Modules: ArcGIS, ArcGISRaster, ColourMask, Config, Contour, Control, DEM, Declination, Dither, Feature, Font, Formats, GDALGlob, GeoJSON, Grid, Import, Labels, Log, MaskRender, OS, Overlay, Raster, RasterImport, RasterRender, Relief, SVG, Safely, Shapefile, Spot, TiledWebMap, VectorRender, Vegetation, Zip
Classes: Archive, Chrome, ESRIHdr, GPS, Layer, Map, Projection, SVGFormatter, TreeIndenter, Version
Constant Summary
collapse
- PartialFailureError =
Class.new RuntimeError
- VERSION =
Version["nswtopo 3.1.1"]
- MIN_VERSION =
Version["nswtopo 3.0"]
Constants included
from Log
Log::FAILURE, Log::NEUTRAL, Log::SUCCESS, Log::UPDATE
Instance Method Summary
collapse
-
#add(archive, *layers, after: nil, before: nil, replace: nil, overwrite: false, strict: false, **options) ⇒ Object
-
#config(layer = nil, **options) ⇒ Object
-
#contours(archive, dem_path, **options) ⇒ Object
-
#controls(archive, gps_path, **options) ⇒ Object
-
#declination(archive, **options) ⇒ Object
-
#delete(archive, *names, **options) ⇒ Object
-
#grid(archive, **options) ⇒ Object
-
#info(archive, **options) ⇒ Object
-
#init(archive, **options) ⇒ Object
-
#inspect(url_or_path, layer: nil, coords: nil, codes: nil, countwise: nil, **options) ⇒ Object
-
#layer_dirs ⇒ Object
-
#layers(state: nil) ⇒ Object
-
#move(archive, name, **options) ⇒ Object
-
#overlay(archive, gps_path, **options) ⇒ Object
-
#relief(archive, dem_path, **options) ⇒ Object
-
#render(archive, basename, *formats, overwrite: false, svg_path: nil, **options) ⇒ Object
-
#scrape(url, path, coords: nil, name: nil, epsg: nil, paginate: nil, concat: nil, **options) ⇒ Object
-
#spot_heights(archive, dem_path, **options) ⇒ Object
Methods included from Log
log_abort, log_neutral, log_success, log_update, log_warn
Instance Method Details
#add(archive, *layers, after: nil, before: nil, replace: nil, overwrite: false, strict: false, **options) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
# File 'lib/nswtopo/commands/add.rb', line 2
def add(archive, *layers, after: nil, before: nil, replace: nil, overwrite: false, strict: false, **options)
create_options = {
after: Layer.sanitise(after),
before: Layer.sanitise(before),
replace: Layer.sanitise(replace),
overwrite: overwrite,
strict: strict
}
map = Map.load archive
Enumerator.new do |yielder|
while layers.any?
layer, basedir = layers.shift
path = Pathname(layer).expand_path(*basedir)
case layer
when /^controls\.(gpx|kml)$/i
yielder << [path.basename(path.extname).to_s, "type" => "Control", "path" => path]
when /\.(gpx|kml)$/i
yielder << [path.basename(path.extname).to_s, "type" => "Overlay", "path" => path]
when /\.(tiff?|png|jpg)$/i
yielder << [path.basename(path.extname).to_s, "type" => "Import", "path" => path]
when "contours"
yielder << [layer, "type" => "Contour"]
when "spot-heights"
yielder << [layer, "type" => "Spot"]
when "relief"
yielder << [layer, "type" => "Relief"]
when "grid"
yielder << [layer, "type" => "Grid"]
when "declination"
yielder << [layer, "type" => "Declination"]
when "controls"
yielder << [layer, "type" => "Control"]
when /\.yml$/i
basedir ||= path.parent
raise "couldn't find '#{layer}'" unless path.file?
case contents = YAML.load(path.read)
when Array
contents.reverse.map do |item|
Pathname(item.to_s)
end.each do |relative_path|
raise "#{relative_path} is not a relative path" unless relative_path.relative?
layers.prepend [Pathname(relative_path).expand_path(path.parent).relative_path_from(basedir).to_s, basedir]
end
when Hash
name = path.sub_ext("").relative_path_from(basedir).descend.map(&:basename).join(?.)
yielder << [name, contents.merge("source" => path)]
else
raise "couldn't parse #{path}"
end
else
path = Pathname("#{layer}.yml")
raise "#{layer} is not a relative path" unless path.relative?
basedir ||= layer_dirs.find do |root|
path.expand_path(root).file?
end
layers.prepend [path.to_s, basedir]
end
end
rescue YAML::Exception
raise "couldn't parse #{path}"
end.map do |name, params|
params.merge! options.transform_keys(&:to_s)
params.merge! Config[name] if Config[name]
Layer.new(name, map, params)
end.tap do |layers|
raise OptionParser::MissingArgument, "no layers specified" unless layers.any?
unless layers.one?
raise OptionParser::InvalidArgument, "can't specify opacity when adding multiple layers" if options[:opacity]
raise OptionParser::InvalidArgument, "can't specify data path when adding multiple layers" if options[:path]
end
map.add *layers, **create_options
end
end
|
#config(layer = nil, **options) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
# File 'lib/nswtopo/commands/config.rb', line 2
def config(layer = nil, **options)
path, resolution = options[:path], options[:resolution]
layer = Layer.sanitise layer
case
when !layer
raise OptionParser::InvalidArgument, "no layer name specified for path" if path
raise OptionParser::InvalidArgument, "no layer name specified for resolution" if resolution
when path || resolution
Config.store layer, "path", path.to_s if path
Config.store layer, "resolution", resolution if resolution
end
options.each do |key, value|
case key
when :chrome
raise "chrome path is not an executable" unless value.executable? && !value.directory?
Config.store key.to_s, value.to_s
when :"layer-dir"
raise "not a directory: %s" % value unless value.directory?
Config.store key.to_s, value.to_s
when *%i[labelling debug gpu versioning zlib-level knockout]
Config.store key.to_s, value
when :delete
Config.delete *layer, value
end
end
if options.empty?
puts Config.to_str.each_line.drop(1)
log_neutral "no configuration yet" if Config.empty?
else
Config.save
log_success "configuration updated"
end
end
|
#contours(archive, dem_path, **options) ⇒ Object
77
78
79
|
# File 'lib/nswtopo/commands/add.rb', line 77
def contours(archive, dem_path, **options)
add archive, "contours", **options, path: Pathname(dem_path)
end
|
#controls(archive, gps_path, **options) ⇒ Object
97
98
99
100
|
# File 'lib/nswtopo/commands/add.rb', line 97
def controls(archive, gps_path, **options)
raise OptionParser::InvalidArgument, gps_path unless gps_path =~ /\.(gpx|kml)$/i
add archive, "controls", **options, path: Pathname(gps_path)
end
|
#declination(archive, **options) ⇒ Object
93
94
95
|
# File 'lib/nswtopo/commands/add.rb', line 93
def declination(archive, **options)
add archive, "declination", **options
end
|
#delete(archive, *names, **options) ⇒ Object
17
18
19
20
21
22
23
24
25
26
|
# File 'lib/nswtopo/commands.rb', line 17
def delete(archive, *names, **options)
map = Map.load archive
names.map do |name|
Layer.sanitise name
end.uniq.map do |name|
name[?*] ? %r[^#{name.gsub(?., '\.').gsub(?*, '.*')}$] : name
end.tap do |names|
map.delete *names
end
end
|
#grid(archive, **options) ⇒ Object
89
90
91
|
# File 'lib/nswtopo/commands/add.rb', line 89
def grid(archive, **options)
add archive, "grid", **options
end
|
#info(archive, **options) ⇒ Object
12
13
14
15
|
# File 'lib/nswtopo/commands.rb', line 12
def info(archive, **options)
raise OptionParser::InvalidArgument, "one output option only" if options.slice(:json, :proj).length > 1
puts Map.load(archive).info(**options)
end
|
#init(archive, **options) ⇒ Object
8
9
10
|
# File 'lib/nswtopo/commands.rb', line 8
def init(archive, **options)
puts Map.init(archive, **options)
end
|
#inspect(url_or_path, layer: nil, coords: nil, codes: nil, countwise: nil, **options) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
# File 'lib/nswtopo/commands/inspect.rb', line 2
def inspect(url_or_path, layer: nil, coords: nil, codes: nil, countwise: nil, **options)
options[:geometry] = GeoJSON.multipoint(coords).bbox if coords
case url_or_path
when ArcGIS::Service
source = ArcGIS::Service.new(url_or_path)
when Shapefile::Source
raise OptionParser::InvalidOption, "--id only applies to ArcGIS layers" if options[:id]
raise OptionParser::InvalidOption, "--decode only applies to ArcGIS layers" if options[:decode]
raise OptionParser::InvalidOption, "--codes only applies to ArcGIS layers" if codes
source = Shapefile::Source.new(url_or_path)
layer ||= source.only_layer
else
raise OptionParser::InvalidArgument, url_or_path
end
layer = source.layer(layer: layer, **options)
case
when codes
TreeIndenter.new(layer.codes) do |level|
level.map do |key, values|
case key
when Array
code, value = key
display_value = value.nil? || /[\t\n\r]/ === value ? value.inspect : value
["#{code} → #{display_value}", values]
else
["#{key}:", values]
end
end
end.each do |indents, info|
puts indents.join << info
end
when fields = options[:fields]
template = "%%%is │ %%%is │ %%s"
TreeIndenter.new(layer.counts) do |counts|
counts.group_by do |attributes, count|
attributes.shift
end.entries.select(&:first).map do |(name, value), counts|
[[name, counts.sum(&:last), value], counts]
end.sort do |((name1, count1, value1), counts1), ((name2, count2, value2), counts2)|
next count2 <=> count1 if countwise
value1 && value2 ? value1 <=> value2 : value1 ? 1 : value2 ? -1 : 0
end
end.map do |indents, (name, count, value)|
next name, count.to_s, indents.join << (value.nil? || /[\t\n\r]/ === value ? value.inspect : value.to_s)
end.transpose.tap do |names, counts, lines|
template %= [names.map(&:size).max, counts.map(&:size).max] if names
end.transpose.each do |row|
puts template % row
end
else
TreeIndenter.new(layer.info) do |hash|
hash.map do |key, value|
Hash === value ? ["#{key}:", value] : "#{key}: #{value}"
end
end.each do |indents, info|
puts indents.join << info
end
end
rescue ArcGIS::Layer::NoLayerError, Shapefile::Layer::NoLayerError => error
raise OptionParser::MissingArgument, error.message if codes || countwise || options.any?
puts "layers:"
TreeIndenter.new(source.layer_info, []).each do |indents, info|
puts indents.join << info
end
rescue ArcGIS::Renderer::TooManyFieldsError
raise OptionParser::InvalidOption, "use less fields with --fields"
end
|
#layer_dirs ⇒ Object
54
55
56
|
# File 'lib/nswtopo.rb', line 54
def layer_dirs
@layer_dirs ||= Array(Config["layer-dir"]).map(&Pathname.method(:new)) << Pathname.pwd
end
|
#layers(state: nil) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# File 'lib/nswtopo/commands/layers.rb', line 2
def layers(state: nil)
paths = layer_dirs.grep_v(Pathname.pwd).flat_map do |directory|
Array(state).inject(directory, &:/).glob("*")
end.sort
log_warn "no layers installed" if paths.none?
TreeIndenter.new(paths) do |paths|
paths.filter_map do |path|
case
when path.glob("**/*.yml").any?
[path.basename.sub_ext(""), path.children.sort]
when path.sub_ext("").directory?
when path.extname == ".yml"
path.basename.sub_ext("")
end
end
end.each do |indents, name|
puts [*indents, name].join
end
end
|
#move(archive, name, **options) ⇒ Object
28
29
30
31
32
|
# File 'lib/nswtopo/commands.rb', line 28
def move(archive, name, **options)
raise OptionParser::InvalidArgument, "only one of --before and --after allowed" if options[:after] && options[:before]
raise OptionParser::MissingArgument, "--before or --after required" unless options[:after] || options[:before]
Map.load(archive).move(name, **options)
end
|
#overlay(archive, gps_path, **options) ⇒ Object
102
103
104
105
|
# File 'lib/nswtopo/commands/add.rb', line 102
def overlay(archive, gps_path, **options)
raise OptionParser::InvalidArgument, gps_path unless gps_path =~ /\.(gpx|kml)$/i
add archive, gps_path, **options, path: Pathname(gps_path)
end
|
#relief(archive, dem_path, **options) ⇒ Object
85
86
87
|
# File 'lib/nswtopo/commands/add.rb', line 85
def relief(archive, dem_path, **options)
add archive, "relief", **options, path: Pathname(dem_path)
end
|
#render(archive, basename, *formats, overwrite: false, svg_path: nil, **options) ⇒ Object
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
# File 'lib/nswtopo/commands.rb', line 34
def render(archive, basename, *formats, overwrite: false, svg_path: nil, **options)
case
when formats.any?
when svg_path
raise OptionParser::MissingArgument, "no output format specified"
else
formats << "svg"
end
formats.map do |format|
Pathname(Formats === format ? "#{basename}.#{format}" : format)
end.uniq.each do |path|
format = path.extname.delete_prefix(?.)
raise "unrecognised format: #{path}" if format.empty?
raise "unrecognised format: #{format}" unless Formats === format
raise "already a directory: #{path}" if path.directory?
raise "file already exists: #{path}" if path.exist? && !overwrite
raise "no such directory: #{path.parent}" unless path.parent.directory?
end.tap do |paths|
map = svg_path ? Map.from_svg(archive, svg_path) : Map.load(archive)
map.render *paths, **options
end
end
|
#scrape(url, path, coords: nil, name: nil, epsg: nil, paginate: nil, concat: nil, **options) ⇒ Object
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# File 'lib/nswtopo/commands/scrape.rb', line 2
def scrape(url, path, coords: nil, name: nil, epsg: nil, paginate: nil, concat: nil, **options)
flags = %w[-skipfailures]
flags += %W[-t_srs epsg:#{epsg}] if epsg
flags += %W[-nln #{name}] if name
format_flags = case path.to_s
when Shapefile::Source then %w[-update -overwrite]
when /\.sqlite3?$/ then %w[-f SQLite -dsco SPATIALITE=YES]
when /\.db$/ then %w[-f SQLite -dsco SPATIALITE=YES]
when /\.gpkg$/ then %w[-f GPKG]
when /\.tab$/ then ["-f", "MapInfo File"]
else ["-f", "ESRI Shapefile", "-lco", "ENCODING=UTF-8"]
end
options.merge! case path.to_s
when /\.sqlite3?$/ then { mixed: concat, launder: true }
when /\.db$/ then { mixed: concat, launder: true }
when /\.gpkg$/ then { mixed: concat, launder: true }
when /\.tab$/ then { }
else { truncate: 10 }
end
options[:geometry] = GeoJSON.multipoint(coords).bbox if coords
log_update "nswtopo: contacting server"
layer = ArcGIS::Service.new(url).layer(**options)
queue = Queue.new
thread = Thread.new do
while page = queue.pop
*, status = Open3.capture3 *%W[ogr2ogr #{path} /vsistdin/], *flags, *format_flags, stdin_data: page.to_json
format_flags = %w[-update -append]
queue.close unless status.success?
end
status
end
total_features, percent = "%i feature%s", "%%.%if%%%%"
Enumerator.new do |yielder|
hold, ok, count = [], nil, 0
layer.paged(per_page: paginate).tap do
total_features %= [layer.count, (?s unless layer.count == 1)]
percent %= layer.count < 1000 ? 0 : layer.count < 10000 ? 1 : 2
log_update "nswtopo: retrieving #{total_features}"
end.each do |page|
log_update "nswtopo: retrieving #{percent} of #{total_features}" % [100.0 * (count += page.count) / layer.count]
next hold << page if concat
next yielder << page if ok
next hold << page if page.all? do |feature|
feature.properties.values.any?(&:nil?)
end
yielder << page
ok = true
end
next hold.inject(yielder, &:<<) if ok && !concat
next yielder << hold.inject(&:merge!) if hold.any?
end.inject(queue) do |queue, page|
queue << page
rescue ClosedQueueError
break queue
end.close
log_update "nswtop: saving #{total_features}"
raise "error while saving features" unless thread.value&.success?
log_success "saved #{total_features}"
rescue ArcGIS::Layer::NoLayerError
raise OptionParser::InvalidArgument, "specify an ArcGIS layer in URL or with --layer"
rescue ArcGIS::Map::NoUniqueFieldError
raise OptionParser::InvalidOption, "--unique required for this layer"
rescue ArcGIS::Renderer::NoGeometryError
raise OptionParser::InvalidOption, "--coords not available for this layer"
rescue ArcGIS::Query::UniqueFieldError
raise OptionParser::InvalidOption, "--unique not available for this layer"
rescue ArcGIS::Service::InvalidURLError
raise OptionParser::InvalidArgument, url
end
|
#spot_heights(archive, dem_path, **options) ⇒ Object
81
82
83
|
# File 'lib/nswtopo/commands/add.rb', line 81
def spot_heights(archive, dem_path, **options)
add archive, "spot-heights", **options, path: Pathname(dem_path)
end
|