class NSWTopo::GeoJSON::MultiPoint

def self.[](coordinates, properties = nil, &block)

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

def bounds

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

def convex_hull

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

def minimum_bbox_angle(*margins)

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

def rotate_by_degrees(angle)

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