Class: Dse::Geometry::Polygon

Inherits:
Object
  • Object
show all
Includes:
Cassandra::CustomData
Defined in:
lib/dse/geometry/polygon.rb

Overview

Encapsulates a polygon consisting of a set of linear-rings in the xy-plane. It corresponds to the org.apache.cassandra.db.marshal.PolygonType column type in DSE.

A linear-ring is a LineString whose last point is the same as its first point. The first ring specified in a polygon defines the outer edges of the polygon and is called the exterior ring. A polygon may also have holes within it, specified by other linear-rings, and those holes may contain linear-rings indicating islands. All such rings are called interior rings.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Polygon

Returns a new instance of Polygon.

Examples:

Construct an empty Polygon

polygon = Polygon.new

Construct a Polygon with LineString objects.

exterior_ring = LineString.new(Point.new(0, 0), Point.new(10, 0), Point.new(10, 10), Point.new(0, 0))
interior_ring = LineString.new(Point.new(1, 1), Point.new(1, 5), Point.new(5, 1), Point.new(1, 1))
polygon = Polygon.new(exterior_ring, interior_ring)

Construct a line-string with a wkt string.

polygon = Polygon.new('POLYGON ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 0.0), ' \
                      '(1.0 1.0, 1.0 5.0, 5.0 1.0, 1.0 1.0))')

Parameters:

  • args (Array<LineString>, Array<String>)

    varargs-style arguments in two forms:

    • ordered collection of linear-rings that make up this polygon. Can be empty.
    • one-element string array with the wkt representation.


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/dse/geometry/polygon.rb', line 44

def initialize(*args)
  # The constructor has two forms:
  # 1. 0 or more LineString objects where the first is the exterior ring and the rest are interior rings.
  # 2. one String arg as the wkt representation.

  if args.size == 1 && args.first.is_a?(String)
    # subsitute eol chars in the string with a space.
    wkt = args.first.gsub(EOL_RE, ' ')
    # Consolidate whitespace before/after commas and parens.
    wkt.gsub!(/\s*([,\(\)])\s*/, '\1')
    if wkt == 'POLYGON EMPTY'
      @rings = [].freeze
    else
      match = wkt.match(WKT_RE)
      raise ArgumentError, "#{wkt.inspect} is not a valid WKT representation of a polygon" unless match
      @rings = parse_wkt_internal(match[1])
    end
  else
    @rings = args.freeze
    @rings.each do |ring|
      Cassandra::Util.assert_instance_of(LineString, ring, "#{ring.inspect} is not a LineString")
    end
  end
end

Class Method Details

.deserialize(data) ⇒ Polygon

Deserialize the given data into an instance of this domain object class.

Parameters:

  • data (String)

    byte-array representation of a column value of this custom type.

Returns:

Raises:

  • (Cassandra::Errors::DecodingError)

    upon failure.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/dse/geometry/polygon.rb', line 150

def self.deserialize(data)
  buffer = Cassandra::Protocol::CqlByteBuffer.new(data)
  little_endian = buffer.read(1) != "\x00"

  # Depending on the endian-ness of the data, we want to read it differently. Wrap the buffer
  # with an "endian-aware" reader that reads the desired way.
  buffer = Dse::Util::EndianBuffer.new(buffer, little_endian)

  type = buffer.read_unsigned
  raise Cassandra::Errors::DecodingError, "LineString data-type value should be 3, but was #{type}" if type != 3

  # Now comes the number of rings.
  num_rings = buffer.read_unsigned

  # Read that many line-string's (rings) from the buffer.
  rings = []
  num_rings.times do
    rings << LineString.deserialize_raw(buffer)
  end
  Polygon.new(*rings)
end

.typeCassandra::Types::Custom

Returns type of column that is processed by this domain object class.

Returns:

  • (Cassandra::Types::Custom)

    type of column that is processed by this domain object class.



142
143
144
# File 'lib/dse/geometry/polygon.rb', line 142

def self.type
  TYPE
end

Instance Method Details

#exterior_ringLineString

Returns linear-ring characterizing the exterior of the polygon. nil for empty polygon.

Returns:

  • (LineString)

    linear-ring characterizing the exterior of the polygon. nil for empty polygon.



85
86
87
# File 'lib/dse/geometry/polygon.rb', line 85

def exterior_ring
  @rings.first
end

#interior_ringsArray<LineString>

Returns ordered collection of linear-rings that make up the interior of this polygon. Empty if there are no interior rings.

Returns:

  • (Array<LineString>)

    ordered collection of linear-rings that make up the interior of this polygon. Empty if there are no interior rings.



91
92
93
# File 'lib/dse/geometry/polygon.rb', line 91

def interior_rings
  @interior_rings ||= (@rings[1..-1] || []).freeze
end

#serializeString

Serialize this domain object into a byte array to send to DSE.

Returns:

  • (String)

    byte-array representation of this domain object.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/dse/geometry/polygon.rb', line 174

def serialize
  buffer = Cassandra::Protocol::CqlByteBuffer.new

  # We serialize in little-endian form.

  buffer << "\x01"

  # This is a polygon.
  buffer.append([3].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))

  # Write out the count of how many rings we have.
  buffer.append([@rings.size].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))

  # Now write out the raw serialization of each ring (e.g. linestring).
  @rings.each do |ring|
    ring.serialize_raw(buffer)
  end

  buffer
end

#to_sString

Returns a human-readable English string describing this Dse::Geometry::Polygon.

Returns:



111
112
113
114
115
# File 'lib/dse/geometry/polygon.rb', line 111

def to_s
  "Exterior ring: #{@rings.first}\n" \
    "Interior rings:\n    " +
    interior_rings.join("\n    ")
end

#wktString

Returns well-known-text representation of this polygon.

Returns:

  • (String)

    well-known-text representation of this polygon.



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/dse/geometry/polygon.rb', line 96

def wkt
  return 'POLYGON EMPTY' if @rings.empty?

  result = 'POLYGON ('
  first = true
  @rings.each do |ring|
    result += ', ' unless first
    first = false
    result += "(#{ring.wkt_internal})"
  end
  result += ')'
  result
end