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
@@ -0,0 +1,86 @@
1
+ module NSWTopo
2
+ module Labels
3
+ class ConvexHulls < GeoJSON::MultiLineString
4
+ def initialize(feature, buffer, &block)
5
+ coordinates = case feature
6
+ when GeoJSON::Polygon then feature.rings
7
+ when GeoJSON::MultiPolygon then feature.rings
8
+ else feature
9
+ end.explode.flat_map do |feature|
10
+ case feature
11
+ when GeoJSON::Point # a point feature barrier
12
+ x, y = *feature
13
+ [[Vector[x-buffer, y-buffer], Vector[x+buffer, y-buffer], Vector[x+buffer, y+buffer], Vector[x-buffer, y+buffer]]]
14
+ when GeoJSON::LineString # a linestring label to be broken down into segment hulls
15
+ offsets = feature.each_cons(2).map do |p0, p1|
16
+ (p1 - p0).perp.normalised * buffer
17
+ end
18
+ corners = offsets.then do |offsets|
19
+ feature.closed? ? [offsets.last, *offsets, offsets.first] : [offsets.first, *offsets, offsets.last]
20
+ end.each_cons(2).map do |o01, o12|
21
+ next if o12.cross(o01) == 0
22
+ (o01 + o12).normalised * buffer * (o12.cross(o01) <=> 0)
23
+ end.each_cons(2)
24
+ feature.each_cons(2).zip(corners, offsets).map do |(p0, p1), (c0, c1), offset|
25
+ if c0 then [p0 + offset, p0 + c0, p0 - offset] else [p0 + offset, p0 - offset] end +
26
+ if c1 then [p1 - offset, p1 + c1, p1 + offset] else [p1 - offset, p1 + offset] end
27
+ end
28
+ end
29
+ end
30
+ super coordinates, &block
31
+ end
32
+
33
+ delegate :length => :@coordinates
34
+
35
+ def each(&block)
36
+ enum = Enumerator.new do |yielder|
37
+ @coordinates.each do |coordinates|
38
+ yielder << ConvexHull.new(self, coordinates)
39
+ end
40
+ end
41
+ block_given? ? enum.each(&block) : enum
42
+ end
43
+
44
+ def self.overlap?(ring0, ring1, buffer)
45
+ # implements Gilbert–Johnson–Keerthi
46
+ simplex = [ring0.first - ring1.first]
47
+ perp = simplex[0].perp
48
+ loop do
49
+ return true unless case
50
+ when simplex.one? then simplex[0].norm
51
+ when simplex.inject(&:-).dot(simplex[1]) > 0 then simplex[1].norm
52
+ when simplex.inject(&:-).dot(simplex[0]) < 0 then simplex[0].norm
53
+ else simplex.inject(&:cross).abs / simplex.inject(&:-).norm
54
+ end > buffer
55
+ max = ring0.max_by { |point| perp.cross point }
56
+ min = ring1.min_by { |point| perp.cross point }
57
+ support = max - min
58
+ return false unless (simplex[0] - support).cross(perp) > 0
59
+ rays = simplex.map { |point| point - support }
60
+ case simplex.length
61
+ when 1
62
+ case
63
+ when rays[0].dot(support) > 0
64
+ simplex, perp = [support], support.perp
65
+ when rays[0].cross(support) < 0
66
+ simplex, perp = [support, *simplex], rays[0]
67
+ else
68
+ simplex, perp = [*simplex, support], -rays[0]
69
+ end
70
+ when 2
71
+ case
72
+ when rays[0].cross(support) > 0 && rays[0].dot(support) < 0
73
+ simplex, perp = [simplex[0], support], -rays[0]
74
+ when rays[1].cross(support) < 0 && rays[1].dot(support) < 0
75
+ simplex, perp = [support, simplex[1]], rays[1]
76
+ when rays[0].cross(support) <= 0 && rays[1].cross(support) >= 0
77
+ return true
78
+ else
79
+ simplex, perp = [support], support.perp
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,63 @@
1
+ module NSWTopo
2
+ module Labels
3
+ class Label < ConvexHulls
4
+ def self.knockout(value)
5
+ Float(Numeric === value ? value : value ? Config.fetch("knockout", 0.3) : 0)
6
+ end
7
+
8
+ def initialize(baselines, feature, priority, elements, along: nil, fixed: nil, &block)
9
+ super baselines, 0.5 * feature["font-size"] do
10
+ @categories, @text, @dual, @layer_name, @label_index, @feature_index = feature.values_at(:categories, :text, :dual, :layer_name, :label_index, :feature_index)
11
+ @indices = [@label_index, @feature_index]
12
+ @priority, @elements, @along, @fixed = priority, elements, along, fixed
13
+ @hull = dissolve_points.convex_hull
14
+ @optional, @coexist, knockout = feature.values_at("optional", "coexist", "knockout")
15
+ @barrier_count = each.with_object(Label.knockout(knockout)).map(&block).inject(&:merge).size
16
+ @ordinal = [@barrier_count, @priority]
17
+ @separation = feature.fetch("separation", {})
18
+ end
19
+ end
20
+
21
+ attr_reader :categories, :text, :dual, :layer_name, :label_index, :feature_index, :indices
22
+ attr_reader :priority, :elements, :along, :fixed, :hull, :barrier_count, :ordinal, :separation
23
+
24
+ def point?
25
+ @along.nil?
26
+ end
27
+
28
+ def barriers?
29
+ @barrier_count > 0
30
+ end
31
+
32
+ def optional?
33
+ @optional
34
+ end
35
+
36
+ def coexists_with?(other)
37
+ Array(@coexist).include? other.layer_name
38
+ end
39
+
40
+ def <=>(other)
41
+ self.ordinal <=> other.ordinal
42
+ end
43
+
44
+ def self.overlaps(labels, group = labels, &block)
45
+ return Set[] unless group.any?(&block)
46
+ index = RTree.load(labels.flat_map(&:explode), &:bounds)
47
+ group.each.with_object Set[] do |label, overlaps|
48
+ next unless buffer = yield(label)
49
+ index.search(label.bounds, buffer).each do |other|
50
+ next if label == other.source
51
+ next if overlaps === [label, other.source]
52
+ next if overlaps === [other.source, label]
53
+ next unless label.length < 3 || ConvexHulls.overlap?(label.hull, other, buffer)
54
+ next unless label.any? do |hull|
55
+ ConvexHulls.overlap?(hull, other, buffer)
56
+ end
57
+ overlaps << [label, other.source]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end