class Geos::CoordinateSequence

A CoordinateSequence is a list of coordinates in a Geometry.

Attributes

ptr[R]
x[R]
y[R]
z[R]

Public Class Methods

new(ptr, auto_free = true, parent = nil) click to toggle source
new(size = 0, dimensions = 0)
new(options)
new(points)

The ptr version of the initializer is for internal use.

new(points) will try to glean the size and dimensions of your CoordinateSequence from an Array of points. The Array should contain uniform-sized Arrays which represent the [ x, y, z ] values of your coordinates.

# File lib/ffi-geos/coordinate_sequence.rb, line 57
def initialize(*args)
  points = nil # forward declaration we can use later

  ptr, auto_free, parent = if args.first.is_a?(FFI::Pointer)
    args.first(3)
  else
    size, dimensions = if args.first.is_a?(Array)
      points = if args.first.first.is_a?(Array)
        args.first
      else
        args
      end
      lengths = points.collect(&:length).uniq

      if lengths.empty?
        [0, 0]
      elsif lengths.length != 1
        raise ParseError, 'Different sized points found in Array'
      elsif !lengths.first.between?(1, 3)
        raise ParseError, 'Expected points to contain 1-3 elements'
      else
        [points.length, points.first.length]
      end
    elsif args.first.is_a?(Hash)
      args.first.values_at(:size, :dimensions)
    elsif !args.length.between?(0, 2)
      raise ArgumentError, "wrong number of arguments (#{args.length} for 0-2)"
    else
      [args[0], args[1]]
    end

    size ||= 0
    dimensions ||= 0

    [FFIGeos.GEOSCoordSeq_create_r(Geos.current_handle_pointer, size, dimensions), true]
  end

  @ptr = FFI::AutoPointer.new(
    ptr,
    self.class.method(:release)
  )

  @ptr.autorelease = auto_free
  @parent = parent if parent

  @x = CoordinateAccessor.new(self, 0)
  @y = CoordinateAccessor.new(self, 1)
  @z = CoordinateAccessor.new(self, 2)

  return unless points

  points.each_with_index do |point, idx|
    point.each_with_index do |val, dim|
      set_ordinate(idx, dim, val)
    end
  end
end

Public Instance Methods

[](*args) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 145
def [](*args)
  if args.length == 1 && args.first.is_a?(Numeric) && args.first >= 0
    i = args.first
    ary = [get_x(i), get_y(i)]
    ary << get_z(i) if has_z?
    ary
  else
    to_a[*args]
  end
end
Also aliased as: slice
affine(options) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 374
def affine(options)
  dup.affine!(options)
end
affine!(options) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 348
def affine!(options)
  options.default = 0.0

  if has_z?
    length.times do |i|
      x = self.x[i]
      y = self.y[i]
      z = self.z[i]

      self.x[i] = (options[:afac] * x) + (options[:bfac] * y) + (options[:cfac] * z) + options[:xoff]
      self.y[i] = (options[:dfac] * x) + (options[:efac] * y) + (options[:ffac] * z) + options[:yoff]
      self.z[i] = (options[:gfac] * x) + (options[:hfac] * y) + (options[:ifac] * z) + options[:zoff]
    end
  else
    length.times do |i|
      x = self.x[i]
      y = self.y[i]

      self.x[i] = (options[:afac] * x) + (options[:bfac] * y) + options[:xoff]
      self.y[i] = (options[:dfac] * x) + (options[:efac] * y) + options[:yoff]
    end
  end

  self
end
ccw?()
Alias for: counter_clockwise?
counter_clockwise?() click to toggle source

Available in GEOS 3.7+.

# File lib/ffi-geos/coordinate_sequence.rb, line 239
def counter_clockwise?
  char_ptr = FFI::MemoryPointer.new(:char)
  FFIGeos.GEOSCoordSeq_isCCW_r(Geos.current_handle_pointer, ptr, char_ptr)
  Tools.bool_result(char_ptr.read_char)
