nswtopo 3.0.1 → 3.1.1

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/bin/nswtopo +20 -4
  3. data/docs/contours.md +2 -0
  4. data/docs/relief.md +2 -3
  5. data/docs/spot-heights.md +2 -0
  6. data/lib/nswtopo/archive.rb +6 -3
  7. data/lib/nswtopo/chrome.rb +9 -6
  8. data/lib/nswtopo/commands/layers.rb +2 -2
  9. data/lib/nswtopo/config.rb +1 -0
  10. data/lib/nswtopo/formats/gemf.rb +1 -0
  11. data/lib/nswtopo/formats/kmz.rb +16 -10
  12. data/lib/nswtopo/formats/mbtiles.rb +1 -0
  13. data/lib/nswtopo/formats/pdf.rb +4 -3
  14. data/lib/nswtopo/formats/svg.rb +5 -13
  15. data/lib/nswtopo/formats/svgz.rb +1 -0
  16. data/lib/nswtopo/formats/zip.rb +5 -4
  17. data/lib/nswtopo/formats.rb +35 -36
  18. data/lib/nswtopo/geometry/r_tree.rb +24 -23
  19. data/lib/nswtopo/geometry/straight_skeleton/node.rb +4 -4
  20. data/lib/nswtopo/geometry/straight_skeleton/nodes.rb +51 -40
  21. data/lib/nswtopo/geometry/straight_skeleton/split.rb +2 -2
  22. data/lib/nswtopo/geometry/vector.rb +55 -49
  23. data/lib/nswtopo/geometry.rb +0 -5
  24. data/lib/nswtopo/gis/arcgis/layer/map.rb +11 -10
  25. data/lib/nswtopo/gis/arcgis/layer/query.rb +8 -10
  26. data/lib/nswtopo/gis/arcgis/layer.rb +7 -11
  27. data/lib/nswtopo/gis/dem.rb +3 -2
  28. data/lib/nswtopo/gis/gdal_glob.rb +3 -3
  29. data/lib/nswtopo/gis/geojson/collection.rb +60 -14
  30. data/lib/nswtopo/gis/geojson/line_string.rb +142 -1
  31. data/lib/nswtopo/gis/geojson/multi_line_string.rb +49 -7
  32. data/lib/nswtopo/gis/geojson/multi_point.rb +87 -0
  33. data/lib/nswtopo/gis/geojson/multi_polygon.rb +35 -23
  34. data/lib/nswtopo/gis/geojson/point.rb +16 -1
  35. data/lib/nswtopo/gis/geojson/polygon.rb +69 -7
  36. data/lib/nswtopo/gis/geojson.rb +92 -46
  37. data/lib/nswtopo/gis/projection.rb +5 -1
  38. data/lib/nswtopo/helpers/thread_pool.rb +39 -0
  39. data/lib/nswtopo/helpers.rb +44 -5
  40. data/lib/nswtopo/layer/arcgis_raster.rb +4 -6
  41. data/lib/nswtopo/layer/contour.rb +24 -26
  42. data/lib/nswtopo/layer/control.rb +5 -3
  43. data/lib/nswtopo/layer/declination.rb +14 -10
  44. data/lib/nswtopo/layer/feature.rb +5 -5
  45. data/lib/nswtopo/layer/grid.rb +19 -18
  46. data/lib/nswtopo/layer/labels/barriers.rb +23 -0
  47. data/lib/nswtopo/layer/labels/convex_hull.rb +12 -0
  48. data/lib/nswtopo/layer/labels/convex_hulls.rb +86 -0
  49. data/lib/nswtopo/layer/labels/label.rb +63 -0
  50. data/lib/nswtopo/layer/labels.rb +192 -315
  51. data/lib/nswtopo/layer/overlay.rb +11 -12
  52. data/lib/nswtopo/layer/raster.rb +1 -0
  53. data/lib/nswtopo/layer/relief.rb +6 -4
  54. data/lib/nswtopo/layer/spot.rb +11 -17
  55. data/lib/nswtopo/layer/{vector → vector_render}/cutout.rb +1 -1
  56. data/lib/nswtopo/layer/{vector → vector_render}/knockout.rb +2 -3
  57. data/lib/nswtopo/layer/{vector.rb → vector_render.rb} +20 -45
  58. data/lib/nswtopo/layer.rb +2 -1
  59. data/lib/nswtopo/map.rb +70 -56
  60. data/lib/nswtopo/svg.rb +5 -0
  61. data/lib/nswtopo/tiled_web_map.rb +3 -3
  62. data/lib/nswtopo/tree_indenter.rb +2 -2
  63. data/lib/nswtopo/version.rb +1 -1
  64. data/lib/nswtopo.rb +4 -0
  65. metadata +15 -17
  66. data/lib/nswtopo/geometry/overlap.rb +0 -47
  67. data/lib/nswtopo/geometry/segment.rb +0 -27
  68. data/lib/nswtopo/geometry/vector_sequence.rb +0 -180
  69. data/lib/nswtopo/helpers/array.rb +0 -19
  70. data/lib/nswtopo/helpers/concurrently.rb +0 -27
  71. data/lib/nswtopo/helpers/dir.rb +0 -7
  72. data/lib/nswtopo/helpers/hash.rb +0 -15
  73. data/lib/nswtopo/helpers/tar_writer.rb +0 -11
  74. data/lib/nswtopo/layer/labels/barrier.rb +0 -39
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nswtopo
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Hollingworth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-05 00:00:00.000000000 Z
11
+ date: 2023-11-21 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -59,9 +59,7 @@ files:
59
59
  - lib/nswtopo/formats/svgz.rb
