Spatial. GIS. Hikes in Neo4j

The question is, how to place the hikes data saved in gpx file into neo4j using spatial functions.
I can easily extract the data from gpx files, parse them but then... how do I model the data structure in neo ?
I dont want to make a mess in the database. I Would like to use WGS84 crs in 3D space.

I can create a node with:
CREATE (n:hike { name: 'hike1', p1: point({x: 56.7, y: 12.78, z: 8.0}), p2: point({x: 56.7, y: 12.78, z: 8.0}) })
But then, will it be correctly stored in R-tree spatial structure which is inbuilt in neo ?

Any ideas ?

Hi @adrian.oramus -

I was recently playing around with something similar based on my Strava data export. Since each hike/route is a line geometry I chose to model the route as an array of Point values stored as a property on the node. In order to make it easier to import each route I converted all the .gpx files into geojson using the ogr2ogr CLI tool (but you could also use apoc.load.xml to parse gpx). Then in Cypher:

// Create Activity Nodes
LOAD CSV WITH HEADERS FROM "file:///activities.csv" AS row
MERGE (a:Activity {activity_id: row.`Activity ID`})
SET a.filename = row.Filename,
    a.activity_type = row.`Activity Type`,
    a.distance = toFloat(row.Distance),
    a.activity_name = row.`Activity Name`,
    a.activity_data = row.`Activity Date`,
    a.activity_description = row.`Activity Description`,
    a.max_grade = toFloat(row.`Max Grade`),
    a.elevation_high = toFloat(row.`Elevation High`),
    a.elevation_loss = toFloat(row.`Elevation Loss`),
    a.elevation_gain = toFloat(row.`Elevation Gain`),
    a.elevation_low = toFloat(row.`Elevation Low`),
    a.moving_time = toFloat(row.`Moving Time`),
    a.max_speed = toFloat(row.`Max Speed`),
    a.avg_grade = toFloat(row.`Average Grade`)

// Parse geojson geometries and create Geometry:Line nodes
MATCH (a:Activity) 
WITH a WHERE a.filename IS NOT NULL AND a.filename CONTAINS ".gpx"
MERGE (n:Geometry {geom_id:a.activity_id })
MERGE (n)<-[:HAS_FEATURE]-(a)
WITH n,a
CALL apoc.load.json('file:///' + replace(a.filename, '.gpx', '.geojson')) YIELD value
UNWIND value.features[0].geometry.coordinates AS coord
WITH n, collect(point({latitude: coord[1], longitude: coord[0]})) AS coords
SET n.coordinates = coords
SET n:Line

For operations like spatial search where we want to find hikes that have at least one point within some radius distance from a point, we can write a query like this:

WITH point({latitude: $latitude, longitude: $longitude}) AS radiusCenter
MATCH (g:Geometry) 
    WHERE any(
        p IN g.coordinates WHERE point.distance(p, radiusCenter) < $radius
    )
RETURN [n IN g.coordinates | [n.latitude, n.longitude]] AS route

You can find a few more similar examples in this GitHub repo.