end
Also aliased as: ccw?
dimensions() click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 227
def dimensions
  if defined?(@dimensions)
    @dimensions
  else
    int_ptr = FFI::MemoryPointer.new(:int)
    FFIGeos.GEOSCoordSeq_getDimensions_r(Geos.current_handle_pointer, ptr, int_ptr)
    @dimensions = int_ptr.read_int
  end
end
each() { |build_coordinate(n)| ... } click to toggle source

Yields coordinates as [ x, y, z ]. The z coordinate may be omitted for 2-dimensional CoordinateSequences.

# File lib/ffi-geos/coordinate_sequence.rb, line 132
def each
  if block_given?
    length.times do |n|
      yield build_coordinate(n)
    end
    self
  else
    length.times.collect { |n|
      build_coordinate(n)
    }.to_enum
  end
end
empty?() click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 223
def empty?
  length.zero?
end
get_ordinate(idx, dim) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 209
def get_ordinate(idx, dim)
  check_bounds(idx)
  double_ptr = FFI::MemoryPointer.new(:double)
  FFIGeos.GEOSCoordSeq_getOrdinate_r(Geos.current_handle_pointer, ptr, idx, dim, double_ptr)
  double_ptr.read_double
end
get_x(idx) click to toggle source

Gets the x value of a coordinate. Can also be retrieved via x[].

# File lib/ffi-geos/coordinate_sequence.rb, line 186
def get_x(idx)
  check_bounds(idx)
  double_ptr = FFI::MemoryPointer.new(:double)
  FFIGeos.GEOSCoordSeq_getX_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
  double_ptr.read_double
end
get_y(idx) click to toggle source

Gets the y value of a coordinate. Can also be retrieved via y[].

# File lib/ffi-geos/coordinate_sequence.rb, line 194
def get_y(idx)
  check_bounds(idx)
  double_ptr = FFI::MemoryPointer.new(:double)
  FFIGeos.GEOSCoordSeq_getY_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
  double_ptr.read_double
end
get_z(idx) click to toggle source

Gets the z value of a coordinate. Can also be retrieved via z[].

# File lib/ffi-geos/coordinate_sequence.rb, line 202
def get_z(idx)
  check_bounds(idx)
  double_ptr = FFI::MemoryPointer.new(:double)
  FFIGeos.GEOSCoordSeq_getZ_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
  double_ptr.read_double
end
has_z?()
Alias for: z?
initialize_copy(source) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 115
def initialize_copy(source)
  @ptr = FFI::AutoPointer.new(
    FFIGeos.GEOSCoordSeq_clone_r(Geos.current_handle_pointer, source.ptr),
    self.class.method(:release)
  )

  @x = CoordinateAccessor.new(self, 0)
  @y = CoordinateAccessor.new(self, 1)
  @z = CoordinateAccessor.new(self, 2)
end
length() click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 216
def length
  int_ptr = FFI::MemoryPointer.new(:int)
  FFIGeos.GEOSCoordSeq_getSize_r(Geos.current_handle_pointer, ptr, int_ptr)
  int_ptr.read_int
end
Also aliased as: size
remove_duplicate_coords() click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 342
def remove_duplicate_coords
  Geos::CoordinateSequence.new(to_a.each_with_object([]) do |v, memo|
    memo << v unless memo.last == v
  end)
end
rotate(radians, origin = [0.0, 0.0]) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 405
def rotate(radians, origin = [0.0, 0.0])
  dup.rotate!(radians, origin)
end
rotate!(radians, origin = [0.0, 0.0]) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 378
def rotate!(radians, origin = [0.0, 0.0])
  origin = case origin
    when Array
      origin
    when Geos::Geometry
      center = origin.centroid
      [center.x, center.y]
    else
      raise ArgumentError, 'Expected an Array or a Geos::Geometry for the origin'
  end

  affine!(
    afac: Math.cos(radians),
    bfac: -Math.sin(radians),
    cfac: 0,
    dfac: Math.sin(radians),
    efac: Math.cos(radians),
    ffac: 0,
    gfac: 0,
    hfac: 0,
    ifac: 1,
    xoff: origin[0] - (Math.cos(radians) * origin[0]) + (Math.sin(radians) * origin[1]),
    yoff: origin[1] - (Math.sin(radians) * origin[0]) - (Math.cos(radians) * origin[1]),
    zoff: 0
  )
