Cartesian spatial traversals

Creating Cartesian spatial traversal queries.

Cartesian spatial queries are used to discover Cartesian (graphable) information. All Cartesian data types (points, linestrings, and polygons) can be searched for specified values with simple queries. More interesting traversal queries discover points or linestrings within a radius from a specified point or within a specified spatial polygon.

DSE Search indexes can be created to decrease the latency in response time, but are not required. Create schema to use a search index for point and linestring properties in the Cartesian schema.

Schema and data

The examples here use the following schema:
// SCHEMA
// POINT
schema.propertyKey('name').Text().create()
schema.propertyKey('point').Point().withBounds(-3, -3, 3, 3).create()
schema.vertexLabel('location').properties('name','point').create()
// LINESTRING
schema.propertyKey('line').Linestring().withBounds(-3, -3, 3, 3).create()
schema.vertexLabel('lineLocation').properties('name','line').create()
// POLYGON
schema.propertyKey('polygon').Polygon().withBounds(-3, -3, 3, 3).create()
schema.vertexLabel('polyLocation').properties('name','polygon').create()
// MATERIALIZED VIEW INDEXES
schema.vertexLabel('location').index('byname').materialized().by('name').add()
schema.vertexLabel('lineLocation').index('byname').materialized().by('name').add()
schema.vertexLabel('polyLocation').index('byname').materialized().by('name').add()
//SEARCH INDEX - ONLY WORKS FOR POINT AND LINESTRING
schema.vertexLabel('location').index('search').search().by('point').add()
schema.vertexLabel('lineLocation').index('search').search().by('line').add()
The example use the following data:
/// Create a point
graph.addVertex(label,'location','name','p0','point',Geo.point(0.5,0.5))
graph.addVertex(label,'location','name','p1','point',Geo.point(1,1))
graph.addVertex(label,'location','name','p2','point',Geo.point(-1,1))
graph.addVertex(label,'location','name','p3','point',Geo.point(-2,-2))
graph.addVertex(label,'location','name','p4','point',Geo.point(2,2))

// Create a linestring
graph.addVertex(label, 'lineLocation', 'name', 'l1', 'line', "LINESTRING(0 0, 1 1)")
graph.addVertex(label, 'lineLocation', 'name', 'l2', 'line', "LINESTRING(0 0, -1 1)")
graph.addVertex(label, 'lineLocation', 'name', 'l3', 'line', "LINESTRING(0 0, -2 -2)")
graph.addVertex(label, 'lineLocation', 'name', 'l4', 'line', "LINESTRING(0 0, 2 -2)")

// Create a polygon
graph.addVertex(label, 'polyLocation','name', 'g1', 'polygon',Geo.polygon(0,0,1,1,0,1,0,0))
graph.addVertex(label, 'polyLocation','name', 'g2', 'polygon',Geo.polygon(0,0,0,1,-1,1,0,0))
graph.addVertex(label, 'polyLocation','name', 'g3', 'polygon',Geo.polygon(0,0,-2,0,-2,-2,0,0))
graph.addVertex(label, 'polyLocation','name', 'g4', 'polygon',Geo.polygon(0,0,2,0,2,-2,0,0))

Find stored Cartesian spatial data that matches specified information

Find the stored data that matches a point mapped to the specified (x, y):
g.V().
has('location','point', Geo.point(0.5, 0.5)).
valueMap()
results in:
{name=[p0], point=[POINT (0.5 0.5)]}
Find the stored data that matches a line mapped to the specified points:
 g.V().
has('lineLocation','line',Geo.lineString(0, 0, 1, 1)).
valueMap()
results in:
{line=[LINESTRING (0 0, 1 1)], name=[l1]}
Find the stored data that matches a polygon mapped to the specified points:
 g.V().
has('polyLocation', 'polygon',Geo.polygon(0,0,1,1,0,1,0,0)).
valueMap()
results in:
{polygon=[POLYGON ((0 0, 1 1, 0 1, 0 0))], name=[g1]}

Find stored Cartesian spatial points or linestrings within a specified radius from a specified point

These queries, as well as the queries that use a specified Cartesian spatial polygon use a method Geo.inside() that specifies a point and a radius.

Find all the points within a radius from a particular point (centerpoint):
g.V().
has('location', 'point', Geo.inside(Geo.point(0, 0), 1)).
values('name')
lists:
==>p0
Centering the query on (0, 0) and searching within 1 unit returns one point, p0, from the dataset.
Find all the linestrings within a radius from a particular location (centerpoint):
 g.V().has('lineLocation', 'line', Geo.inside(Geo.point(0.0, 0.0), 1.415)).valueMap()
lists:
==>{line=[LINESTRING (0 0, 1 1)], name=[l1]}
==>{line=[LINESTRING (0 0, -1 1)], name=[l2]}
Centering the query on (0,0) and searching within 1.415 units returns two stored linestrings: l1 and l2.

Find stored Cartesian spatial points or linestrings within a specified Cartesian spatial polygon

Polygons may be used in these queries to find points with a polygon.

Find all points within a specified Cartesian spatial polygon:
g.V().
has('location', 'point', Geo.inside(Geo.polygon(0, 0, 1, 0, 1, 1, 0, 1, 0, 0))).
values('name')
lists:
==>p0
find linestrings within a polygon
g.V().
has('lineLocation', 'line', Geo.inside(Geo.polygon(0, 0, 1, 0, 1, 1, 0, 1, 0, 0))).
values('name')
lists:
==>l1

Schema and data