60
60
  - lib/nswtopo/formats/zip.rb
61
61
  - lib/nswtopo/geometry.rb
62
- - lib/nswtopo/geometry/overlap.rb
63
62
  - lib/nswtopo/geometry/r_tree.rb
64
- - lib/nswtopo/geometry/segment.rb
65
63
  - lib/nswtopo/geometry/straight_skeleton.rb
66
64
  - lib/nswtopo/geometry/straight_skeleton/collapse.rb
67
65
  - lib/nswtopo/geometry/straight_skeleton/interior_node.rb
@@ -70,7 +68,6 @@ files:
70
68
  - lib/nswtopo/geometry/straight_skeleton/split.rb
71
69
  - lib/nswtopo/geometry/straight_skeleton/vertex.rb
72
70
  - lib/nswtopo/geometry/vector.rb
73
- - lib/nswtopo/geometry/vector_sequence.rb
74
71
  - lib/nswtopo/gis.rb
75
72
  - lib/nswtopo/gis/arcgis.rb
76
73
  - lib/nswtopo/gis/arcgis/connection.rb
@@ -87,6 +84,7 @@ files:
87
84
  - lib/nswtopo/gis/geojson/collection.rb
88
85
  - lib/nswtopo/gis/geojson/line_string.rb
89
86
  - lib/nswtopo/gis/geojson/multi_line_string.rb
87
+ - lib/nswtopo/gis/geojson/multi_point.rb
90
88
  - lib/nswtopo/gis/geojson/multi_polygon.rb
91
89
  - lib/nswtopo/gis/geojson/point.rb
92
90
  - lib/nswtopo/gis/geojson/polygon.rb
@@ -97,12 +95,8 @@ files:
97
95
  - lib/nswtopo/gis/shapefile.rb
98
96
  - lib/nswtopo/help_formatter.rb
99
97
  - lib/nswtopo/helpers.rb
100
- - lib/nswtopo/helpers/array.rb
101
98
  - lib/nswtopo/helpers/colour.rb
102
- - lib/nswtopo/helpers/concurrently.rb
103
- - lib/nswtopo/helpers/dir.rb
104
- - lib/nswtopo/helpers/hash.rb
105
- - lib/nswtopo/helpers/tar_writer.rb
99
+ - lib/nswtopo/helpers/thread_pool.rb
106
100
  - lib/nswtopo/layer.rb
107
101
  - lib/nswtopo/layer/arcgis_raster.rb
108
102
  - lib/nswtopo/layer/colour_mask.rb
@@ -113,7 +107,10 @@ files:
113
107
  - lib/nswtopo/layer/grid.rb
114
108
  - lib/nswtopo/layer/import.rb
115
109
  - lib/nswtopo/layer/labels.rb
116
- - lib/nswtopo/layer/labels/barrier.rb
110
+ - lib/nswtopo/layer/labels/barriers.rb
111
+ - lib/nswtopo/layer/labels/convex_hull.rb
112
+ - lib/nswtopo/layer/labels/convex_hulls.rb
113
+ - lib/nswtopo/layer/labels/label.rb
117
114
  - lib/nswtopo/layer/mask_render.rb
118
115
  - lib/nswtopo/layer/overlay.rb
119
116
  - lib/nswtopo/layer/raster.rb
@@ -121,21 +118,22 @@ files:
121
118
  - lib/nswtopo/layer/raster_render.rb
122
119
  - lib/nswtopo/layer/relief.rb
123
120
  - lib/nswtopo/layer/spot.rb
