Расчет расстояния и продолжительности действия пользователя из журналов GPS - PullRequest
1 голос
/ 17 июня 2020

Я работаю с наборами данных 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 выше показывает последовательность точек GPS для 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_start принимает значения lat, 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 здесь , содержащий точки GPS для 2 поездок для этого пользователя (по 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;

1 Ответ

2 голосов
/ 17 июня 2020

Для упрощения вычисления расстояний вам необходимо установить 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!

Дальнейшее чтение:

...