class NSWTopo::GeoJSON::MultiPolygon
def area
def area rings.sum(&:signed_area) end
def buffer(*margins, **options)
def buffer(*margins, **options) rings.offset(*margins.map(&:-@), **options).to_multipolygon end
def centrelines(**options)
def centrelines(**options) centres(**options, interval: nil, lines: true) end
def centrepoints(interval:, **options)
def centrepoints(interval:, **options) centres(**options, interval: interval, lines: false) end
def centres(fraction: 0.5, min_width: nil, interval:, lines: true)
def centres(fraction: 0.5, min_width: nil, interval:, lines: true) neighbours = Hash.new { |neighbours, node| neighbours[node] = [] } samples, tails, node1 = {}, {}, nil nodes.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| LineString.new(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).norm] 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).norm] 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).map(&:to_f) end end features.prepend MultiLineString.new(linestrings, @properties) end
def centroids
def centroids map(&:centroid).inject(empty_points, &:+) end
def dissolve_points
def dissolve_points MultiPoint.new @coordinates.flatten(2), @properties end
def freeze!
def freeze! each { } freeze end
def nodes
def nodes Nodes.new rings end
def remove_holes(&block)
def remove_holes(&block) map do |polygon| polygon.remove_holes(&block) end.inject(empty_polygons, &:+) end
def rings
def rings MultiLineString.new @coordinates.flatten(1), @properties end
def samples(interval)
def samples(interval) points = rings.flat_map do |coordinates| linestring.sample_at(interval) end MultiPoint.new points, @properties end
def skeleton
def skeleton segments = [] nodes.progress do |event, node0, node1| segments << [node0.point.to_f, node1.point.to_f] end MultiLineString.new segments, @properties end