124
- - lib/nswtopo/layer/vector.rb
125
- - lib/nswtopo/layer/vector/cutout.rb
126
- - lib/nswtopo/layer/vector/knockout.rb
121
+ - lib/nswtopo/layer/vector_render.rb
122
+ - lib/nswtopo/layer/vector_render/cutout.rb
123
+ - lib/nswtopo/layer/vector_render/knockout.rb
127
124
  - lib/nswtopo/layer/vegetation.rb
128
125
  - lib/nswtopo/log.rb
129
126
  - lib/nswtopo/map.rb
130
127
  - lib/nswtopo/os.rb
131
128
  - lib/nswtopo/safely.rb
129
+ - lib/nswtopo/svg.rb
132
130
  - lib/nswtopo/tiled_web_map.rb
133
131
  - lib/nswtopo/tree_indenter.rb
134
132
  - lib/nswtopo/version.rb
135
133
  - lib/nswtopo/zip.rb
136
134
  homepage: https://github.com/mholling/nswtopo
137
135
  licenses:
138
- - GPL-3.0
136
+ - AGPL-3.0
139
137
  metadata: {}
140
138
  post_install_message:
141
139
  rdoc_options: []
@@ -145,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
145
143
  requirements:
146
144
  - - ">="
147
145
  - !ruby/object:Gem::Version
148
- version: 3.0.4
146
+ version: 3.1.4
149
147
  required_rubygems_version: !ruby/object:Gem::Requirement
150
148
  requirements:
151
149
  - - ">="
@@ -154,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
152
  requirements:
155
153
  - GDAL >= v3.4
156
154
  - Google Chrome >= v112
157
- rubygems_version: 3.2.33
155
+ rubygems_version: 3.3.26
158
156
  signing_key:
159
157
  specification_version: 4
160
158
  summary: A vector topographic mapping tool
