dse-driver 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|