地理半径内的事件数

Number of events within a geo-radius

我有一长串仅用时间、经度和纬度表示的事件。我已经设法在 grafana 中看到结果,在 grafana 的“世界地图插件”中将每个事件描述为一个点。

对于当前的地理走廊,我想均匀地安排一些点,例如。相距0.25公里。每个事件,无论何时发生,都应计算在 5 公里范围内,并针对该特定点显示此测量值。

我已经读过地理距离,因为距离不是太大地球的形状在这里不相关,这应该足够了(python3 代码):

lat = (lat1 + lat2) / 2 * 0.01745
dx = 111.3 * math.cos(lat) * (lon1 - lon2)
dy = 111.3 * (lat1 - lat2)
distance = math.sqrt(dx * dx + dy * dy)

这将导致 n*(n-1)/2 次计算,这很容易打破我想投入的时间。所以我第一步只使用5公里宽的“走廊”可能没问题,尽管距离特别是角落里的距离会很大。

我应该用所有这些新“点”创建一个新数据库(例如 sqlite)吗?这可以在 influx 或 grafana 中实现吗?我不确定这里最好的方法是什么。

是否有一种优雅的方法(例如在 python3 中)来“加载”所有这些点并在内存中进行计算?每次都查询 influxdb 在这里听起来不太合适。

经过深思熟虑,我终于想出了一个“在某种程度上可行”的解决方案。我对它不太满意,因为它仍然需要很多时间并且在很大程度上取决于数据库中没有太多事件。

事件被加载到内存中,因此只访问数据库一次。计算每个点到任何点的距离,无论它是否在范围内,这需要更多调整,但我会处理的。

如果你们有任何关于如何改进这个的想法,请在这里告诉我。

#!/usr/bin/python3
import math
import sqlite3
from influxdb import InfluxDBClient
# Connection to the InfluxDB
client = InfluxDBClient(host='localhost', port=8086)
client.get_list_database()
client.switch_database('geopoints')
# Which geo corridor should be searched
startinglat = 5.4
stoplat = 5.7
startinglon = 33.2
stoplon = 33.7
# Select the events in this range from influxdb
results = client.query(f'SELECT * FROM "events" WHERE lat>={startinglat} AND lat<={stoplat} AND lon>={startinglon} AND lon<={stoplon}')
# Connect to the sqlite3-DB, this is our output
con = sqlite3.connect('/opt/sqlite3dbs/part.db')
cur = con.cursor()
# Create table in sqlite3
cur.execute('''DROP TABLE geopoints''')
cur.execute('''CREATE TABLE geopoints (lon real, lat real, count int)''')
# How many steps from lat-to-lat and lon-to-lon
step = 100
# Events in this radius should be counted
reqdist = 3
for lat1 in [round(x * (1/step),3) for x in range(int(startinglat * step), int(stoplat * step))]:
  print (f"Lat: {lat1}   ", end='\r')
  for lon1 in [round(x * (1/step),3) for x in range(int(startinglon * step), int(stoplon * step))]:
    count1 = 0
    for m2 in results.get_points():
      lon2 = m2['lon']
      lat2 = m2['lat']
      # Distance calculation
      lat = (lat1 + lat2) / 2 * 0.01745
      dx = 111.3 * math.cos(lat) * (lon1 - lon2)
      dy = 111.3 * (lat1 - lat2)
      distance = math.sqrt(dx * dx + dy * dy)
      if (distance < reqdist):
        count1+=1
    # Insert the number of events within the required distance to this point in sqlite3
    cur.execute(f"INSERT INTO geopoints VALUES ({lon1}, {lat1}, {count1})")
con.commit()
con.close()