Geospatial traversals
Creating geospatial traversal queries.
(longitude, latitude)
following WKT format.Distance calculations are crucial to proper results. DSE Search indexes can be created for geospatial data in DSE Graph, and DSE Search uses the Haversine formula to determine the great-circle distance between two points. DSE Search indexes cannot be created for polygons, but are essential to making geospatial point and linestring queries performant. See the note on the difference between DSE Search-backed geospatial queries and non-index-backed queries.
Schema and data
// SCHEMA
// POINT
schema.propertyKey('name').Text().create()
schema.propertyKey('point').Point().withGeoBounds().create()
schema.vertexLabel('location').properties('name','point').create()
// LINESTRING
schema.propertyKey('line').Linestring().withGeoBounds().create()
schema.vertexLabel('lineLocation').properties('name','line').create()
// POLYGON
schema.propertyKey('polygon').Polygon().withGeoBounds().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()
// Create a point
graph.addVertex(label,'location','name','Paris','point',Geo.point(2.352222, 48.856614))
graph.addVertex(label,'location','name','London','point',Geo.point(-0.127758,51.507351))
graph.addVertex(label,'location','name','Dublin','point',Geo.point(-6.26031, 53.349805))
graph.addVertex(label,'location','name','Aachen','point',Geo.point(6.083887, 50.775346))
graph.addVertex(label,'location','name','Tokyo','point',Geo.point(139.691706, 35.689487))
// Create a linestring
graph.addVertex(label, 'lineLocation', 'name', 'ParisLondon', 'line', "LINESTRING(2.352222 48.856614, -0.127758 51.507351)")
graph.addVertex(label, 'lineLocation', 'name', 'LondonDublin', 'line', "LINESTRING(-0.127758 51.507351, -6.26031 53.349805)")
graph.addVertex(label, 'lineLocation', 'name', 'ParisAachen', 'line', "LINESTRING(2.352222 48.856614, 6.083887 50.775346)")
graph.addVertex(label, 'lineLocation', 'name', 'AachenTokyo', 'line', "LINESTRING(6.083887 50.775346, 139.691706 35.689487)")
// Create a polygon
graph.addVertex(label, 'polyLocation','name', 'ParisLondonDublin', 'polygon',Geo.polygon(2.352222, 48.856614, -0.127758, 51.507351, -6.26031, 53.349805))
graph.addVertex(label, 'polyLocation','name', 'LondonDublinAachen', 'polygon',Geo.polygon(-0.127758, 51.507351, -6.26031, 53.349805, 6.083887, 50.775346))
graph.addVertex(label, 'polyLocation','name', 'DublinAachenTokyo', 'polygon',Geo.polygon(-6.26031, 53.349805, 6.083887, 50.775346, 139.691706, 35.689487))
// PARIS TO LONDON: 3.08 DEGREES; 344 KM; 214 MI; 344,000 M
// PARIS TO AACHEN: 3.07 DEGREES; 343 KM; 213 MI; 343,000 M
// PARIS TO DUBLIN: 7.02 DEGREES; 781 KM; 485 MI; 781,000 M
// PARIS TO TOYKO: 86.3 DEGREES; 9713 KM; 6035 MI; 9,713,000 M
Find stored geospatial data that matches specified information
g.V().
has('location','point', Geo.point(2.352222, 48.856614)).
valueMap()
results
in:{name=[Paris], point=[POINT (2.352222 48.856614)]}
g.V().
has('lineLocation','line',Geo.lineString(2.352222, 48.856614, -0.127758, 51.507351)).
valueMap()
results
in:{line=[LINESTRING (2.352222 48.856614, -0.127758 51.507351)], name=[ParisLondon]}
g.V().
has('polyLocation', 'polygon',Geo.polygon(2.352222, 48.856614, -0.127758, 51.507351, -6.26031, 53.349805)).
valueMap()
results
in:{polygon=[POLYGON ((2.352222 48.856614, -0.127758 51.507351, -6.26031 53.349805, 2.352222 48.856614))], name=[ParisLondonDublin]}
Find stored geospatial points or linestrings within a specified radius from a specified point
These queries, as well as the queries that use a specified geospatial polygon use a method
Geo.inside()
that specifies a point, a radius, and the units to be
used.
Geo.inside()
method:- DEGREES
- Degrees of distance. One degree of latitude is approximately 111.2 kilometers, whereas one degree of longitude depends on the distance from the equator. At the equator, one degree of longitude equals 111.2 kilometers, but at 45 degrees of latitude, one degree of longitude is 78.6 kilometers. While the physical distance over a single degree of longitude changes with latitude, we calculate only great-circle distances in degrees.
- KILOMETERS
- Kilometers of distance.
- MILES
- Miles of distance.
- METERS
- Meters of distance.
g.V().
has('location', 'point', Geo.inside(Geo.point(2.352222, 48.856614), 4.2, Geo.Unit.DEGREES)).
values('name')
lists:==>Paris
==>London
==>Aachen
Centering
the query on Paris and searching within 4.2 degrees returns three cities: Paris, London, and
Aachen from the dataset.g.V().
has('lineLocation', 'line', Geo.inside(Geo.point(2.352222, 48.856614), 9713, Geo.Unit.KILOMETERS)).
values('name')
lists:==>ParisLondon
==>LondonDublin
==>AachenTokyo
==>ParisAachen
Centering the query on Paris and searching within 9713 kilometers returns four stored
linestrings: Paris to London, London to Dublin, Aachen to Tokyo, and Paris to Aachen. Note
that London to Dublin was not a stored linestring.Find stored geospatial points or linestrings within a specified geospatial polygon
Polygons may be used in these queries with a search index on point if a JAR file from the JTS library is added to DSE Search. Be sure to add the JAR file before attempting to insert the graph data.
g.V().
has('location', 'point', Geo.inside(Geo.polygon(-6.26031, 53.349805, 6.083887, 50.775346, 139.691706, 35.689487))).
values('name')
lists:==>Dublin
==>Aachen
==>Tokyo
This results is not surprising, since the three points used to create the polygon represent
the three cities discovered.g.V().
has('location', 'point', Geo.inside(Geo.polygon(-7.9541015625, 55.148273231753834,-9.6240234375, 51.47539580264131,1.0986328125, 50.86924482345238,0.5712890625, 53.29887631763788,-7.9541015625, 55.148273231753834))).
values('name')
lists:==>London
==>Dublin
The
polygon used encompasses most of the Republic of Ireland as well as the southern half of the
United Kingdom, and finds London and Dublin within the polygon.g.V().
has('lineLocation', 'line', Geo.inside(Geo.polygon(-6.26031, 53.349805, 6.083887, 50.775346, 139.691706, 35.689487))).
values('name')
lists:==>AachenTokyo
Since two of the points in the specified polygon represent Aachen and Tokyo, it is
reassuring that the linestring of Aachen to Tokyo is found.Schema and data
//SCHEMA
// PROPERTY KEYS
// Check for previous creation of property key with ifNotExists()
schema.propertyKey('name').Text().ifNotExists().create()
schema.propertyKey('gender').Text().ifNotExists().create()
schema.propertyKey('location').Point().withGeoBounds().ifNotExists().create()
// VERTEX LABELS
schema.vertexLabel('author').properties('name','gender').ifNotExists().create()
schema.vertexLabel('place').properties('name','location').create()
// EDGE LABELS
schema.edgeLabel('livesIn').connection('author','place').ifNotExists().create()
// VERTEX INDEXES
// Secondary
schema.vertexLabel('author').index('byName').secondary().by('name').add()
// Search
schema.vertexLabel('author').index('search').search().by('name').asString().ifNotExists().add()
schema.vertexLabel('place').index('search').search().by('location')ifNotExists().add();
//VERTICES
// AUTHOR VERTICES
juliaChild = graph.addVertex(label, 'author', 'name','Julia Child', 'gender', 'F')
simoneBeck = graph.addVertex(label, 'author', 'name', 'Simone Beck', 'gender', 'F')
louisetteBertholie = graph.addVertex(label, 'author', 'name', 'Louisette Bertholie', 'gender', 'F')
patriciaSimon = graph.addVertex(label, 'author', 'name', 'Patricia Simon', 'gender', 'F')
aliceWaters = graph.addVertex(label, 'author', 'name', 'Alice Waters', 'gender', 'F')
patriciaCurtan = graph.addVertex(label, 'author', 'name', 'Patricia Curtan', 'gender', 'F')
kelsieKerr = graph.addVertex(label, 'author', 'name', 'Kelsie Kerr', 'gender', 'F')
fritzStreiff = graph.addVertex(label, 'author', 'name', 'Fritz Streiff', 'gender', 'M')
emerilLagasse = graph.addVertex(label, 'author', 'name', 'Emeril Lagasse', 'gender', 'M')
jamesBeard = graph.addVertex(label, 'author', 'name', 'James Beard', 'gender', 'M')
// PLACE VERTICES
newYork = graph.addVertex(label, 'place', 'name', 'New York', 'location', Geo.point(74.0059,40.7128));
paris = graph.addVertex(label, 'place', 'name', 'Paris', 'location', Geo.point(2.3522, 48.8566));
newOrleans = graph.addVertex(label, 'place', 'name', 'New Orleans', 'location', Geo.point(90.0715, 29.9511));
losAngeles = graph.addVertex(label, 'place', 'name', 'Los Angeles', 'location', Geo.point(118.2437, 34.0522));
london = graph.addVertex(label, 'place', 'name', 'London', 'location', Geo.point(-0.1278, 51.5074));
chicago = graph.addVertex(label, 'place', 'name', 'Chicago', 'location', Geo.point(-87.6298, 41.8781136));
tokyo = graph.addVertex(label, 'place', 'name', 'Tokyo', 'location', Geo.point(139.6917, 35.6895));
// EDGES
juliaChild.addEdge('livesIn', newYork);
simoneBeck.addEdge('livesIn', paris);
louisetteBertholie.addEdge('livesIn', london);
patriciaSimon.addEdge('livesIn', newYork);
aliceWaters.addEdge('livesIn', losAngeles);
patriciaCurtan.addEdge('livesIn', chicago);
kelsieKerr.addEdge('livesIn', tokyo);
fritzStreiff.addEdge('livesIn', tokyo);
emerilLagasse.addEdge('livesIn', newOrleans);
jamesBeard.addEdge('livesIn', london);
Of
course, this data can be loaded using the DSE Graph
Loader as well, from CSV or other formatted files.Find authors who live within a certain distance from a specified city in sorted order
g.V().
has('place', 'location', Geo.inside(Geo.point(74.0,40.5),50,Geo.Unit.DEGREES)).
values('name')
results
in:==>New York
==>Paris
==>New Orleans
==>Los Angeles
// Order by name, not by distance from location point given
g.V().has('place', 'location', Geo.inside(Geo.point(74.0,40.5),50,Geo.Unit.DEGREES)).
order().by('name').
as('Location').
in().as('Author').
select('Location','Author').
by('name').
by('name')
finds:==>{Location=Los Angeles, Author=Alice Waters}
==>{Location=New Orleans, Author=Emeril Lagasse}
==>{Location=New York, Author=Patricia Simon}
==>{Location=New York, Author=Julia Child}
==>{Location=Paris, Author=Simone Beck}
This
query uses some additional methods such as order()
and
select()
that are explained in Simple
Traversals.// Order by distance from NYC
g.V().has('place', 'location', Geo.inside(Geo.point(74.0,40.5),50,Geo.Unit.DEGREES)).
order().by{it.value('location').getOgcGeometry().distance(Geo.point(74.0059,40.7128).getOgcGeometry())}.
as('Location').
in().as('Author').
select('Location', 'Author').
by('name').
by('name')
==>{Location=New York, Author=Patricia Simon}
==>{Location=New York, Author=Julia Child}
==>{Location=New Orleans, Author=Emeril Lagasse}
==>{Location=Los Angeles, Author=Alice Waters}
==>{Location=Paris, Author=Simone Beck}
This
query introduces some additional methods 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;