nswtopo 2.0.0 → 3.0.1
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 +232 -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 +112 -18
- 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 +204 -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 +7 -196
- 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/lib/nswtopo/layer/vector.rb
CHANGED
@@ -1,13 +1,48 @@
|
|
1
|
+
require_relative 'vector/cutout'
|
2
|
+
require_relative 'vector/knockout'
|
3
|
+
|
1
4
|
module NSWTopo
|
2
5
|
module Vector
|
3
|
-
SVG_ATTRIBUTES = %w[
|
4
|
-
|
6
|
+
SVG_ATTRIBUTES = %w[
|
7
|
+
fill-opacity
|
8
|
+
fill
|
9
|
+
font-family
|
10
|
+
font-size
|
11
|
+
font-style
|
12
|
+
font-variant
|
13
|
+
font-weight
|
14
|
+
letter-spacing
|
15
|
+
opacity
|
16
|
+
paint-order
|
17
|
+
stroke-dasharray
|
18
|
+
stroke-dashoffset
|
19
|
+
stroke-linecap
|
20
|
+
stroke-linejoin
|
21
|
+
stroke-miterlimit
|
22
|
+
stroke-opacity
|
23
|
+
stroke-width
|
24
|
+
stroke
|
25
|
+
text-decoration
|
26
|
+
visibility
|
27
|
+
word-spacing
|
28
|
+
nswtopo:overprint
|
29
|
+
nswtopo:stroke
|
30
|
+
nswtopo:fill
|
31
|
+
]
|
32
|
+
|
33
|
+
FONT_SCALED_ATTRIBUTES = %w[
|
34
|
+
word-spacing
|
35
|
+
letter-spacing
|
36
|
+
stroke-width
|
37
|
+
line-height
|
38
|
+
]
|
39
|
+
|
5
40
|
SHIELD_X, SHIELD_Y = 1.0, 0.5
|
6
41
|
MARGIN = { mm: 1.0 }
|
7
42
|
VALUE, POINT, ANGLE = "%.5f", "%.5f %.5f", "%.2f"
|
8
43
|
|
9
44
|
def create
|
10
|
-
@features = get_features.reproject_to(@map.projection).clip
|
45
|
+
@features = get_features.reproject_to(@map.neatline.projection).clip(@map.neatline(**MARGIN))
|
11
46
|
@map.write filename, @features.to_json
|
12
47
|
end
|
13
48
|
|
@@ -22,10 +57,6 @@ module NSWTopo
|
|
22
57
|
extend Forwardable
|
23
58
|
def_delegator :features, :none?, :empty?
|
24
59
|
|
25
|
-
def to_mm
|
26
|
-
@to_mm ||= @map.method(:coords_to_mm)
|
27
|
-
end
|
28
|
-
|
29
60
|
def drawing_features
|
30
61
|
features.explode.reject do |feature|
|
31
62
|
feature["draw"] == false
|
@@ -55,10 +86,10 @@ module NSWTopo
|
|
55
86
|
distances = extras.segments.map(&:distance)
|
56
87
|
offsets = midpoints.zip(distances).segments.map(&:transpose).map do |segment, distance|
|
57
88
|
segment.along(distance.first / distance.inject(&:+))
|
58
|
-
end.zip(points).map(&:
|
59
|
-
controls = midpoints.segments.zip(offsets).
|
89
|
+
end.zip(points).map(&:diff)
|
90
|
+
controls = midpoints.segments.zip(offsets).flat_map do |segment, offset|
|
60
91
|
segment.map { |point| [point, point.plus(offset)].along(fraction) }
|
61
|
-
end.
|
92
|
+
end.drop(1).each_slice(2).entries.prepend(nil)
|
62
93
|
points.zip(controls).map do |point, controls|
|
63
94
|
controls ? "C %s %s %s" % [POINT, POINT, POINT] % [*controls.flatten, *point] : "M %s" % POINT % point
|
64
95
|
end.join(" ")
|
@@ -74,51 +105,67 @@ module NSWTopo
|
|
74
105
|
Array(key).any? do |selector|
|
75
106
|
String(selector).split(?\s).to_set <= categories
|
76
107
|
end
|
77
|
-
end.values.inject(params, &:
|
108
|
+
end.values.inject(params, &:deep_merge)
|
78
109
|
end
|
79
110
|
|
80
|
-
def render(
|
111
|
+
def render(knockout:, **, &block)
|
112
|
+
defs = REXML::Element.new("defs").tap(&block)
|
113
|
+
defs.add_attributes "id" => "#{@name}.defs"
|
114
|
+
|
81
115
|
drawing_features.group_by do |feature, categories|
|
82
116
|
categories || Array(feature["category"]).map(&:to_s).map(&method(:categorise)).to_set
|
83
|
-
end.
|
117
|
+
end.flat_map do |categories, features|
|
84
118
|
dupes = params_for(categories)["dupe"]
|
85
119
|
Array(dupes).map(&:to_s).map do |dupe|
|
86
120
|
[categories | Set[dupe], [name, *categories, "content"].join(?.)]
|
87
121
|
end.push [categories, features]
|
88
|
-
end.
|
122
|
+
end.tap do |ordered|
|
123
|
+
params.fetch("order", []).reverse.map(&:split).map(&:to_set).each do |filter|
|
124
|
+
ordered.sort_by!.with_index do |(categories, features), index|
|
125
|
+
[filter <= categories ? 0 : 1, index]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end.each do |categories, features|
|
89
129
|
ids = [name, *categories]
|
130
|
+
use = REXML::Element.new("use")
|
131
|
+
use.add_attributes "id" => ids.join(?.)
|
132
|
+
|
90
133
|
case features
|
91
134
|
when String
|
92
|
-
|
135
|
+
use.add_attributes "href" => "##{features}"
|
93
136
|
when Array
|
94
|
-
|
95
|
-
|
137
|
+
content = defs.add_element "g", "id" => [*ids, "content"].join(?.)
|
138
|
+
use.add_attributes "href" => "#" + [*ids, "content"].join(?.)
|
96
139
|
end
|
97
|
-
|
140
|
+
use.tap(&block)
|
98
141
|
|
99
|
-
|
100
|
-
font_size, bezier, section =
|
101
|
-
|
102
|
-
|
103
|
-
|
142
|
+
category_params = params_for(categories)
|
143
|
+
font_size, stroke_width, bezier, section = category_params.values_at "font-size", "stroke-width", "bezier", "section"
|
144
|
+
|
145
|
+
category_params.slice(*SVG_ATTRIBUTES).tap do |svg_attributes|
|
146
|
+
svg_attributes.slice(*FONT_SCALED_ATTRIBUTES).each do |key, value|
|
147
|
+
svg_attributes[key] = svg_attributes[key].to_i * font_size * 0.01 if /^\d+%$/ === value
|
148
|
+
end if font_size
|
149
|
+
use.add_attributes svg_attributes
|
150
|
+
end
|
104
151
|
|
105
152
|
features.each do |feature, _|
|
106
153
|
case feature
|
107
154
|
when GeoJSON::Point
|
108
155
|
symbol_id = [*ids, "symbol"].join(?.)
|
109
|
-
transform = "translate(%s) rotate(%s)" % [POINT, ANGLE] % [*feature.coordinates
|
110
|
-
content.add_element "use", "transform" => transform, "
|
156
|
+
transform = "translate(%s) rotate(%s)" % [POINT, ANGLE] % [*feature.coordinates, feature.fetch("rotation", @map.rotation) - @map.rotation]
|
157
|
+
content.add_element "use", "transform" => transform, "href" => "#%s" % symbol_id
|
111
158
|
|
112
159
|
when GeoJSON::LineString
|
113
|
-
linestring = feature.coordinates
|
160
|
+
linestring = feature.coordinates
|
114
161
|
(section ? linestring.in_sections(section) : [linestring]).each do |linestring|
|
115
162
|
content.add_element "path", "fill" => "none", "d" => svg_path_data(linestring, bezier: bezier)
|
116
163
|
end
|
117
164
|
|
118
165
|
when GeoJSON::Polygon
|
119
166
|
path_data = feature.coordinates.map do |ring|
|
120
|
-
svg_path_data ring
|
121
|
-
end.
|
167
|
+
svg_path_data ring, bezier: bezier
|
168
|
+
end.each.with_object("Z").entries.join(?\s)
|
122
169
|
content.add_element "path", "fill-rule" => "nonzero", "d" => path_data
|
123
170
|
|
124
171
|
when REXML::Element
|
@@ -132,28 +179,25 @@ module NSWTopo
|
|
132
179
|
end
|
133
180
|
end if content
|
134
181
|
|
135
|
-
|
182
|
+
category_params.each do |command, args|
|
136
183
|
next unless args
|
137
184
|
args = args.map(&:to_a).inject([], &:+) if Array === args && args.all?(Hash)
|
138
185
|
|
139
186
|
case command
|
140
187
|
when "blur"
|
141
188
|
filter_id = [*ids, "blur"].join(?.)
|
142
|
-
|
189
|
+
use.add_attribute "filter", "url(#%s)" % filter_id
|
143
190
|
defs.add_element("filter", "id" => filter_id).add_element "feGaussianBlur", "stdDeviation" => args, "in" => "SourceGraphic"
|
144
191
|
|
145
|
-
when "opacity"
|
146
|
-
if categories.none?
|
147
|
-
group.add_attribute "style", "opacity:#{args}"
|
148
|
-
else
|
149
|
-
container.add_attribute "opacity", args
|
150
|
-
end
|
151
|
-
|
152
192
|
when "symbol"
|
153
193
|
next unless content
|
154
194
|
symbol = defs.add_element "g", "id" => [*ids, "symbol"].join(?.)
|
155
195
|
args.each do |element, attributes|
|
156
|
-
|
196
|
+
if attributes
|
197
|
+
symbol.add_element element, attributes
|
198
|
+
else
|
199
|
+
symbol.add_element REXML::Document.new(element).root
|
200
|
+
end
|
157
201
|
end
|
158
202
|
|
159
203
|
when "pattern"
|
@@ -166,25 +210,25 @@ module NSWTopo
|
|
166
210
|
args.each do |element, attributes|
|
167
211
|
pattern.add_element element, attributes
|
168
212
|
end
|
169
|
-
|
213
|
+
use.add_attribute "fill", "url(#%s)" % pattern_id
|
170
214
|
|
171
215
|
when "symbolise"
|
172
216
|
next unless content
|
173
|
-
|
174
|
-
|
217
|
+
args, symbols = args.partition do |element, attributes|
|
218
|
+
%w[interval offset].include? element
|
175
219
|
end
|
176
|
-
interval = Hash[
|
220
|
+
interval, offset = Hash[args].values_at "interval", "offset"
|
177
221
|
symbol_ids = symbols.map.with_index do |(element, attributes), index|
|
178
222
|
symbol_id = [*ids, "symbol", index].join(?.).tap do |symbol_id|
|
179
223
|
defs.add_element("g", "id" => symbol_id).add_element(element, attributes)
|
180
224
|
end
|
181
225
|
end
|
182
226
|
lines_or_rings = features.grep(GeoJSON::LineString).map(&:coordinates)
|
183
|
-
lines_or_rings += features.grep(GeoJSON::Polygon).
|
227
|
+
lines_or_rings += features.grep(GeoJSON::Polygon).flat_map(&:coordinates)
|
184
228
|
lines_or_rings.each do |points|
|
185
|
-
points.
|
229
|
+
points.sample_at(interval, angle: true, offset: offset).each do |point, angle|
|
186
230
|
transform = "translate(%s) rotate(%s)" % [POINT, ANGLE] % [*point, 180.0 * angle / Math::PI]
|
187
|
-
content.add_element "use", "transform" => transform, "
|
231
|
+
content.add_element "use", "transform" => transform, "href" => "#%s" % symbol_ids.sample
|
188
232
|
end
|
189
233
|
end
|
190
234
|
|
@@ -195,66 +239,54 @@ module NSWTopo
|
|
195
239
|
args.each do |element, attributes|
|
196
240
|
symbol.add_element element, attributes
|
197
241
|
end
|
198
|
-
features.grep(GeoJSON::LineString).map do |
|
199
|
-
feature.coordinates.map(&to_mm)
|
200
|
-
end.each do |line|
|
242
|
+
features.grep(GeoJSON::LineString).map(&:coordinates).each do |line|
|
201
243
|
case command
|
202
244
|
when "inpoint" then [line.first(2)]
|
203
245
|
when "outpoint" then [line.last(2).rotate]
|
204
246
|
when "endpoint" then [line.first(2), line.last(2).rotate]
|
205
247
|
end.each do |segment|
|
206
|
-
transform = "translate(%s) rotate(%s)" % [POINT, ANGLE] % [*segment.first, 180.0 * segment.
|
207
|
-
|
248
|
+
transform = "translate(%s) rotate(%s)" % [POINT, ANGLE] % [*segment.first, 180.0 * segment.diff.angle / Math::PI]
|
249
|
+
use.add_element "use", "transform" => transform, "href" => "#%s" % symbol_id
|
208
250
|
end
|
209
251
|
end
|
210
252
|
|
211
|
-
when "
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
end
|
221
|
-
transforms = REXML::XPath.each(content, "ancestor::g[@transform]/@transform").map(&:value)
|
222
|
-
mask_contents.add_element "use", "xlink:href" => "#%s" % content.attributes["id"], "transform" => (transforms.join(?\s) if transforms.any?)
|
253
|
+
when "knockout"
|
254
|
+
use.add_attributes "mask" => "url(##{knockout})"
|
255
|
+
Knockout.new(use, *args).tap(&block)
|
256
|
+
|
257
|
+
when "preserve"
|
258
|
+
use.add_attributes "mask" => "none"
|
259
|
+
|
260
|
+
when "cutout", "mask" # mask deprecated
|
261
|
+
Cutout.new(use).tap(&block)
|
223
262
|
|
224
|
-
when "fence"
|
263
|
+
when "barrier", "fence" # fence deprecated
|
225
264
|
next unless content && args
|
226
|
-
buffer = 0.5 * (Numeric === args ? args :
|
227
|
-
features.each do |feature|
|
228
|
-
|
229
|
-
yield feature, buffer
|
265
|
+
buffer = 0.5 * (Numeric === args ? args : Numeric === stroke_width ? stroke_width : 0)
|
266
|
+
features.grep_v(REXML::Element).each do |feature|
|
267
|
+
Labels::Barrier.new(feature, buffer).tap(&block)
|
230
268
|
end
|
231
269
|
|
232
270
|
when "shield"
|
233
271
|
next unless content
|
234
272
|
content.elements.each("text") do |element|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
273
|
+
case
|
274
|
+
when text_length = element.elements["./ancestor-or-self::[@textLength]/@textLength"]&.value&.to_f
|
275
|
+
shield = REXML::Element.new("g")
|
276
|
+
width, height = text_length + SHIELD_X * font_size, (1 + SHIELD_Y) * font_size
|
277
|
+
shield.add_element "rect", "x" => -0.5 * width, "y" => -0.5 * height, "width" => width, "height" => height, "rx" => font_size * 0.3, "ry" => font_size * 0.3, "stroke" => "none", "fill" => args
|
278
|
+
text_transform = element.attributes.get_attribute "transform"
|
279
|
+
text_transform.remove
|
280
|
+
shield.attributes << text_transform
|
281
|
+
element.parent.elements << shield
|
282
|
+
shield << element
|
283
|
+
when href = element.elements["./textPath[@href]/@href"]&.value
|
284
|
+
shield = REXML::Element.new("g")
|
285
|
+
shield.add_element "use", "href" => href, "stroke-width" => (1 + SHIELD_Y) * font_size, "stroke" => args, "stroke-linecap" => "round"
|
286
|
+
element.parent.elements << shield
|
287
|
+
shield << element
|
288
|
+
end
|
244
289
|
end
|
245
|
-
|
246
|
-
when *SVG_ATTRIBUTES
|
247
|
-
container.add_attribute command, args
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
next categories, features, container
|
252
|
-
end.tap do |categorised|
|
253
|
-
params.fetch("order", []).reverse.map(&:split).map(&:to_set).each do |filter|
|
254
|
-
categorised.select do |categories, features, container|
|
255
|
-
filter <= categories
|
256
|
-
end.reverse.each do |categories, features, container|
|
257
|
-
group.unshift container.remove
|
258
290
|
end
|
259
291
|
end
|
260
292
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module NSWTopo
|
2
2
|
module Vegetation
|
3
|
-
include Raster, GDALGlob
|
4
|
-
CREATE = %w[mapping contrast
|
3
|
+
include Raster, MaskRender, GDALGlob
|
4
|
+
CREATE = %w[mapping contrast]
|
5
|
+
DEFAULTS = YAML.load <<~YAML
|
6
|
+
colour: hsl(75,55%,72%)
|
7
|
+
YAML
|
5
8
|
|
6
9
|
def get_raster(temp_dir)
|
7
|
-
|
8
|
-
vrt_path = temp_dir / "source.vrt"
|
9
|
-
|
10
|
+
@params["colour"] = @params["colour"]["woody"] if Hash === @params["colour"]
|
10
11
|
min, max = minmax = @mapping&.values_at("min", "max")
|
11
|
-
low, high, factor =
|
12
|
-
woody, nonwoody = { "woody" => "#A6F1A6", "non-woody" => "#FFFFFF" }.merge(@colour || {}).values_at("woody", "non-woody").map { |string| Colour.new string }
|
12
|
+
low, high, factor = [0, 100, 0].zip(Array @contrast&.values_at("low", "high", "factor")).map(&:compact).map(&:last)
|
13
13
|
|
14
|
-
|
14
|
+
alpha_table = (0..255).map do |index|
|
15
15
|
case
|
16
16
|
when minmax&.all?(Integer) && minmax.all?(0..255)
|
17
17
|
(100.0 * (index - min) / (max - min)).clamp(0.0, 100.0)
|
@@ -29,45 +29,50 @@ module NSWTopo
|
|
29
29
|
end.inject(&:-)
|
30
30
|
end.inject(&:/) # sigmoid between 0..1
|
31
31
|
end.map do |x|
|
32
|
-
|
32
|
+
Integer(255 * x)
|
33
33
|
end
|
34
34
|
|
35
35
|
Dir.chdir(@source ? @source.parent : Pathname.pwd) do
|
36
36
|
gdal_rasters @path
|
37
|
-
end.
|
38
|
-
raise "
|
37
|
+
end.each do |path, info|
|
38
|
+
raise "can't process vegetation data for #{@name}" unless info["bands"].one?
|
39
|
+
raise "can't process vegetation data for #{@name}" unless info.dig("bands", 0, "colorInterpretation") == "Palette"
|
40
|
+
raise "can't process vegetation data for #{@name}" unless info.dig("bands", 0, "colorTable", "count") == 256
|
41
|
+
end.group_by do |path, info|
|
42
|
+
info.dig("bands", 0).values_at("colorTable", "noDataValue")
|
43
|
+
end.values.then do |rasters, *others|
|
44
|
+
raise "no vegetation data file specified" unless rasters
|
45
|
+
raise "can't process vegetation data for #{@name}" if others.any?
|
46
|
+
rasters
|
39
47
|
end.group_by do |path, info|
|
40
48
|
Projection.new info.dig("coordinateSystem", "wkt")
|
41
49
|
end.map.with_index do |(projection, rasters), index|
|
42
|
-
|
43
|
-
|
44
|
-
coloured_tif_path = temp_dir / "coloured.#{index}.tif"
|
45
|
-
tif_path = temp_dir / "output.#{index}.tif"
|
50
|
+
vrt_path = temp_dir / "indexed.#{index}.vrt"
|
51
|
+
txt_path = temp_dir / "source.txt"
|
46
52
|
|
47
53
|
txt_path.write rasters.map(&:first).join(?\n)
|
48
|
-
OS.gdalbuildvrt "-overwrite", "-input_file_list", txt_path, vrt_path
|
49
|
-
OS.gdal_translate "-projwin", *@map.projwin(projection), "-r", "near", "-co", "TFW=YES", vrt_path, indexed_tif_path
|
50
|
-
OS.gdal_translate "-of", "VRT", indexed_tif_path, indexed_vrt_path
|
54
|
+
OS.gdalbuildvrt "-overwrite", "-r", "nearest", "-input_file_list", txt_path, vrt_path
|
51
55
|
|
52
|
-
xml = REXML::Document.new
|
53
|
-
|
54
|
-
|
55
|
-
xml.elements.collect("/VRTDataset/VRTRasterBand/ColorTable/Entry", &:itself).zip(colour_table) do |entry, colour|
|
56
|
-
entry.attributes["c1"], entry.attributes["c2"], entry.attributes["c3"], entry.attributes["c4"] = *colour.triplet, 255
|
56
|
+
xml = REXML::Document.new vrt_path.read
|
57
|
+
xml.elements.collect("/VRTDataset/VRTRasterBand/ColorTable/Entry", &:itself).zip(alpha_table) do |entry, alpha|
|
58
|
+
entry.attributes["c1"], entry.attributes["c2"], entry.attributes["c3"], entry.attributes["c4"] = alpha, alpha, alpha, 255
|
57
59
|
end
|
58
|
-
xml.elements.each("/VRTDataset/VRTRasterBand/NoDataValue", &:remove)
|
59
|
-
indexed_vrt_path.write xml
|
60
|
-
OS.gdal_translate "-expand", "rgb", indexed_vrt_path, coloured_tif_path
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
end.
|
65
|
-
|
66
|
-
|
67
|
-
OS.gdalbuildvrt "-overwrite", "-input_file_list", txt_path, vrt_path
|
68
|
-
end
|
61
|
+
vrt_path.write xml
|
62
|
+
vrt_path
|
63
|
+
end.then do |vrt_paths|
|
64
|
+
tif_path = temp_dir / "source.tif"
|
65
|
+
vrt_path = temp_dir / "source.vrt"
|
69
66
|
|
70
|
-
|
67
|
+
args = ["-t_srs", @map.projection, "-r", "nearest", "-cutline", "GeoJSON:/vsistdin/", "-crop_to_cutline"]
|
68
|
+
args += ["-tr", @mm_per_px, @mm_per_px] if @mm_per_px
|
69
|
+
OS.gdalwarp *args, *vrt_paths, tif_path do |stdin|
|
70
|
+
stdin.puts @map.cutline.to_json
|
71
|
+
end
|
72
|
+
OS.gdal_translate "-expand", "gray", "-a_nodata", "none", tif_path, vrt_path
|
73
|
+
|
74
|
+
return vrt_path
|
75
|
+
end
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
data/lib/nswtopo/layer.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
require_relative 'layer/raster_import'
|
1
2
|
require_relative 'layer/raster'
|
3
|
+
require_relative 'layer/raster_render'
|
4
|
+
require_relative 'layer/mask_render'
|
2
5
|
require_relative 'layer/vector'
|
3
6
|
require_relative 'layer/vegetation'
|
4
7
|
require_relative 'layer/import'
|
5
8
|
require_relative 'layer/arcgis_raster'
|
9
|
+
require_relative 'layer/colour_mask'
|
6
10
|
require_relative 'layer/feature'
|
7
11
|
require_relative 'layer/contour'
|
8
12
|
require_relative 'layer/spot'
|
@@ -15,19 +19,28 @@ require_relative 'layer/labels'
|
|
15
19
|
|
16
20
|
module NSWTopo
|
17
21
|
class Layer
|
18
|
-
TYPES = Set[Vegetation, Import, ArcGISRaster, Feature, Contour, Spot, Overlay, Relief, Grid, Declination, Control, Labels]
|
22
|
+
TYPES = Set[Vegetation, Import, ColourMask, ArcGISRaster, Feature, Contour, Spot, Overlay, Relief, Grid, Declination, Control, Labels]
|
19
23
|
|
20
24
|
def initialize(name, map, params)
|
25
|
+
params.delete("min-version").then do |creator_string|
|
26
|
+
creator_string ? Version[creator_string] : VERSION
|
27
|
+
rescue Version::Error
|
28
|
+
raise "layer '%s' has unrecognised version: %s" % [name, creator_string]
|
29
|
+
end.then do |min_version|
|
30
|
+
raise "layer '%s' requires nswtopo %s, this version: %s" % [name, min_version, VERSION] unless min_version <= VERSION
|
31
|
+
end
|
32
|
+
|
21
33
|
@type = begin
|
22
34
|
NSWTopo.const_get params["type"]
|
23
35
|
rescue NameError, TypeError
|
24
36
|
end
|
25
37
|
|
26
|
-
raise "unrecognised
|
38
|
+
raise "layer '%s' has unrecognised type: %s" % [name, params["type"].inspect] unless TYPES === @type
|
27
39
|
extend @type
|
28
40
|
|
29
|
-
@params = @type.const_defined?(:DEFAULTS) ? @type.const_get(:DEFAULTS).transform_keys(&:to_s).
|
30
|
-
@name, @map, @source, @path,
|
41
|
+
@params = @type.const_defined?(:DEFAULTS) ? @type.const_get(:DEFAULTS).transform_keys(&:to_s).deep_merge(params) : params
|
42
|
+
@name, @map, @source, @path, resolution, ppi = Layer.sanitise(name), map, @params.delete("source"), @params.delete("path"), @params.delete("resolution"), @params.delete("ppi")
|
43
|
+
@mm_per_px = ppi ? 25.4 / ppi : resolution ? @map.to_mm(resolution) : nil
|
31
44
|
|
32
45
|
@type.const_get(:CREATE).map(&:to_s).each do |attr|
|
33
46
|
instance_variable_set ?@ + attr.tr_s(?-, ?_), @params.delete(attr)
|
@@ -39,23 +52,24 @@ module NSWTopo
|
|
39
52
|
|
40
53
|
def level
|
41
54
|
case
|
42
|
-
when
|
43
|
-
when
|
44
|
-
when
|
45
|
-
when
|
46
|
-
when
|
47
|
-
when
|
48
|
-
when
|
49
|
-
when
|
50
|
-
when
|
51
|
-
when
|
52
|
-
when
|
55
|
+
when Import == @type then 0
|
56
|
+
when ArcGISRaster == @type then 0
|
57
|
+
when Vegetation == @type then 1
|
58
|
+
when ColourMask == @type then 2
|
59
|
+
when Feature == @type then 3
|
60
|
+
when Contour == @type then 3
|
61
|
+
when Spot == @type then 3
|
62
|
+
when Overlay == @type then 4
|
63
|
+
when Relief == @type then 5
|
64
|
+
when Grid == @type then 6
|
65
|
+
when Declination == @type then 7
|
66
|
+
when Control == @type then 8
|
53
67
|
when Labels == @type then 99
|
54
68
|
end
|
55
69
|
end
|
56
70
|
|
57
71
|
def <=>(other)
|
58
|
-
|
72
|
+
self.level <=> other.level
|
59
73
|
end
|
60
74
|
|
61
75
|
def ==(other)
|