Geospatial Types

DSE 5.0 introduced Geospatial Types. Point, LineString, and Polygon geospatial datatypes can be used from the Dse::Geometry module. These datatypes can be used in both Cassandra tables and as parameters to DSE Graph.

Background

Given
a running dse cluster with schema:
CREATE KEYSPACE simplex WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};
USE simplex;
CREATE TABLE points (k text PRIMARY KEY, v 'PointType');
CREATE TABLE line_strings (k text PRIMARY KEY, v 'LineStringType');
CREATE TABLE polygons (k text PRIMARY KEY, v 'PolygonType');

Creating a Point using well-known text (WKT)

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

session.execute("INSERT INTO points (k, v) VALUES ('point0', 'POINT (3.0 2.0)')")

result = session.execute('SELECT * FROM points').first
point = result['v']
puts "X coordinate: #{point.x}"
puts "Y coordinate: #{point.y}"
puts "Coords as string: #{point.to_s}"
puts "Point's WKT: #{point.wkt}"
puts ""

test_point = Dse::Geometry::Point.new('POINT (30 10)')
puts "X coordinate: #{test_point.x}"
puts "Y coordinate: #{test_point.y}"
puts "Coords as string: #{test_point.to_s}"
puts "Point's WKT: #{test_point.wkt}"
When
it is executed
Then
its output should contain:
X coordinate: 3.0
Y coordinate: 2.0
Coords as string: 3.0,2.0
Point's WKT: POINT (3.0 2.0)

X coordinate: 30.0
Y coordinate: 10.0
Coords as string: 30.0,10.0
Point's WKT: POINT (30.0 10.0)

Creating a Point using well-known binary (WKB)

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

test_point = Dse::Geometry::Point.new(38.0, 21.0)
session.execute('INSERT INTO points (k, v) VALUES (?, ?)', arguments: ['point1', test_point])

result = session.execute("SELECT * FROM points WHERE k='point1'").first
point = result['v']
puts "X coordinate: #{point.x}"
puts "Y coordinate: #{point.y}"
puts "Coords as string: #{point.to_s}"
puts "Point's WKT: #{point.wkt}"
puts "Are they the same? #{test_point == point}"
When
it is executed
Then
its output should contain:
X coordinate: 38.0
Y coordinate: 21.0
Coords as string: 38.0,21.0
Point's WKT: POINT (38.0 21.0)
Are they the same? true

Creating a LineString using well-known text (WKT)

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

session.execute("INSERT INTO line_strings (k, v) VALUES ('linestring0', 'LineString (0.0 0.0, 1.0 1.0)')")

result = session.execute('SELECT * FROM line_strings').first
line_string = result['v']
puts "First point: #{line_string.points[0]}"
puts "Second point: #{line_string.points[1]}"
puts "LineString as string: #{line_string.to_s}"
puts "LineString's WKT: #{line_string.wkt}"
puts ""

test_line_string = Dse::Geometry::LineString.new('LINESTRING (30 10, 10 30, 40 40)')
puts "First point: #{test_line_string.points[0]}"
puts "Second point: #{test_line_string.points[1]}"
puts "Third point: #{test_line_string.points[2]}"
puts "LineString as string: #{test_line_string.to_s}"
puts "LineString's WKT: #{test_line_string.wkt}"
When
it is executed
Then
its output should contain:
First point: 0.0,0.0
Second point: 1.0,1.0
LineString as string: 0.0,0.0 to 1.0,1.0
LineString's WKT: LINESTRING (0.0 0.0, 1.0 1.0)

First point: 30.0,10.0
Second point: 10.0,30.0
Third point: 40.0,40.0
LineString as string: 30.0,10.0 to 10.0,30.0 to 40.0,40.0
LineString's WKT: LINESTRING (30.0 10.0, 10.0 30.0, 40.0 40.0)

Creating a LineString using well-known binary (WKB)

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

points = [Dse::Geometry::Point.new(2.0, 3.0),
          Dse::Geometry::Point.new(3.0, 4.0),
          Dse::Geometry::Point.new(4.0, 5.0)
]

test_line_string = Dse::Geometry::LineString.new(*points)
session.execute('INSERT INTO line_strings (k, v) VALUES (?, ?)', arguments: ['linestring1', test_line_string])

result = session.execute("SELECT * FROM line_strings WHERE k='linestring1'").first
line_string = result['v']
puts "First point: #{line_string.points[0]}"
puts "Second point: #{line_string.points[1]}"
puts "Third point: #{line_string.points[2]}"
puts "LineString as string: #{line_string.to_s}"
puts "LineString's WKT: #{line_string.wkt}"
puts "Are they the same? #{test_line_string == line_string}"
When
it is executed
Then
its output should contain:
First point: 2.0,3.0
Second point: 3.0,4.0
Third point: 4.0,5.0
LineString as string: 2.0,3.0 to 3.0,4.0 to 4.0,5.0
LineString's WKT: LINESTRING (2.0 3.0, 3.0 4.0, 4.0 5.0)
Are they the same? true

Creating a Polygon using well-known text (WKT)

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

