nswtopo 2.0.0.pre.beta1 → 3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/COPYING +70 -83
- data/bin/nswtopo +227 -116
- data/docs/README.md +1 -12
- data/docs/add.md +1 -1
- data/docs/config.md +1 -1
- data/docs/contours.md +3 -1
- data/docs/init.md +8 -0
- data/docs/inspect.md +103 -0
- data/docs/move.md +9 -0
- data/docs/render.md +16 -7
- data/docs/scrape.md +67 -0
- data/docs/spot-heights.md +6 -2
- data/lib/nswtopo/archive.rb +50 -41
- data/lib/nswtopo/chrome.rb +227 -0
- data/lib/nswtopo/commands/add.rb +106 -0
- data/lib/nswtopo/commands/config.rb +38 -0
- data/lib/nswtopo/commands/inspect.rb +74 -0
- data/lib/nswtopo/commands/layers.rb +22 -0
- data/lib/nswtopo/commands/scrape.rb +79 -0
- data/lib/nswtopo/commands.rb +57 -0
- data/lib/nswtopo/dither.rb +5 -3
- data/lib/nswtopo/font.rb +46 -21
- data/lib/nswtopo/formats/gemf.rb +42 -0
- data/lib/nswtopo/formats/kmz.rb +26 -24
- data/lib/nswtopo/formats/mbtiles.rb +5 -41
- data/lib/nswtopo/formats/pdf.rb +82 -17
- data/lib/nswtopo/formats/svg.rb +114 -45
- data/lib/nswtopo/formats/svgz.rb +2 -2
- data/lib/nswtopo/formats/zip.rb +33 -23
- data/lib/nswtopo/formats.rb +77 -32
- data/lib/nswtopo/geometry/overlap.rb +1 -32
- data/lib/nswtopo/geometry/r_tree.rb +16 -10
- data/lib/nswtopo/geometry/segment.rb +3 -3
- data/lib/nswtopo/geometry/straight_skeleton/nodes.rb +5 -6
- data/lib/nswtopo/geometry/vector_sequence.rb +7 -6
- data/lib/nswtopo/gis/arcgis/connection.rb +56 -0
- data/lib/nswtopo/gis/arcgis/layer/map.rb +163 -0
- data/lib/nswtopo/gis/arcgis/layer/query.rb +87 -0
- data/lib/nswtopo/gis/arcgis/layer/renderer.rb +66 -0
- data/lib/nswtopo/gis/arcgis/layer/statistics.rb +15 -0
- data/lib/nswtopo/gis/arcgis/layer.rb +201 -0
- data/lib/nswtopo/gis/arcgis/service.rb +57 -0
- data/lib/nswtopo/gis/arcgis.rb +3 -0
- data/lib/nswtopo/gis/dem.rb +13 -12
- data/lib/nswtopo/gis/esri_hdr.rb +8 -2
- data/lib/nswtopo/gis/geojson/collection.rb +45 -21
- data/lib/nswtopo/gis/geojson/multi_line_string.rb +2 -24
- data/lib/nswtopo/gis/geojson/multi_polygon.rb +2 -53
- data/lib/nswtopo/gis/geojson/polygon.rb +15 -0
- data/lib/nswtopo/gis/geojson.rb +12 -3
- data/lib/nswtopo/gis/gps/kml.rb +25 -19
- data/lib/nswtopo/gis/gps.rb +2 -0
- data/lib/nswtopo/gis/projection.rb +35 -24
- data/lib/nswtopo/gis/shapefile.rb +89 -16
- data/lib/nswtopo/gis.rb +1 -2
- data/lib/nswtopo/helpers/array.rb +0 -11
- data/lib/nswtopo/helpers/colour.rb +34 -14
- data/lib/nswtopo/layer/arcgis_raster.rb +44 -48
- data/lib/nswtopo/layer/colour_mask.rb +5 -0
- data/lib/nswtopo/layer/contour.rb +35 -28
- data/lib/nswtopo/layer/control.rb +2 -7
- data/lib/nswtopo/layer/declination.rb +9 -9
- data/lib/nswtopo/layer/feature.rb +36 -22
- data/lib/nswtopo/layer/grid.rb +30 -27
- data/lib/nswtopo/layer/import.rb +1 -21
- data/lib/nswtopo/layer/labels/barrier.rb +39 -0
- data/lib/nswtopo/layer/labels.rb +551 -383
- data/lib/nswtopo/layer/mask_render.rb +37 -0
- data/lib/nswtopo/layer/overlay.rb +2 -2
- data/lib/nswtopo/layer/raster.rb +31 -41
- data/lib/nswtopo/layer/raster_import.rb +17 -0
- data/lib/nswtopo/layer/raster_render.rb +15 -0
- data/lib/nswtopo/layer/relief.rb +27 -95
- data/lib/nswtopo/layer/spot.rb +63 -62
- data/lib/nswtopo/layer/vector/cutout.rb +15 -0
- data/lib/nswtopo/layer/vector/knockout.rb +16 -0
- data/lib/nswtopo/layer/vector.rb +121 -89
- data/lib/nswtopo/layer/vegetation.rb +39 -34
- data/lib/nswtopo/layer.rb +30 -16
- data/lib/nswtopo/map.rb +202 -109
- data/lib/nswtopo/os.rb +5 -27
- data/lib/nswtopo/tiled_web_map.rb +54 -0
- data/lib/nswtopo/tree_indenter.rb +27 -0
- data/lib/nswtopo/version.rb +27 -2
- data/lib/nswtopo.rb +6 -199
- metadata +41 -22
- data/lib/nswtopo/font/chrome.rb +0 -59
- data/lib/nswtopo/font/generic.rb +0 -25
- data/lib/nswtopo/gis/arcgis_server/connection.rb +0 -52
- data/lib/nswtopo/gis/arcgis_server.rb +0 -155
- data/lib/nswtopo/gis/geojson/multi_point.rb +0 -12
- data/lib/nswtopo/gis/world_file.rb +0 -19
- data/lib/nswtopo/layer/labels/fence.rb +0 -20
data/lib/nswtopo/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)
|