@@ -1,47 +0,0 @@
1
- module Overlap
2
- def separated_by?(buffer)
3
- simplex = [map(&:first).inject(&:minus)]
4
- perp = simplex[0].perp
5
- loop do
6
- return false unless case
7
- when simplex.one? then simplex[0].norm
8
- when simplex.inject(&:minus).dot(simplex[1]) > 0 then simplex[1].norm
9
- when simplex.inject(&:minus).dot(simplex[0]) < 0 then simplex[0].norm
10
- else simplex.inject(&:cross).abs / simplex.inject(&:minus).norm
11
- end > buffer
12
- max = self[0].max_by { |point| perp.cross point }
13
- min = self[1].min_by { |point| perp.cross point }
14
- support = max.minus min
15
- return true unless simplex[0].minus(support).cross(perp) > 0
16
- rays = simplex.map { |point| point.minus support }
17
- case simplex.length
18
- when 1
19
- case
20
- when rays[0].dot(support) > 0
21
- simplex, perp = [support], support.perp
22
- when rays[0].cross(support) < 0
23
- simplex, perp = [support, *simplex], rays[0]
24
- else
25
- simplex, perp = [*simplex, support], rays[0].negate
26
- end
27
- when 2
28
- case
29
- when rays[0].cross(support) > 0 && rays[0].dot(support) < 0
30
- simplex, perp = [simplex[0], support], rays[0].negate
31
- when rays[1].cross(support) < 0 && rays[1].dot(support) < 0
32
- simplex, perp = [support, simplex[1]], rays[1]
33
- when rays[0].cross(support) <= 0 && rays[1].cross(support) >= 0
34
- return false
35
- else
36
- simplex, perp = [support], support.perp
37
- end
38
- end
39
- end
40
- end
41
-
42
- def overlap?(buffer)
43
- !separated_by?(buffer)
44
- end
45
- end
46
-
47
- Array.send :include, Overlap
@@ -1,27 +0,0 @@
1
- module Segment
2
- def segments
3
- each_cons(2).entries
4
- end
5
-
6
- def ring
7
- zip rotate
8
- end
9
-
10
- def diff
11
- last.minus first
12
- end
13
-
14
- def distance
15
- diff.norm
16
- end
17
-
18
- def along(fraction)
19
- self[1].times(fraction).plus self[0].times(1.0 - fraction)
20
- end
21
-
22
- def midpoint
23
- transpose.map(&:mean)
24
- end
25
- end
26
-
27
- Array.send :include, Segment
@@ -1,180 +0,0 @@
1
- module VectorSequence
2
- def perps
3
- ring.map(&:diff).map(&:perp)
4
- end
5
-
6
- def signed_area
7
- 0.5 * ring.map { |p1, p2| p1.cross p2 }.inject(&:+)
8
- end
9
-
10
- def clockwise?
11
- signed_area < 0
12
- end
13
- alias hole? clockwise?
14
-
15
- def anticlockwise?
16
- signed_area >= 0
17
- end
18
-
19
- def centroid
20
- ring.map do |p1, p2|
21
- (p1.plus p2).times(p1.cross p2)
22
- end.inject(&:plus) / (6.0 * signed_area)
23
- end
24
-
25
- def convex?
26
- ring.map(&:diff).ring.all? do |directions|
27
- directions.inject(&:cross) >= 0
28
- end
29
- end
30
-
31
- def surrounds?(points)
32
- points.all? do |point|
33
- point.within? self
34
- end
35
- end
36
-
37
- def convex_hull
38
- start = min_by(&:reverse)
39
- hull, remaining = uniq.partition { |point| point == start }
40
- remaining.sort_by do |point|
41
- [point.minus(start).angle, point.minus(start).norm]
42
- end.inject(hull) do |memo, p3|
43
- while memo.many? do
44
- p1, p2 = memo.last(2)
45
- (p3.minus p1).cross(p2.minus p1) < 0 ? break : memo.pop
46
- end
47
- memo << p3
48
- end
49
- end
50
-
51
- def minimum_bounding_box(*margins)
52
- polygon = convex_hull
53
- return polygon[0], [0, 0], 0 if polygon.one?
54
- indices = [%i[min_by max_by], [0, 1]].inject(:product).map do |min, axis|
55
- polygon.map.with_index.send(min) { |point, index| point[axis] }.last
56
- end
57
- calipers = [[0, -1], [1, 0], [0, 1], [-1, 0]]
58
- rotation = 0.0
59
- candidates = []
60
-
61
- while rotation < Math::PI / 2
62
- edges = indices.map do |index|
63
- polygon[(index + 1) % polygon.length].minus polygon[index]
64
- end
65
- angle, which = [edges, calipers].transpose.map do |edge, caliper|
66
- Math::acos caliper.proj(edge).clamp(-1, 1)
67
- end.map.with_index.min_by { |angle, index| angle }
68
-
69
- calipers.each { |caliper| caliper.rotate_by!(angle) }
70
- rotation += angle
71
-
72
- break if rotation >= Math::PI / 2
73
-
74
- dimensions = [0, 1].map do |offset|
75
- polygon[indices[offset + 2]].minus(polygon[indices[offset]]).proj(calipers[offset + 1])
76
- end
77
-
78
- centre = polygon.values_at(*indices).map do |point|
79
- point.rotate_by(-rotation)
80
- end.partition.with_index do |point, index|
81
- index.even?
82
- end.map.with_index do |pair, index|
83
- 0.5 * pair.map { |point| point[index] }.inject(:+)
84
- end.rotate_by(rotation)
85
-
86
- if rotation < Math::PI / 4
87
- candidates << [centre, dimensions, rotation]
88
- else
89
- candidates << [centre, dimensions.reverse, rotation - Math::PI / 2]
90
- end
91
-
92
- indices[which] += 1
93
- indices[which] %= polygon.length
94
- end
95
-
96
- candidates.min_by do |centre, dimensions, rotation|
97
- dimensions.zip(margins).map do |dimension, margin|
98
- margin ? dimension + 2 * margin : dimension
99
- end.inject(:*)
100
- end
101
- end
102
-
103
- def path_length
104
- segments.map(&:diff).sum(&:norm)
105
- end
106
-
107
- def trim(margin)
108
- start = [margin, 0].max
109
- stop = path_length - start
110
- return [] unless start < stop
111
- points, total = [], 0
112
- segments.each do |segment|
113
- distance = segment.distance
114
- case
115
- when total + distance <= start
116
- when total <= start
117
- points << segment.along((start - total) / distance)
118
- points << segment.along((stop - total) / distance) if total + distance >= stop
119
- else
120
- points << segment[0]
121
- points << segment.along((stop - total) / distance) if total + distance >= stop
122
- end
123
- total += distance
124
- break if total >= stop
125
- end
126
- points
127
- end
128
-
129
- def crop(length)
130
- trim(0.5 * (path_length - length))
131
- end
132
-
133
- def sample_at(interval, along: false, angle: false, offset: nil)
134
- Enumerator.new do |yielder|
135
- alpha = (0.5 + Float(offset || 0) / interval) % 1.0
136
- segments.inject [alpha, 0] do |(alpha, sum), segment|
137
- loop do
138
- fraction = alpha * interval / segment.distance
139
- break unless fraction < 1
140
- segment[0] = segment.along(fraction)
141
- sum += alpha * interval
142
- yielder << case
143
- when along then [segment[0], sum]
144
- when angle then [segment[0], segment.diff.angle]
145
- else segment[0]
146
- end
147
- alpha = 1.0
148
- end
149
- [alpha - segment.distance / interval, sum + segment.distance]
150
- end
151
- end.entries
152
- end
153
-
154
- def in_sections(count)
155
- segments.each_slice(count).map do |segments|
156
- segments.inject do |section, segment|
157
- section << segment[1]
158
- end
159
- end
160
- end
161
-
162
- def douglas_peucker(tolerance)
163
- chunks, simplified = [self], []
164
- while chunk = chunks.pop
165
- direction = chunk.last.minus(chunk.first).normalised
166
- deltas = chunk.map do |point|
167
- point.minus(chunk.first).cross(direction).abs
168
- end
169
- delta, index = deltas.each.with_index.max_by(&:first)
170
- if delta < tolerance
171
- simplified.prepend chunk.first
172
- else
173
- chunks << chunk[0..index] << chunk[index..-1]
174
- end
175
- end
176
- simplified << last
177
- end
178
- end
179
-
180
- Array.send :include, VectorSequence
@@ -1,19 +0,0 @@
1
- module ArrayHelpers
2
- def median
3
- sort[length / 2]
4
- end
5
-
6
- def mean
7
- empty? ? nil : inject(&:+) / length
8
- end
9
-
10
- def many?
11
- length > 1
12
- end
13
-
14
- def in_two
15
- each_slice(1 + [length - 1, 0].max / 2)
16
- end
17
- end
18
-
19
- Array.send :include, ArrayHelpers
@@ -1,27 +0,0 @@
1
- module Concurrently
2
- CORES = Etc.nprocessors rescue 1
3
-
4
- def concurrently(threads = CORES, &block)
5
- elements = Queue.new
6
- threads.times.map do
7
- Thread.new do
8
- while element = elements.pop
9
- block.call element
10
- end
11
- end
12
- end.tap do
13
- inject(elements, &:<<).close
14
- end.each(&:join)
15
- self
16
- end
17
-
18
- def concurrent_groups(threads = CORES, &block)
19
- group_by.with_index do |item, index|
20
- index % threads
21
- end.values.map do |items|
22
- Thread.new(items, &block)
23
- end.each(&:join)
24
- end
25
- end
26
-
27
- Enumerator.send :include, Concurrently
@@ -1,7 +0,0 @@
1
- class Dir
2
- def self.mktmppath
3
- mktmpdir do |path|
4
- yield Pathname.new(path)
5
- end
6
- end
7
- end
@@ -1,15 +0,0 @@
1
- module HashHelpers
2
- def deep_merge(other)
3
- merge(other) do |key, old_value, new_value|
4
- Hash === old_value ? Hash === new_value ? old_value.deep_merge(new_value) : new_value : new_value
5
- end
6
- end
7
-
8
- def deep_merge!(other)
9
- merge!(other) do |key, old_value, new_value|
10
- Hash === old_value ? Hash === new_value ? old_value.deep_merge!(new_value) : new_value : new_value
11
- end
12
- end
13
- end
14
-
15
- Hash.send :include, HashHelpers
@@ -1,11 +0,0 @@
1
- module TarWriterHelpers
2
- def add_entry(entry)
3
- check_closed
4
- @io.write entry.header
5
- @io.write entry.read
6
- @io.write ?\0 while @io.pos % 512 > 0
7
- self
8
- end
9
- end
10
-
11
- Gem::Package::TarWriter.send :include, TarWriterHelpers
@@ -1,39 +0,0 @@
1
- module NSWTopo
2
- module Labels
3
- class Barrier
4
- class Segment
5
- def initialize(segment, barrier)
6
- @segment, @barrier = segment, barrier
7
- @bounds = @segment.transpose.map(&:minmax).map do |min, max|
8
- [min - barrier.buffer, max + barrier.buffer]
9
- end
10
- end
11
- attr_reader :barrier, :bounds
12
-
13
- def conflicts_with?(segment, buffer: 0)
14
- [@segment, segment].overlap?(@barrier.buffer + buffer)
15
- end
16
- end
17
-
18
- def initialize(feature, buffer)
19
- @feature, @buffer = feature, buffer
20
- end
21
- attr_reader :buffer
22
-
23
- def segments
24
- case @feature
25
- when GeoJSON::Point
26
- [[@feature.coordinates] * 2]
27
- when GeoJSON::LineString
28
- @feature.coordinates.segments
29
- when GeoJSON::Polygon
30
- @feature.coordinates.flat_map do |coordinates|
31
- coordinates.segments
32
- end
33
- end.map do |segment|
34
- Segment.new segment, self
35
- end
36
- end
37
- end
38
- end
39
- end