· neo4j

Neo4j: Find the intermediate point between two lat/longs

Yesterday I wrote a blog post showing how to find the midpoint between two lat/longs using Cypher which worked well as a first attempt at filling in missing locations, but I realised I could do better.

As I mentioned in the last post, when I find a stop that's missing lat/long coordinates I can usually find two nearby stops that allow me to triangulate this stop's location.

I also have train routes which indicate the number of seconds it takes to go from one stop to another, which allows me to indicate whether the location-less stop is closer to one stop than the other.

For example, consider stops a, b, and c where b doesn't have a location. If we have these distances between the stops:


(a)-[:NEXT {time: 60}]->(b)-[:NEXT {time: 240}]->(c)

it tells us that point 'b' is actually 0.2 of the distance from 'a' to 'c' rather than being the midpoint.

There's a formula we can use to work out that point:


a = sin((1−f)⋅δ) / sin δ
b = sin(f⋅δ) / sin δ
x = a ⋅ cos φ1 ⋅ cos λ1 + b ⋅ cos φ2 ⋅ cos λ2
y = a ⋅ cos φ1 ⋅ sin λ1 + b ⋅ cos φ2 ⋅ sin λ2
z = a ⋅ sin φ1 + b ⋅ sin φ2
φi = atan2(z, √x² + y²)
λi = atan2(y, x)

δ is the angular distance d/R between the two points.
φ = latitude
λ = longitude

Translated to Cypher (with mandatory Greek symbols) it reads like this to find the point 0.2 of the way from one point to another

~~~cypher with {latitude: 51.4931963543, longitude: -0.0475185810} AS p1, {latitude: 51.47908, longitude: -0.05393950 } AS p2 WITH p1, p2, distance(point(p1), point(p2)) / 6371000 AS δ, 0.2 AS f WITH p1, p2, δ, sin((1-f) * δ) / sin(δ) AS a, sin(f * δ) / sin(δ) AS b WITH radians(p1.latitude) AS φ1, radians(p1.longitude) AS λ1, radians(p2.latitude) AS φ2, radians(p2.longitude) AS λ2, a, b WITH a * cos(φ1) * cos(λ1) + b * cos(φ2) * cos(λ2) AS x, a * cos(φ1) * sin(λ1) + b * cos(φ2) * sin(λ2) AS y, a * sin(φ1) + b * sin(φ2) AS z RETURN degrees(atan2(z, sqrt(x^2 + y^2))) AS φi, degrees(atan2(y,x)) AS λi ~~~ ~~~text ╒═════════════════╤════════════════════╕ │φi │λi │ ╞═════════════════╪════════════════════╡ │51.49037311149128│-0.04880308288561931│ └─────────────────┴────────────────────┘ ~~~

A quick sanity check plugging in 0.5 instead of 0.2 finds the midpoint which I was able to sanity check against yesterday's post:


╒═════════════════╤═════════════════════╕
│φi               │λi                   │
╞═════════════════╪═════════════════════╡
│51.48613822097523│-0.050729537454086385│
└─────────────────┴─────────────────────┘

That's all for now!

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket