nswtopo 3.0.1 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/bin/nswtopo +19 -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 +4 -4
  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 +59 -13
  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 +3 -3
  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 +56 -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'
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-09-04 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