dse-driver 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.yardopts +13 -0
- data/README.md +72 -0
- data/ext/gss_api_context/extconf.rb +27 -0
- data/ext/gss_api_context/gss_api_context.c +129 -0
- data/ext/gss_api_context/kerberosgss.c +407 -0
- data/ext/gss_api_context/kerberosgss.h +71 -0
- data/lib/dse.rb +104 -0
- data/lib/dse/auth/providers/gss_api.rb +160 -0
- data/lib/dse/auth/providers/password.rb +56 -0
- data/lib/dse/cluster.rb +99 -0
- data/lib/dse/geometry/line_string.rb +181 -0
- data/lib/dse/geometry/point.rb +179 -0
- data/lib/dse/geometry/polygon.rb +196 -0
- data/lib/dse/graph.rb +18 -0
- data/lib/dse/graph/duration.rb +131 -0
- data/lib/dse/graph/edge.rb +74 -0
- data/lib/dse/graph/options.rb +194 -0
- data/lib/dse/graph/path.rb +54 -0
- data/lib/dse/graph/result.rb +85 -0
- data/lib/dse/graph/result_set.rb +77 -0
- data/lib/dse/graph/statement.rb +118 -0
- data/lib/dse/graph/vertex.rb +107 -0
- data/lib/dse/graph/vertex_property.rb +66 -0
- data/lib/dse/load_balancing/policies/host_targeting.rb +102 -0
- data/lib/dse/session.rb +106 -0
- data/lib/dse/statements.rb +15 -0
- data/lib/dse/statements/host_targeting.rb +50 -0
- data/lib/dse/util.rb +12 -0
- data/lib/dse/util/endian_buffer.rb +37 -0
- data/lib/dse/version.rb +12 -0
- metadata +123 -0
@@ -0,0 +1,181 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2016 DataStax Inc.
|
5
|
+
#
|
6
|
+
# This software can be used solely with DataStax Enterprise. Please consult the license at
|
7
|
+
# http://www.datastax.com/terms/datastax-dse-driver-license-terms
|
8
|
+
#++
|
9
|
+
|
10
|
+
module Dse
|
11
|
+
module Geometry
|
12
|
+
# Encapsulates a set of lines, characterized by a sequence of {Point}s in the xy-plane. It corresponds to the
|
13
|
+
# `org.apache.cassandra.db.marshal.LineStringType` column type in DSE.
|
14
|
+
#
|
15
|
+
# @see https://en.wikipedia.org/wiki/Well-known_text Wikipedia article on Well Known Text
|
16
|
+
class LineString
|
17
|
+
include Cassandra::CustomData
|
18
|
+
|
19
|
+
# @return [Array<Point>] collection of points that make up this line-string.
|
20
|
+
attr_reader :points
|
21
|
+
|
22
|
+
# @private
|
23
|
+
WKT_RE = /^LINESTRING\s*\(\s*([^)]+)\s*\)$/
|
24
|
+
# @private
|
25
|
+
POINT_SEPARATOR_RE = /\s*,\s*/
|
26
|
+
|
27
|
+
# @param args [Array<Point>,Array<String>] varargs-style arguments in two forms:
|
28
|
+
# <ul><li>ordered collection of points that make up this line-string.
|
29
|
+
# Must be empty or have at least two points.</li>
|
30
|
+
# <li>one-element string array with the wkt representation.</li></ul>
|
31
|
+
#
|
32
|
+
# @example Construct an empty LineString
|
33
|
+
# line = LineString.new
|
34
|
+
# @example Construct a LineString with Point objects.
|
35
|
+
# line = LineString.new(Point.new(1.0, 2.0), Point.new(3.0, 4.0))
|
36
|
+
# @example Construct a LineString with a wkt string.
|
37
|
+
# line = LineString.new('LINESTRING (1.0 2.0, 3.0 4.0)')
|
38
|
+
def initialize(*args)
|
39
|
+
# The constructor has two forms:
|
40
|
+
# 1. 0, 2, or more Point objects in the order in which one connects them to make a line-string.
|
41
|
+
# 2. one String arg as the wkt representation.
|
42
|
+
|
43
|
+
if args.size == 1
|
44
|
+
wkt = args.first
|
45
|
+
Cassandra::Util.assert_instance_of(String, wkt)
|
46
|
+
|
47
|
+
if wkt == 'LINESTRING EMPTY'
|
48
|
+
@points = [].freeze
|
49
|
+
else
|
50
|
+
match = wkt.match(WKT_RE)
|
51
|
+
raise ArgumentError, "#{wkt.inspect} is not a valid WKT representation of a line-string" unless match
|
52
|
+
@points = self.class.parse_wkt_internal(match[1])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
@points = args.freeze
|
56
|
+
@points.each do |p|
|
57
|
+
Cassandra::Util.assert_instance_of(Point, p, "#{p.inspect} is not a Point")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @private
|
63
|
+
def self.parse_wkt_internal(line_str)
|
64
|
+
point_strings = line_str.split(POINT_SEPARATOR_RE)
|
65
|
+
points = []
|
66
|
+
point_strings.each do |ps|
|
67
|
+
next if ps.empty?
|
68
|
+
points << Point.new(*Point.parse_wkt_internal(ps))
|
69
|
+
end
|
70
|
+
points.freeze
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [String] well-known-text representation of this line-string.
|
74
|
+
def wkt
|
75
|
+
@points.empty? ? 'LINESTRING EMPTY' : "LINESTRING (#{wkt_internal})"
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [String] a human-readable English string describing this {LineString}.
|
79
|
+
def to_s
|
80
|
+
@points.join(' to ')
|
81
|
+
end
|
82
|
+
|
83
|
+
# @private
|
84
|
+
def wkt_internal
|
85
|
+
# This is a helper used to embed point coords into some container (e.g. polygon)
|
86
|
+
@points.map(&:wkt_internal).join(', ')
|
87
|
+
end
|
88
|
+
|
89
|
+
# @private
|
90
|
+
def eql?(other)
|
91
|
+
other.is_a?(LineString) && \
|
92
|
+
@points == other.points
|
93
|
+
end
|
94
|
+
alias == eql?
|
95
|
+
|
96
|
+
# @private
|
97
|
+
def hash
|
98
|
+
@hash ||= 31 * 17 + @points.hash
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
def inspect
|
103
|
+
"#<LineString:0x#{object_id.to_s(16)} " \
|
104
|
+
"@points=#{@points.inspect}>"
|
105
|
+
end
|
106
|
+
|
107
|
+
# methods related to serializing/deserializing.
|
108
|
+
|
109
|
+
# @private
|
110
|
+
TYPE = Cassandra::Types::Custom.new('org.apache.cassandra.db.marshal.LineStringType')
|
111
|
+
|
112
|
+
# @return [Cassandra::Types::Custom] type of column that is processed by this domain object class.
|
113
|
+
def self.type
|
114
|
+
TYPE
|
115
|
+
end
|
116
|
+
|
117
|
+
# Deserialize the given data into an instance of this domain object class.
|
118
|
+
# @param data [String] byte-array representation of a column value of this custom type.
|
119
|
+
# @return [LineString]
|
120
|
+
# @raise [Cassandra::Errors::DecodingError] upon failure.
|
121
|
+
def self.deserialize(data)
|
122
|
+
buffer = Cassandra::Protocol::CqlByteBuffer.new(data)
|
123
|
+
little_endian = buffer.read(1) != "\x00"
|
124
|
+
|
125
|
+
# Depending on the endian-ness of the data, we want to read it differently. Wrap the buffer
|
126
|
+
# with an "endian-aware" reader that reads the desired way.
|
127
|
+
buffer = Dse::Util::EndianBuffer.new(buffer, little_endian)
|
128
|
+
|
129
|
+
type = buffer.read_unsigned
|
130
|
+
raise Cassandra::Errors::DecodingError, "LineString data-type value should be 2, but was #{type}" if type != 2
|
131
|
+
|
132
|
+
deserialize_raw(buffer)
|
133
|
+
end
|
134
|
+
|
135
|
+
# This is a helper function to deserialize the meat of the data (after we've accounted for endianness
|
136
|
+
# and other metadata)
|
137
|
+
# @private
|
138
|
+
def self.deserialize_raw(buffer)
|
139
|
+
# Now comes the number of points in the line-string.
|
140
|
+
num_points = buffer.read_unsigned
|
141
|
+
|
142
|
+
# Read that many x,y coords from the buffer.
|
143
|
+
points = []
|
144
|
+
num_points.times do
|
145
|
+
points << Point.deserialize_raw(buffer)
|
146
|
+
end
|
147
|
+
LineString.new(*points)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Serialize this domain object into a byte array to send to DSE.
|
151
|
+
# @return [String] byte-array representation of this domain object.
|
152
|
+
def serialize
|
153
|
+
buffer = Cassandra::Protocol::CqlByteBuffer.new
|
154
|
+
|
155
|
+
# We serialize in little-endian form.
|
156
|
+
|
157
|
+
buffer << "\x01"
|
158
|
+
|
159
|
+
# This is a line-string.
|
160
|
+
buffer.append([2].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))
|
161
|
+
|
162
|
+
# Write out the count of how many points we have.
|
163
|
+
serialize_raw(buffer)
|
164
|
+
|
165
|
+
buffer
|
166
|
+
end
|
167
|
+
|
168
|
+
# This is a helper function to serialize the meat of the data (after we've accounted for endianness
|
169
|
+
# and other metadata)
|
170
|
+
# @private
|
171
|
+
def serialize_raw(buffer)
|
172
|
+
buffer.append([@points.size].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))
|
173
|
+
|
174
|
+
# Now write out x and y for each point.
|
175
|
+
@points.each do |point|
|
176
|
+
point.serialize_raw(buffer)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2016 DataStax Inc.
|
5
|
+
#
|
6
|
+
# This software can be used solely with DataStax Enterprise. Please consult the license at
|
7
|
+
# http://www.datastax.com/terms/datastax-dse-driver-license-terms
|
8
|
+
#++
|
9
|
+
|
10
|
+
module Dse
|
11
|
+
module Geometry
|
12
|
+
# Encapsulates a 2D point with x,y coordinates. It corresponds to the `org.apache.cassandra.db.marshal.PointType`
|
13
|
+
# column type in DSE.
|
14
|
+
#
|
15
|
+
# @see https://en.wikipedia.org/wiki/Well-known_text Wikipedia article on Well Known Text
|
16
|
+
class Point
|
17
|
+
include Cassandra::CustomData
|
18
|
+
|
19
|
+
# @return [Float] the x coordinate of the point.
|
20
|
+
attr_reader :x
|
21
|
+
# @return [Float] the y coordinate of the point.
|
22
|
+
attr_reader :y
|
23
|
+
|
24
|
+
# @private
|
25
|
+
WKT_RE = /^POINT\s*\(\s*([^)]+?)\s*\)$/
|
26
|
+
# @private
|
27
|
+
POINT_SPEC_RE = /^([0-9\-\.]+)\s+([0-9\-\.]+)$/
|
28
|
+
# @private
|
29
|
+
EOL_RE = /[\r\n]/
|
30
|
+
|
31
|
+
# @param args [Array<Numeric>,Array<String>] varargs-style arguments in two forms:
|
32
|
+
# <ul><li>two-element numeric array representing x,y coordinates.</li>
|
33
|
+
# <li>one-element string array with the wkt representation.</li></ul>
|
34
|
+
#
|
35
|
+
# @example Construct a Point with numeric arguments.
|
36
|
+
# point = Point.new(3, 4)
|
37
|
+
# @example Construct a Point with a wkt string.
|
38
|
+
# point = Point.new('POINT (3.0 4.0)')
|
39
|
+
def initialize(*args)
|
40
|
+
# The constructor has two forms:
|
41
|
+
# 1. two numeric args (x,y)
|
42
|
+
# 2. one String arg as the wkt representation.
|
43
|
+
|
44
|
+
case args.size
|
45
|
+
when 2
|
46
|
+
x, y = args
|
47
|
+
Cassandra::Util.assert_instance_of(::Numeric, x)
|
48
|
+
Cassandra::Util.assert_instance_of(::Numeric, y)
|
49
|
+
Cassandra::Util.assert(!x.nan?, 'x cannot be Float::NAN') if x.is_a?(Float)
|
50
|
+
Cassandra::Util.assert(!y.nan?, 'y cannot be Float::NAN') if y.is_a?(Float)
|
51
|
+
@x = x.to_f
|
52
|
+
@y = y.to_f
|
53
|
+
when 1
|
54
|
+
wkt = args.first
|
55
|
+
Cassandra::Util.assert_instance_of(String, wkt)
|
56
|
+
# subsitute eol chars in the string with a space.
|
57
|
+
wkt.gsub!(EOL_RE, ' ')
|
58
|
+
match = wkt.match(WKT_RE)
|
59
|
+
raise ArgumentError, "#{wkt.inspect} is not a valid WKT representation of a point" unless match
|
60
|
+
@x, @y = self.class.parse_wkt_internal(match[1])
|
61
|
+
else
|
62
|
+
raise ArgumentError,
|
63
|
+
'wrong number of arguments: use one string argument (wkt) or two numeric arguments (x,y)'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [String] well-known-text representation of this point.
|
68
|
+
def wkt
|
69
|
+
"POINT (#{wkt_internal})"
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [String] a human-readable English string describing this {Point}.
|
73
|
+
def to_s
|
74
|
+
"#{@x},#{@y}"
|
75
|
+
end
|
76
|
+
|
77
|
+
# @private
|
78
|
+
def wkt_internal
|
79
|
+
# This is a helper used to embed point coords into some container (e.g. line-string, polygon)
|
80
|
+
"#{@x} #{@y}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# @private
|
84
|
+
def self.parse_wkt_internal(point_str)
|
85
|
+
point_str.rstrip!
|
86
|
+
match = point_str.match(POINT_SPEC_RE)
|
87
|
+
raise ArgumentError, "#{point_str.inspect} is not a valid WKT representation of a point" unless match
|
88
|
+
[match[1].to_f, match[2].to_f]
|
89
|
+
end
|
90
|
+
|
91
|
+
# @private
|
92
|
+
def eql?(other)
|
93
|
+
other.is_a?(Point) && \
|
94
|
+
@x == other.x && \
|
95
|
+
@y == other.y
|
96
|
+
end
|
97
|
+
alias == eql?
|
98
|
+
|
99
|
+
# @private
|
100
|
+
def hash
|
101
|
+
@hash ||= begin
|
102
|
+
h = 17
|
103
|
+
h = 31 * h + @x.hash
|
104
|
+
h = 31 * h + @y.hash
|
105
|
+
h
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @private
|
110
|
+
def inspect
|
111
|
+
"#<Point:0x#{object_id.to_s(16)} " \
|
112
|
+
"@x=#{@x.inspect}, " \
|
113
|
+
"@y=#{@y.inspect}>"
|
114
|
+
end
|
115
|
+
|
116
|
+
# methods related to serializing/deserializing.
|
117
|
+
|
118
|
+
# @private
|
119
|
+
TYPE = Cassandra::Types::Custom.new('org.apache.cassandra.db.marshal.PointType')
|
120
|
+
|
121
|
+
# @return [Cassandra::Types::Custom] type of column that is processed by this domain object class.
|
122
|
+
def self.type
|
123
|
+
TYPE
|
124
|
+
end
|
125
|
+
|
126
|
+
# Deserialize the given data into an instance of this domain object class.
|
127
|
+
# @param data [String] byte-array representation of a column value of this custom type.
|
128
|
+
# @return [Point]
|
129
|
+
# @raise [Cassandra::Errors::DecodingError] upon failure.
|
130
|
+
def self.deserialize(data)
|
131
|
+
buffer = Cassandra::Protocol::CqlByteBuffer.new(data)
|
132
|
+
little_endian = buffer.read(1) != "\x00"
|
133
|
+
|
134
|
+
# Depending on the endian-ness of the data, we want to read it differently. Wrap the buffer
|
135
|
+
# with an "endian-aware" reader that reads the desired way.
|
136
|
+
buffer = Dse::Util::EndianBuffer.new(buffer, little_endian)
|
137
|
+
|
138
|
+
type = buffer.read_unsigned
|
139
|
+
raise Cassandra::Errors::DecodingError, "Point data-type value should be 1, but was #{type}" if type != 1
|
140
|
+
deserialize_raw(buffer)
|
141
|
+
end
|
142
|
+
|
143
|
+
# This is a helper function to deserialize the meat of the data (after we've accounted for endianness
|
144
|
+
# and other metadata)
|
145
|
+
# @private
|
146
|
+
def self.deserialize_raw(buffer)
|
147
|
+
x = buffer.read_double
|
148
|
+
y = buffer.read_double
|
149
|
+
Point.new(x, y)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Serialize this domain object into a byte array to send to DSE.
|
153
|
+
# @return [String] byte-array representation of this domain object.
|
154
|
+
def serialize
|
155
|
+
buffer = Cassandra::Protocol::CqlByteBuffer.new
|
156
|
+
|
157
|
+
# Serialize little-endian.
|
158
|
+
|
159
|
+
buffer << "\x01"
|
160
|
+
|
161
|
+
# This is a point.
|
162
|
+
buffer.append([1].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))
|
163
|
+
|
164
|
+
# Write out x and y.
|
165
|
+
serialize_raw(buffer)
|
166
|
+
|
167
|
+
buffer
|
168
|
+
end
|
169
|
+
|
170
|
+
# This is a helper function to serialize the meat of the data (after we've accounted for endianness
|
171
|
+
# and other metadata)
|
172
|
+
# @private
|
173
|
+
def serialize_raw(buffer)
|
174
|
+
buffer.append([@x].pack(Cassandra::Protocol::Formats::DOUBLE_FORMAT_LE))
|
175
|
+
buffer.append([@y].pack(Cassandra::Protocol::Formats::DOUBLE_FORMAT_LE))
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright (C) 2016 DataStax Inc.
|
5
|
+
#
|
6
|
+
# This software can be used solely with DataStax Enterprise. Please consult the license at
|
7
|
+
# http://www.datastax.com/terms/datastax-dse-driver-license-terms
|
8
|
+
#++
|
9
|
+
|
10
|
+
module Dse
|
11
|
+
module Geometry
|
12
|
+
# Encapsulates a polygon consisting of a set of linear-rings in the xy-plane. It corresponds to the
|
13
|
+
# `org.apache.cassandra.db.marshal.PolygonType` column type in DSE.
|
14
|
+
#
|
15
|
+
# A linear-ring is a {LineString} whose last point is the same as its first point. The first ring specified
|
16
|
+
# in a polygon defines the outer edges of the polygon and is called the _exterior ring_. A polygon may also have
|
17
|
+
# _holes_ within it, specified by other linear-rings, and those holes may contain linear-rings indicating
|
18
|
+
# _islands_. All such rings are called _interior rings_.
|
19
|
+
#
|
20
|
+
# @see https://en.wikipedia.org/wiki/Well-known_text Wikipedia article on Well Known Text
|
21
|
+
class Polygon
|
22
|
+
include Cassandra::CustomData
|
23
|
+
|
24
|
+
# @private
|
25
|
+
WKT_RE = /^POLYGON\s*\(\s*(.+?)\s*\)$/
|
26
|
+
# @private
|
27
|
+
LINESTRING_SEPARATOR_RE = /\),?/
|
28
|
+
# @private
|
29
|
+
EOL_RE = /[\r\n]/
|
30
|
+
|
31
|
+
# @param args [Array<LineString>,Array<String>] varargs-style arguments in two forms:
|
32
|
+
# <ul><li>ordered collection of linear-rings that make up this polygon. Can be empty.</li>
|
33
|
+
# <li>one-element string array with the wkt representation.</li></ul>
|
34
|
+
#
|
35
|
+
# @example Construct an empty Polygon
|
36
|
+
# polygon = Polygon.new
|
37
|
+
# @example Construct a Polygon with LineString objects.
|
38
|
+
# exterior_ring = LineString.new(Point.new(0, 0), Point.new(10, 0), Point.new(10, 10), Point.new(0, 0))
|
39
|
+
# interior_ring = LineString.new(Point.new(1, 1), Point.new(1, 5), Point.new(5, 1), Point.new(1, 1))
|
40
|
+
# polygon = Polygon.new(exterior_ring, interior_ring)
|
41
|
+
# @example Construct a line-string with a wkt string.
|
42
|
+
# polygon = Polygon.new('POLYGON ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 0.0), ' \
|
43
|
+
# '(1.0 1.0, 1.0 5.0, 5.0 1.0, 1.0 1.0))')
|
44
|
+
def initialize(*args)
|
45
|
+
# The constructor has two forms:
|
46
|
+
# 1. 0 or more LineString objects where the first is the exterior ring and the rest are interior rings.
|
47
|
+
# 2. one String arg as the wkt representation.
|
48
|
+
|
49
|
+
if args.size == 1 && args.first.is_a?(String)
|
50
|
+
# subsitute eol chars in the string with a space.
|
51
|
+
wkt = args.first.gsub(EOL_RE, ' ')
|
52
|
+
# Consolidate whitespace before/after commas and parens.
|
53
|
+
wkt.gsub!(/\s*([,\(\)])\s*/, '\1')
|
54
|
+
if wkt == 'POLYGON EMPTY'
|
55
|
+
@rings = [].freeze
|
56
|
+
else
|
57
|
+
match = wkt.match(WKT_RE)
|
58
|
+
raise ArgumentError, "#{wkt.inspect} is not a valid WKT representation of a polygon" unless match
|
59
|
+
@rings = parse_wkt_internal(match[1])
|
60
|
+
end
|
61
|
+
else
|
62
|
+
@rings = args.freeze
|
63
|
+
@rings.each do |ring|
|
64
|
+
Cassandra::Util.assert_instance_of(LineString, ring, "#{ring.inspect} is not a LineString")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @private
|
70
|
+
def parse_wkt_internal(poly_str)
|
71
|
+
# poly_str is everything inside the outer parens. Example:
|
72
|
+
# original: POLYGON ( (1 2, 3 4, 5 6, 1 2), (9 8, 7 6, 5 4, 9 8) )
|
73
|
+
# poly_str: (1 2, 3 4, 5 6, 1 2), (9 8, 7 6, 5 4, 9 8)
|
74
|
+
line_string_strings = poly_str.split(LINESTRING_SEPARATOR_RE)
|
75
|
+
line_strings = []
|
76
|
+
line_string_strings.each do |ls|
|
77
|
+
# ls still has a leading open paren and possibly spaces.
|
78
|
+
ls.sub!(/\s*\(\s*/, '')
|
79
|
+
line_strings << LineString.new(*LineString.parse_wkt_internal(ls))
|
80
|
+
end
|
81
|
+
line_strings.freeze
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [LineString] linear-ring characterizing the exterior of the polygon. `nil` for empty polygon.
|
85
|
+
def exterior_ring
|
86
|
+
@rings.first
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Array<LineString>] ordered collection of linear-rings that make up the interior of this polygon.
|
90
|
+
# Empty if there are no interior rings.
|
91
|
+
def interior_rings
|
92
|
+
@interior_rings ||= (@rings[1..-1] || []).freeze
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [String] well-known-text representation of this polygon.
|
96
|
+
def wkt
|
97
|
+
return 'POLYGON EMPTY' if @rings.empty?
|
98
|
+
|
99
|
+
result = 'POLYGON ('
|
100
|
+
first = true
|
101
|
+
@rings.each do |ring|
|
102
|
+
result += ', ' unless first
|
103
|
+
first = false
|
104
|
+
result += "(#{ring.wkt_internal})"
|
105
|
+
end
|
106
|
+
result += ')'
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [String] a human-readable English string describing this {Polygon}.
|
111
|
+
def to_s
|
112
|
+
"Exterior ring: #{@rings.first}\n" \
|
113
|
+
"Interior rings:\n " +
|
114
|
+
interior_rings.join("\n ")
|
115
|
+
end
|
116
|
+
|
117
|
+
# @private
|
118
|
+
def eql?(other)
|
119
|
+
other.is_a?(Polygon) && \
|
120
|
+
@rings == other.instance_variable_get(:@rings)
|
121
|
+
end
|
122
|
+
alias == eql?
|
123
|
+
|
124
|
+
# @private
|
125
|
+
def hash
|
126
|
+
@hash ||= 31 * 17 + @rings.hash
|
127
|
+
end
|
128
|
+
|
129
|
+
# @private
|
130
|
+
def inspect
|
131
|
+
"#<Polygon:0x#{object_id.to_s(16)} " \
|
132
|
+
"@exterior_ring=#{@rings.first.inspect}, " \
|
133
|
+
"@interior_rings=#{interior_rings.inspect}>"
|
134
|
+
end
|
135
|
+
|
136
|
+
# methods related to serializing/deserializing.
|
137
|
+
|
138
|
+
# @private
|
139
|
+
TYPE = Cassandra::Types::Custom.new('org.apache.cassandra.db.marshal.PolygonType')
|
140
|
+
|
141
|
+
# @return [Cassandra::Types::Custom] type of column that is processed by this domain object class.
|
142
|
+
def self.type
|
143
|
+
TYPE
|
144
|
+
end
|
145
|
+
|
146
|
+
# Deserialize the given data into an instance of this domain object class.
|
147
|
+
# @param data [String] byte-array representation of a column value of this custom type.
|
148
|
+
# @return [Polygon]
|
149
|
+
# @raise [Cassandra::Errors::DecodingError] upon failure.
|
150
|
+
def self.deserialize(data)
|
151
|
+
buffer = Cassandra::Protocol::CqlByteBuffer.new(data)
|
152
|
+
little_endian = buffer.read(1) != "\x00"
|
153
|
+
|
154
|
+
# Depending on the endian-ness of the data, we want to read it differently. Wrap the buffer
|
155
|
+
# with an "endian-aware" reader that reads the desired way.
|
156
|
+
buffer = Dse::Util::EndianBuffer.new(buffer, little_endian)
|
157
|
+
|
158
|
+
type = buffer.read_unsigned
|
159
|
+
raise Cassandra::Errors::DecodingError, "LineString data-type value should be 3, but was #{type}" if type != 3
|
160
|
+
|
161
|
+
# Now comes the number of rings.
|
162
|
+
num_rings = buffer.read_unsigned
|
163
|
+
|
164
|
+
# Read that many line-string's (rings) from the buffer.
|
165
|
+
rings = []
|
166
|
+
num_rings.times do
|
167
|
+
rings << LineString.deserialize_raw(buffer)
|
168
|
+
end
|
169
|
+
Polygon.new(*rings)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Serialize this domain object into a byte array to send to DSE.
|
173
|
+
# @return [String] byte-array representation of this domain object.
|
174
|
+
def serialize
|
175
|
+
buffer = Cassandra::Protocol::CqlByteBuffer.new
|
176
|
+
|
177
|
+
# We serialize in little-endian form.
|
178
|
+
|
179
|
+
buffer << "\x01"
|
180
|
+
|
181
|
+
# This is a polygon.
|
182
|
+
buffer.append([3].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))
|
183
|
+
|
184
|
+
# Write out the count of how many rings we have.
|
185
|
+
buffer.append([@rings.size].pack(Cassandra::Protocol::Formats::INT_FORMAT_LE))
|
186
|
+
|
187
|
+
# Now write out the raw serialization of each ring (e.g. linestring).
|
188
|
+
@rings.each do |ring|
|
189
|
+
ring.serialize_raw(buffer)
|
190
|
+
end
|
191
|
+
|
192
|
+
buffer
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|