The examples here use the following schema:
///SCHEMA
// PROPERTY KEYS
// Check for previous creation of property key with ifNotExists()
schema.propertyKey('name').Text().ifNotExists().create()
schema.propertyKey('address').Text().ifNotExists().create()
schema.propertyKey('location').Point().withBounds(-100,-100,100,100).ifNotExists().create()
// VERTEX LABELS
schema.vertexLabel('person').properties('name').ifNotExists().create()
schema.vertexLabel('home').properties('address','location').ifNotExists().create()
schema.vertexLabel('store').properties('name','location').ifNotExists().create()
schema.vertexLabel('ingredient').properties('name').ifNotExists().create()
// EEDGE LABELS
schema.edgeLabel('livesIn').connection('person','home').ifNotExists().create()
schema.edgeLabel('isStockedWith').connection('store','ingredient').multiple().ifNotExists().create()

// SEARCH INDEXES
schema.vertexLabel('person').index('search').search().by('name').asString().ifNotExists().add()
schema.vertexLabel('home').index('search').search().by('name').by('location').add();
schema.vertexLabel('store').index('search').search().by('name').by('location').add();
schema.vertexLabel('ingredient').index('search').search().by('name').add();

The examples use the following data:
//VERTICES
// PERSON VERTICES
pam = graph.addVertex(label, 'person', 'name','Pam')
les = graph.addVertex(label, 'person', 'name','Les')
paul = graph.addVertex(label, 'person', 'name','Paul')
victoria = graph.addVertex(label, 'person', 'name','Victoria')
terri = graph.addVertex(label, 'person', 'name','Terri')

// HOME VERTICES
home1 = graph.addVertex(label, 'home', 'address', '555 4th St',  'location', Geo.point(7,2));
home2 = graph.addVertex(label, 'home', 'address', '1700 Coyote Rd',  'location', Geo.point(-2,1));
home3 = graph.addVertex(label, 'home', 'address', '99 Mountain Pass Hwy',  'location', Geo.point(0,0));

// STORE VERTICES
store1 = graph.addVertex(label, 'store', 'name', 'ZippyMart',  'location', Geo.point(1,5));
store2 = graph.addVertex(label, 'store', 'name', 'Quik Station',  'location', Geo.point(7,-1));
store3 = graph.addVertex(label, 'store', 'name', 'Mamma's Grocery',  'location', Geo.point(-3,-3));

// INGREDIENT VERTICES
celery = graph.addVertex(label, 'ingredient','name', 'celery');
milk = graph.addVertex(label, 'ingredient','name', 'milk');
bokChoy = graph.addVertex(label, 'ingredient','name', 'bok choy');
steak = graph.addVertex(label, 'ingredient','name', 'steak');
carrots = graph.addVertex(label, 'ingredient','name', 'carrots');
porkChops = graph.addVertex(label, 'ingredient','name', 'pork chops');

// PERSON - HOME EDGES
pam.addEdge('livesIn', home1);
les.addEdge('livesIn', home1);
paul.addEdge('livesIn',home3);
victoria.addEdge('livesIn',home3);
terri.addEdge('livesIn',home2);

// STORE - INGREDIENT EDGES
store1.addEdge('isStockedWith', milk);
store1.addEdge('isStockedWith', bokChoy);
store1.addEdge('isStockedWith', steak);
store2.addEdge('isStockedWith', steak);
store2.addEdge('isStockedWith', carrots);
store2.addEdge('isStockedWith', porkChops);
store3.addEdge('isStockedWith', milk);
store3.addEdge('isStockedWith', carrots);
store3.addEdge('isStockedWith', celery);

Finding celery

You are a mathematics teacher writing simple Cartesian problem for your students. They are great fans of ants on a log, a snack made with celery, cream cheese, and raisins. So, you decide to help them find the nearest store to their house which has celery in stock.

Paul is the student whose home we'll use as the starting point. First, list all stores within the given radius (10 units of distance) from Paul's home (the centerpoint):
g.V().has('store', 'location', Geo.inside(Geo.point(0,0),10)).
values('name')
results in:
==>ZippyMart
==>Quik Station
==>Mamma's Grocery
Note that this exercise is using Cartesian coordinates and distances calculated between Cartesian points, but a similar exercises can use geospatial data.
Now list the stores within a 10 unit radius from Paul's home that have celery:
g.V().has('store', 'location', Geo.inside(Geo.point(0,0),10)).as('Store').
out().has('name','celery').as('Ingred').
select('Store', 'Ingred').
by('name').
by('name')
finds:
==>{Store=ZippyMart, Ingred=celery}
==>{Store=Quik Station, Ingred=celery}
==>{Store=Mamma's Grocery, Ingred=celery}
This query uses methods that are common, such as as(), out(), and select() that are explained in Simple Traversals to narrow the query.
Finally, list the stores within a 10 unit radius of Paul's home that have celery, and sort them by the distance from Paul's home:
// List store name, location, and ingredient in order by distance from the store
g.V().has('store', 'location', Geo.inside(Geo.point(0,0),25)).as('Store').
order().by{it.value('location').getOgcGeometry().distance(Geo.point(0,0).getOgcGeometry())}.
as('Location').
out().has('name','celery').as('Ingred').
select('Store', 'Location','Ingred').
by('name').
by('location').
by('name')
==>{Store=Mamma's Grocery, Location=POINT (-3 -3), Ingred=celery}
==>{Store=ZippyMart, Location=POINT (1 5), Ingred=celery}
==>{Store=Quik Station, Location=POINT (7 -1), Ingred=celery}
This query adds the method order() to sort the results; it is also explained in Simple Traversals. The query must also use a method that must be imported in order for the query to succeed: getOgcGeometry() and distance(). Importing the library is accomplished in the original script using:
import com.esri.core.geometry.ogc.OGCGeometry;

The students working on this problem now know that Mamma's Grocery is the place to head to get the celery they need to make their favorite snack!