从gps日志计算用户的距离和持续时间

k5ifujac  于 2021-07-26  发布在  Java
关注(0)|答案(1)|浏览(399)

我正在为北京市的人口流动性的gps数据集工作。在我的原始gps表中 trajectories 是所有用户的gps序列:

CREATE TABLE trajectories
(
    user_id integer,
    session_id bigint NOT NULL,
    "timestamp" timestamp with time zone NOT NULL,
    lat double precision NOT NULL,
    lon double precision NOT NULL,
    alt double precision,
    CONSTRAINT trajectories_pkey PRIMARY KEY (session_id, "timestamp")
);

SELECT * FROM trajectories ORDER BY user_id, timestamp LIMIT 10;
 user_id |   session_id   |       timestamp        |    lat    |    lon     | alt 
---------+----------------+------------------------+-----------+------------+-----
       1 | 20081023025304 | 2008-10-23 02:53:04+01 | 39.984702 | 116.318417 | 492
       1 | 20081023025304 | 2008-10-23 02:53:10+01 | 39.984683 |  116.31845 | 492
       1 | 20081023025304 | 2008-10-23 02:53:15+01 | 39.984686 | 116.318417 | 492
       1 | 20081023025304 | 2008-10-23 02:53:20+01 | 39.984688 | 116.318385 | 492
       1 | 20081023025304 | 2008-10-23 02:53:25+01 | 39.984655 | 116.318263 | 492
       1 | 20081023025304 | 2008-10-23 02:53:30+01 | 39.984611 | 116.318026 | 493
       1 | 20081023025304 | 2008-10-23 02:53:35+01 | 39.984608 | 116.317761 | 493
       1 | 20081023025304 | 2008-10-23 02:53:40+01 | 39.984563 | 116.317517 | 496
       1 | 20081023025304 | 2008-10-23 02:53:45+01 | 39.984539 | 116.317294 | 500
       1 | 20081023025304 | 2008-10-23 02:53:50+01 | 39.984606 | 116.317065 | 505
(10 rows)

上面的select查询显示了 user 1 ,从当前行程的起点开始( session_id=20081023025304 ). 我想使用此表中的原始数据将计算出的出行指标插入到一个新表中,我定义为:

CREATE TABLE trip_metrics(
  user_id INT,
  session_id BIGINT,
  lat_start DOUBLE PRECISION,
  lat_end DOUBLE PRECISION,
  lon_start DOUBLE PRECISION,
  lon_end DOUBLE PRECISION,
  trip_starttime timestamp,
  trip_endtime timestamp,
  trip_duration DOUBLE PRECISION,
  trip_distance DOUBLE PRECISION,
  PRIMARY KEY (user_id, session_id, trip_starttime)
  );

关键是 trip_metrics 表是用来存储分析结果的,以便 lat_start, lon_startlat, lon 起始位置(在给定示例中: 39.984702, 116.318417 ), trip_starttimestamp 需要开始时间(在这种情况下 2008-10-23 02:53:04+01 )所以 lat_end, lon_end, trip_endtime 分别。
最后使用 lat_start/end, lon_start/end 来计算这个用户在这次旅行中所覆盖的距离。最终结果如下:

+---------+----------------+-----------+-----------+------------+------------+------------------------+------------------------+---------------+---------------+
| user_id |   session_id   | lat_start |  lat_end  | lon_start  |  lon_end   |     trip_starttime     |      trip_endtime      | trip_duration | trip_distance |
+---------+----------------+-----------+-----------+------------+------------+------------------------+------------------------+---------------+---------------+
|       1 | 20081023025304 | 39.984702 | 39.984606 | 116.318417 | 116.317065 | 2008-10-23 02:53:04+01 | 2008-10-23 02:53:50+01 |               |               |
+---------+----------------+-----------+-----------+------------+------------+------------------------+------------------------+---------------+---------------+

价值观 trip_duration 以及 trip_distance 计算(当然是 trip_duration 会是 trip_endtime - trip_starttime ).
我已经在我的研究中停留了几天,在思考如何在中国做到这一点 PostgrSQL 数据库过滤仅限北京市内出行 latitude (39.85 - 40.05) 以及 longitude (116.25 - 116.5) 因为有些旅行跨越了城市。我在这里创建了一个db fiddle,其中包含该用户2次旅行的gps点(每个10点)。
我将感谢任何指导工作围绕这一点取得进展,在我目前的研究。
编辑
遇到这个函数可以用哈弗公式计算距离。我创建了这个函数,但我不知道如何使用它来获取 trip_distance 价值观。

CREATE OR REPLACE FUNCTION distance(
    lat1 double precision,
    lon1 double precision,
    lat2 double precision,
    lon2 double precision)
  RETURNS double precision AS
$BODY$
DECLARE
    R integer = 6371e3; -- Meters
    rad double precision = 0.01745329252;

    φ1 double precision = lat1 * rad;
    φ2 double precision = lat2 * rad;
    Δφ double precision = (lat2-lat1) * rad;
    Δλ double precision = (lon2-lon1) * rad;

    a double precision = sin(Δφ/2) * sin(Δφ/2) + cos(φ1) * cos(φ2) * sin(Δλ/2) * sin(Δλ/2);
    c double precision = 2 * atan2(sqrt(a), sqrt(1-a));    
BEGIN                                                     
    RETURN R * c;        
END  
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
doinxwow

doinxwow1#

为了更容易地计算距离,您必须安装 PostGIS extension ,正如您在标签中所建议的:

CREATE EXTENSION postgis;

函数 ST_Distance 是您想要的,例如(快速和肮脏):

WITH j AS (
  SELECT user_id, session_id, 
    max(timestamp ORDER BY timestamp),
    min(timestamp ORDER BY timestamp) 
  FROM trajectories t
  GROUP BY user_id,session_id  
) 
SELECT 
  s.user_id,s.session_id, 
  lat_start,lon_start, 
  lat_end,lon_end, 
  trip_starttime, 
  trip_endtime,
  age(trip_endtime,trip_starttime),
  ST_Distance(
    ST_MakePoint(lon_start,lat_start)::geography,
    ST_MakePoint(lon_end,lat_end)::geography) AS trip_distance
FROM 
  (SELECT 
    j.user_id, j.session_id, 
    t.timestamp AS trip_starttime,
    lat AS lat_start, lon AS lon_start FROM j
   JOIN trajectories t ON t.timestamp = j.min 
     AND t.session_id = j.session_id AND t.user_id = j.user_id) s,
  (SELECT 
    j.user_id, j.session_id, 
    t.timestamp AS trip_endtime,
    lat AS lat_end,lon AS lon_end FROM j
   JOIN trajectories t ON t.timestamp = j.max 
     AND t.session_id = j.session_id AND t.user_id = j.user_id) e
WHERE s.user_id = e.user_id AND s.session_id = e.session_id;

 user_id |   session_id   | lat_start | lon_start |  lat_end  |  lon_end   |     trip_starttime     |      trip_endtime      |   age    |  trip_distance   
---------+----------------+-----------+-----------+-----------+------------+------------------------+------------------------+----------+------------------
       1 | 20081023025304 | 39.984702 | 16.318417 | 39.984606 | 116.317065 | 2008-10-23 03:53:04+02 | 2008-10-23 03:53:50+02 | 00:00:46 | 8012597.30391588

另一方面:将经度和纬度存储在分开的列中几乎总是一个坏主意。如果可能,将它们存储到几何体或地理列中。一开始似乎很有必要,但postgis提供了大量的 kickass functions !
进一步阅读:
ST_MakePoint Date/Time Functions

相关问题