Class: NSWTopo::GeoJSON::MultiPolygon

Inherits:
Object
  • Object
show all
Includes:
StraightSkeleton
Defined in:
lib/nswtopo/gis/geojson/multi_polygon.rb

Constant Summary

Constants included from StraightSkeleton

StraightSkeleton::DEFAULT_ROUNDING_ANGLE

Instance Method Summary collapse

Instance Method Details

#areaObject



6
7
8
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 6

def area
  @coordinates.flatten(1).sum(&:signed_area)
end

#buffer(*margins, **options) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 89

def buffer(*margins, **options)
  nodes = Nodes.new @coordinates.flatten(1)
  margins.each do |margin|
    nodes.progress limit: -margin, **options.slice(:rounding_angle, :cutoff_angle)
  end
  interior_rings, exterior_rings = nodes.readout.partition(&:hole?)
  polygons, foo = exterior_rings.sort_by(&:signed_area).inject [[], interior_rings] do |(polygons, interior_rings), exterior_ring|
    claimed, unclaimed = interior_rings.partition do |interior_ring|
      interior_ring.first.within? exterior_ring
    end
    [polygons << [exterior_ring, *claimed], unclaimed]
  end
  MultiPolygon.new polygons.entries, @properties
end

#centrelines(**options) ⇒ Object



85
86
87
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 85

def centrelines(**options)
  centres(**options, interval: nil, lines: true)
end

#centrepoints(interval:, **options) ⇒ Object



81
82
83
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 81

def centrepoints(interval:, **options)
  centres(**options, interval: interval, lines: false)
end

#centres(fraction: 0.5, min_width: nil, interval:, lines: true) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 18

def centres(fraction: 0.5, min_width: nil, interval:, lines: true)
  neighbours = Hash.new { |neighbours, node| neighbours[node] = [] }
  samples, tails, node1 = {}, {}, nil

  Nodes.new(@coordinates.flatten(1)).progress(interval: interval) do |event, *args|
    case event
    when :nodes
      node0, node1 = *args
      neighbours[node0] << node1
      neighbours[node1] << node0
    when :interval
      travel, rings = *args
      samples[travel] = rings.flat_map do |ring|
        ring.sample_at interval
      end
    end
  end

  samples[node1.travel] = [node1.point.to_f]
  max_travel = neighbours.keys.map(&:travel).max
  min_travel = [fraction * max_travel, min_width && 0.5 * min_width].compact.max

  features = samples.select do |travel, points|
    travel > min_travel
  end.map do |travel, points|
    MultiPoint.new points, @properties
  end.reverse
  return features unless lines

  loop do
    break unless neighbours.reject do |node, (neighbour, *others)|
      others.any? || neighbours[neighbour].one?
    end.each do |node, (neighbour, *)|
      next if neighbours[neighbour].one?
      neighbours.delete node
      neighbours[neighbour].delete node
      nodes, length = tails.delete(node) || [[node], 0]
      candidate = [nodes << neighbour, length + [node.point, neighbour.point].distance]
      tails[neighbour] = [tails[neighbour], candidate].compact.max_by(&:last)
    end.any?
  end

  lengths, lines, candidates = Hash.new(0), Hash.new, tails.values
  while candidates.any?
    (*nodes, node), length = candidates.pop
    next if (neighbours[node] - nodes).each do |neighbour|
      candidates << [[*nodes, node, neighbour], length + [node.point, neighbour.point].distance]
    end.any?
    index = nodes.find(&:index).index
    tail_nodes, tail_length = tails[node] || [[node], 0]
    lengths[index], lines[index] = length + tail_length, nodes + tail_nodes.reverse if length + tail_length > lengths[index]
  end

  linestrings = lines.values.flat_map do |nodes|
    nodes.chunk do |node|
      node.travel >= min_travel
    end.select(&:first).map(&:last).reject(&:one?).map do |nodes|
      nodes.map(&:point).to_f
    end
  end
  features.prepend MultiLineString.new(linestrings, @properties)
end

#centroidsObject



104
105
106
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 104

def centroids
  MultiPoint.new @coordinates.map(&:first).map(&:centroid), @properties
end

#samples(interval) ⇒ Object



108
109
110
111
112
113
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 108

def samples(interval)
  points = @coordinates.flatten(1).flat_map do |ring|
    ring.sample_at interval
  end
  MultiPoint.new points, @properties
end

#skeletonObject



10
11
12
13
14
15
16
# File 'lib/nswtopo/gis/geojson/multi_polygon.rb', line 10

def skeleton
  segments = []
  Nodes.new(@coordinates.flatten(1)).progress do |event, node0, node1|
    segments << [node0.point, node1.point].to_f
  end
  MultiLineString.new segments, @properties
end