Class: NSWTopo::GeoJSON::MultiPoint

Inherits:
Object
  • Object
show all
Defined in:
lib/nswtopo/gis/geojson/multi_point.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.[](coordinates, properties = nil, &block) ⇒ Object



4
5
6
7
8
9
10
11
# File 'lib/nswtopo/gis/geojson/multi_point.rb', line 4

def self.[](coordinates, properties = nil, &block)
  new(coordinates, properties) do
    @coordinates.map! do |point|
      Vector === point ? point : Vector[*point]
    end
    block.call self if block_given?
  end
end

Instance Method Details

#boundsObject



13
14
15
# File 'lib/nswtopo/gis/geojson/multi_point.rb', line 13

def bounds
  @coordinates.transpose.map(&:minmax)
end

#convex_hullObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/nswtopo/gis/geojson/multi_point.rb', line 26

def convex_hull
  start = @coordinates.min
  points, remaining = @coordinates.partition { |point| point == start }
  remaining.sort_by do |point|
    next (point - start).angle, (point - start).norm
  end.inject(points) do |points, p2|
    while points.length > 1 do
      p0, p1 = points.last(2)
      (p2 - p0).cross(p1 - p0) < 0 ? break : points.pop
    end
    points << p2
  end.then do |points|
    LineString.new points, @properties
  end
end

#minimum_bbox_angle(*margins) ⇒ Object



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
80
81
82
83
84
# File 'lib/nswtopo/gis/geojson/multi_point.rb', line 42

def minimum_bbox_angle(*margins)
  ring = convex_hull.coordinates
  return 0 if ring.one?
  indices = [%i[min_by max_by], %i[x y]].inject(:product).map do |min, coord|
    ring.map(&coord).each.with_index.send(min, &:first).last
  end
  calipers = [Vector[0, -1], Vector[1, 0], Vector[0, 1], Vector[-1, 0]]
  rotation = 0.0
  candidates = []

  while rotation < Math::PI / 2
    edges = indices.map do |index|
      ring[(index + 1) % ring.length] - ring[index]
    end
    angle, which = [edges, calipers].transpose.map do |edge, caliper|
      Math::acos caliper.proj(edge).clamp(-1, 1)
    end.map.with_index.min_by(&:first)

    calipers.map! { |caliper| caliper.rotate_by(angle) }
    rotation += angle

    break if rotation >= Math::PI / 2

    dimensions = [0, 1].map do |offset|
      (ring[indices[offset + 2]] - ring[indices[offset]]).proj(calipers[offset + 1])
    end

    if rotation < Math::PI / 4
      candidates << [dimensions, rotation]
    else
      candidates << [dimensions.reverse, rotation - Math::PI / 2]
    end

    indices[which] += 1
    indices[which] %= ring.length
  end

  candidates.min_by do |dimensions, rotation|
    dimensions.zip(margins).map do |dimension, margin|
      margin ? dimension + 2 * margin : dimension
    end.inject(:*)
  end.last
end

#rotate_by_degrees(angle) ⇒ Object



19
20
21
22
23
24
# File 'lib/nswtopo/gis/geojson/multi_point.rb', line 19

def rotate_by_degrees(angle)
  rotated = @coordinates.map do |point|
    point.rotate_by_degrees(angle)
  end
  MultiPoint.new rotated, @properties
end