session.execute("INSERT INTO polygons (k, v) VALUES ('polygon0',
                'POLYGON (
                          (0.0 0.0, 20.0 0.0, 25.0 25.0, 0.0 25.0, 0.0 0.0),
                          (1.0 1.0, 2.0 2.0, 2.0 1.0, 1.0 1.0)
                )')"
)

result = session.execute("SELECT * FROM polygons WHERE k='polygon0'").first
polygon = result['v']
puts "Exterior ring: #{polygon.exterior_ring}"
puts "Interior rings: #{polygon.interior_rings.map { |linestring| linestring.to_s }}"
puts "Polygon as string:\n#{polygon.to_s}"
puts "Polygon's WKT: #{polygon.wkt}"
puts ""

test_polygon = Dse::Geometry::Polygon.new('POLYGON ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0),
                                         (1.0 1.0, 4.0 9.0, 9.0 1.0, 1.0 1.0))')
puts "Exterior ring: #{test_polygon.exterior_ring}"
puts "Interior rings: #{test_polygon.interior_rings.map { |linestring| linestring.to_s }}"
puts "Polygon as string:\n#{test_polygon.to_s}"
puts "Polygon's WKT: #{test_polygon.wkt}"
When
it is executed
Then
its output should contain:
Exterior ring: 0.0,0.0 to 20.0,0.0 to 25.0,25.0 to 0.0,25.0 to 0.0,0.0
Interior rings: ["1.0,1.0 to 2.0,2.0 to 2.0,1.0 to 1.0,1.0"]
Polygon as string:
Exterior ring: 0.0,0.0 to 20.0,0.0 to 25.0,25.0 to 0.0,25.0 to 0.0,0.0
Interior rings:
    1.0,1.0 to 2.0,2.0 to 2.0,1.0 to 1.0,1.0
Polygon's WKT: POLYGON ((0.0 0.0, 20.0 0.0, 25.0 25.0, 0.0 25.0, 0.0 0.0), (1.0 1.0, 2.0 2.0, 2.0 1.0, 1.0 1.0))

Exterior ring: 0.0,0.0 to 10.0,0.0 to 10.0,10.0 to 0.0,10.0 to 0.0,0.0
Interior rings: ["1.0,1.0 to 4.0,9.0 to 9.0,1.0 to 1.0,1.0"]
Polygon as string:
Exterior ring: 0.0,0.0 to 10.0,0.0 to 10.0,10.0 to 0.0,10.0 to 0.0,0.0
Interior rings:
    1.0,1.0 to 4.0,9.0 to 9.0,1.0 to 1.0,1.0
Polygon's WKT: POLYGON ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0), (1.0 1.0, 4.0 9.0, 9.0 1.0, 1.0 1.0))

Creating a Polygon using well-known binary (WKB)

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

line_string0 = Dse::Geometry::LineString.new('LINESTRING (0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0)')
line_string1 = Dse::Geometry::LineString.new('LINESTRING (1.0 1.0, 4.0 9.0, 9.0 1.0, 1.0 1.0)')
test_polygon = Dse::Geometry::Polygon.new(*[line_string0, line_string1])

insert = session.prepare('INSERT INTO polygons (k, v) VALUES (?, ?)')
session.execute(insert, arguments: ['polygon1', test_polygon])

result = session.execute("SELECT * FROM polygons WHERE k='polygon1'").first
polygon = result['v']
puts "Exterior ring: #{polygon.exterior_ring}"
puts "Interior rings: #{polygon.interior_rings.map { |linestring| linestring.to_s }}"
puts "Polygon as string:\n#{polygon.to_s}"
puts "Polygon's WKT: #{polygon.wkt}"
puts "Are they the same? #{test_polygon == polygon}"
When
it is executed
Then
its output should contain:
Exterior ring: 0.0,0.0 to 10.0,0.0 to 10.0,10.0 to 0.0,10.0 to 0.0,0.0
Interior rings: ["1.0,1.0 to 4.0,9.0 to 9.0,1.0 to 1.0,1.0"]
Polygon as string:
Exterior ring: 0.0,0.0 to 10.0,0.0 to 10.0,10.0 to 0.0,10.0 to 0.0,0.0
Interior rings:
    1.0,1.0 to 4.0,9.0 to 9.0,1.0 to 1.0,1.0
Polygon's WKT: POLYGON ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0), (1.0 1.0, 4.0 9.0, 9.0 1.0, 1.0 1.0))
Are they the same? true

Creating empty LineStrings and Polygons

Given
the following example:
require 'dse'

cluster = Dse.cluster
session = cluster.connect('simplex')

empty_line_string = Dse::Geometry::LineString.new
puts "LineString's points: #{empty_line_string.points}"
puts "LineString's WKT: #{empty_line_string.wkt}"
puts ""

empty_polygon = Dse::Geometry::Polygon.new
puts "Exterior ring nil?: #{empty_polygon.exterior_ring.nil?}"
puts "Interior rings: #{empty_polygon.interior_rings}"
puts "Polygon's WKT: #{empty_polygon.wkt}"
When
it is executed
Then
its output should contain:
LineString's points: []
LineString's WKT: LINESTRING EMPTY

Exterior ring nil?: true
Interior rings: []
Polygon's WKT: POLYGON EMPTY