Статьи

Neo4j: найдите промежуточную точку между двумя широтами / долготами

Вчера я написал сообщение в блоге, показывающее, как найти среднюю точку между двумя широтами / долготами, используя Cypher, что сработало как первая попытка заполнить пропущенные места, но я понял, что могу добиться большего.

Как я упоминал в предыдущем посте, когда я нахожу остановку, в которой отсутствуют координаты широты / долготы, я обычно могу найти две близлежащие остановки, которые позволяют мне триангулировать местоположение этой остановки.

У меня также есть маршруты поездов, которые указывают количество секунд, необходимое для перехода от одной остановки к другой, что позволяет мне указать, ближе ли остановка без местоположения к одной остановке, чем к другой.

Например, рассмотрим остановки a, b и c, где b не имеет местоположения. Если у нас есть эти расстояния между остановками:

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

это говорит нам о том, что точка «b» на самом деле равна 0.2 расстояния от «a» до «c», а не является средней точкой.

Есть формула, которую мы можем использовать, чтобы решить эту проблему:

01
02
03
04
05
06
07
08
09
10
11
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

В переводе на Cypher (с обязательными греческими символами) это выглядит так, чтобы найти точку 0.2 пути от одной точки к другой

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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
1
2
3
4
5
╒═════════════════╤════════════════════╕
│φi               │λi                  │
╞═════════════════╪════════════════════╡
51.49037311149128│-0.04880308288561931
└─────────────────┴────────────────────┘

Быстрая проверка работоспособности с подключением 0,5 вместо 0,2 находит середину, которую я смог проверить на соответствие вчерашнему посту:

1
2
3
4
5
╒═════════════════╤═════════════════════╕
│φi               │λi                   │
╞═════════════════╪═════════════════════╡
51.48613822097523│-0.050729537454086385
└─────────────────┴─────────────────────┘

На этом пока все!