end
rotate_x(radians) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 426
def rotate_x(radians)
  dup.rotate_x!(radians)
end
rotate_x!(radians) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 409
def rotate_x!(radians)
  affine!(
    afac: 1,
    bfac: 0,
    cfac: 0,
    dfac: 0,
    efac: Math.cos(radians),
    ffac: -Math.sin(radians),
    gfac: 0,
    hfac: Math.sin(radians),
    ifac: Math.cos(radians),
    xoff: 0,
    yoff: 0,
    zoff: 0
  )
end
rotate_y(radians) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 447
def rotate_y(radians)
  dup.rotate_y!(radians)
end
rotate_y!(radians) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 430
def rotate_y!(radians)
  affine!(
    afac: Math.cos(radians),
    bfac: 0,
    cfac: Math.sin(radians),
    dfac: 0,
    efac: 1,
    ffac: 0,
    gfac: -Math.sin(radians),
    hfac: 0,
    ifac: Math.cos(radians),
    xoff: 0,
    yoff: 0,
    zoff: 0
  )
end
rotate_z(radians) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 455
def rotate_z(radians)
  dup.rotate!(radians)
end
rotate_z!(radians) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 451
def rotate_z!(radians)
  rotate!(radians)
end
scale(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 484
def scale(*args, **kwargs)
  dup.scale!(*args, **kwargs)
end
scale!(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 459
def scale!(*args, **kwargs)
  x, y, z = if !kwargs.empty?
    kwargs.values_at(:x, :y, :z)
  elsif args.length.between?(1, 3)
    args.values_at(0...3)
  else
    raise ArgumentError, "Wrong number of arguments #{args.length} for 1-3"
  end

  affine!(
    afac: x || 1,
    bfac: 0,
    cfac: 0,
    dfac: 0,
    efac: y || 1,
    ffac: 0,
    gfac: 0,
    hfac: 0,
    ifac: z || 1,
    xoff: 0,
    yoff: 0,
    zoff: 0
  )
end
set_ordinate(idx, dim, val) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 180
def set_ordinate(idx, dim, val)
  check_bounds(idx)
  FFIGeos.GEOSCoordSeq_setOrdinate_r(Geos.current_handle_pointer, ptr, idx, dim, val.to_f)
end
set_x(idx, val) click to toggle source

Sets the x value of a coordinate. Can also be set via x[]=.

# File lib/ffi-geos/coordinate_sequence.rb, line 163
def set_x(idx, val)
  check_bounds(idx)
  FFIGeos.GEOSCoordSeq_setX_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
end
set_y(idx, val) click to toggle source

Sets the y value of a coordinate. Can also be set via y[]=.

# File lib/ffi-geos/coordinate_sequence.rb, line 169
def set_y(idx, val)
  check_bounds(idx)
  FFIGeos.GEOSCoordSeq_setY_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
end
set_z(idx, val) click to toggle source

Sets the z value of a coordinate. Can also be set via z[]=.

# File lib/ffi-geos/coordinate_sequence.rb, line 175
def set_z(idx, val)
  check_bounds(idx)
  FFIGeos.GEOSCoordSeq_setZ_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
end
size()
Alias for: length
slice(*args)
Alias for: []
snap_to_grid(*args, **) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 338
def snap_to_grid(*args, **)
  dup.snap_to_grid!(*args)
end
snap_to_grid!(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 291
def snap_to_grid!(*args, **kwargs)
  grid = {
    offset_x: 0, # 1
    offset_y: 0, # 2
    offset_z: 0, # -
    size_x: 0, # 3
    size_y: 0, # 4
    size_z: 0 # -
  }

  if args.length == 1 && args[0].is_a?(Numeric)
    grid[:size_x] = grid[:size_y] = grid[:size_z] = args[0]
  elsif !kwargs.empty?
    grid.merge!(kwargs)
  end

  grid[:size_x] = grid[:size_y] = grid[:size_z] = grid[:size] if grid[:size]

  if grid[:offset]
    case grid[:offset]
      when Geos::Geometry
        point = grid[:offset].centroid

        grid[:offset_x] = point.x
        grid[:offset_y] = point.y
        grid[:offset_z] = point.z
      when Array
        grid[:offset_x], grid[:offset_y], grid[:offset_z] = grid[:offset]
      else
        raise ArgumentError, 'Expected :offset option to be a Geos::Point'
    end
  end

  length.times do |i|
    x[i] = (((x[i] - grid[:offset_x]) / grid[:size_x]).round * grid[:size_x]) + grid[:offset_x] if grid[:size_x] != 0

    y[i] = (((y[i] - grid[:offset_y]) / grid[:size_y]).round * grid[:size_y]) + grid[:offset_y] if grid[:size_y] != 0

    z[i] = (((z[i] - grid[:offset_z]) / grid[:size_z]).round * grid[:size_z]) + grid[:offset_z] if has_z? && grid[:size_z] != 0
  end

  cs = remove_duplicate_coords
  @ptr = cs.ptr

  self
end
to_line_string(options = {}) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 255
def to_line_string(options = {})
  Geos.create_line_string(self, srid: options[:srid])
end
to_linear_ring(options = {}) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 251
def to_linear_ring(options = {})
  Geos.create_linear_ring(self, srid: options[:srid])
end
to_point(options = {}) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 247
def to_point(options = {})
  Geos.create_point(self, srid: options[:srid])
end
to_polygon(options = {}) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 259
def to_polygon(options = {})
  Geos.create_polygon(self, srid: options[:srid])
end
to_s() click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 263
def to_s
  entries.collect { |entry|
    entry.join(' ')
  }.join(', ')
end
trans_scale(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 518
def trans_scale(*args, **kwargs)
  dup.trans_scale!(*args, **kwargs)
end
trans_scale!(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 488
def trans_scale!(*args, **kwargs)
  delta_x, delta_y, x_factor, y_factor = if !kwargs.empty?
    kwargs.values_at(:delta_x, :delta_y, :x_factor, :y_factor)
  elsif args.length.between?(1, 4)
    args.values_at(0...4)
  else
    raise ArgumentError, "Wrong number of arguments #{args.length} for 1-4"
  end

  x_factor ||= 1
  y_factor ||= 1
  delta_x ||= 0
  delta_y ||= 0

  affine!(
    afac: x_factor,
    bfac: 0,
    cfac: 0,
    dfac: 0,
    efac: y_factor,
    ffac: 0,
    gfac: 0,
    hfac: 0,
    ifac: 1,
    xoff: delta_x * x_factor,
    yoff: delta_y * y_factor,
    zoff: 0
  )
end
translate(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 547
def translate(*args, **kwargs)
  dup.translate!(*args, **kwargs)
end
translate!(*args, **kwargs) click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 522
def translate!(*args, **kwargs)
  x, y, z = if !kwargs.empty?
    kwargs.values_at(:x, :y, :z)
  elsif args.length.between?(1, 3)
    args.values_at(0...3)
  else
    raise ArgumentError, "Wrong number of arguments #{args.length} for 1-3"
  end

  affine!(
    afac: 1,
    bfac: 0,
    cfac: 0,
    dfac: 0,
    efac: 1,
    ffac: 0,
    gfac: 0,
    hfac: 0,
    ifac: 1,
    xoff: x || 0,
    yoff: y || 0,
    zoff: z || 1
  )
end
z?() click to toggle source
# File lib/ffi-geos/coordinate_sequence.rb, line 157
def z?
  dimensions == 3
end
Also aliased as: has_z?