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))')


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.

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



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

def self.type
  TYPE
end

Instance Method Details

#exterior_ringLineString



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

def exterior_ring
  @rings.first
end

#interior_ringsArray<LineString>



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.



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



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



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