Distance between sequential points postgres - postgresql

My table is as follows
1;"2015-10-02";"POINT(lat,lon) as geometry"
2;"2015-10-03";"POINT(lat,lon) as geometry"
3;"2015-10-04";"POINT(lat,lon) as geometry"
How can I find the distance between two sequential points?
So I'd have id=1 and id=2 distance between them = 99m (distances would be found between [1,2],[2,3],[3,4] and so on
then if distance < 100m aggregate them
I have not go very far with it
This gives me the distance but I don't know how to get the next row's geometry
SELECT st_distance_sphere(t.latlon,next.latlon) from test as t where id=1
Then I tried to read the distance as a additional column but could figure out a correct query
UPDATE test SET dist=ST_Distance(test.latlon, next.geom)
FROM (SELECT latlon FROM test WHERE id = test.id + 1) into b;
1;"2015-10-02";"POINT(lat,lon) as geometry";distance between 1 and 2
2;"2015-10-03";"POINT(lat,lon) as geometry";distance between 2 and 3
3;"2015-10-04";"POINT(lat,lon) as geometry";distance between 3 and 4

To take the distance between current point and next point you can use a window function lead like this:
select
test.*,
st_distance(latlon, lead(latlon, 1, latlon) over(order by id)) as distance
from test;

Related

What is wrong with my ST_Within query - query result contains point twice although it exists only once

I have two point tables, tab_1 and tab_2. I want to query all points from the first table that are probably the same points from the table 2. So i give the points from table 2 a buffer. Then I want to get the points from table 1 and query from table 2 within a 30 m buffer. My problem is, I get the points from table 1 and table 2 twice. But point 1 from table 1 exists only once and point 1 from table 2 also only once.
My query is:
with
"points1" as
(
select id, geom from tab_1
)
,
"points2" as
(
select id, geom from tab_2
)
select "points1".*, "points2".* from "points1", "points2"
where
st_within(st_transform("points1".geom, 31468), st_buffer(st_transform("points2".geom, 31468), 30)) = true;
id_tab1
geom
id_tab2
geom
st_distance
767074270
POINT (11.6968379 48.132722)
16455
POINT (11.69707 48.13265)
19.041083533921977
767074270
POINT (11.6968379 48.132722)
16455
POINT (11.69707 48.13265)
19.041083533921977
The query should be give only one result:
id_tab1
geom
id_tab2
geom
st_distance
767074270
POINT (11.6968379 48.132722)
16455
POINT (11.69707 48.13265)
19.041083533921977
Is my query wrong?
STEP 1. Query
SELECT *
FROM tab_1
JOIN tab_2
ON ST_DWithin
( ST_Transform(tab_1.geom, 31468)
, ST_Transform(tab_2.geom, 31468)
, 30
)
STEP 2. Spatial index
Most likely, the query cannot use the spatial index (even if it exists) and the function ST_DWithin() properly (ST_Transform() does not allow using an existing spatial index).
Solution - create new spatial indexes for EPSG:31468
CREATE
INDEX tab_1_geom_31468_idx
ON tab_1
USING GIST (ST_Transform(geom, 31468))
;
CREATE
INDEX tab_2_geom_31468_idx
ON tab_2
USING GIST (ST_Transform(geom, 31468))
;

How to optimze the query (which tooks to long)

I have a query which I want to know relatively how many locations are up to 100 meters away (relate to all distances):
select person_tbl.tdm, sum((st_distance (person_tbl.geo, location_tbl.geo) < 100)::INT)::FLOAT / count(*)
from persons as person_tbl, locations as location_tbl
where person_tbl.geo is not null
group by person_tbl.tdm
The 2 tables contains geometry indexs:
create index idx on persons using gist(geo)
create index idx on locations using gist(geo)
The first table (persons) the values of geo field is POLYGON
The second table (locations) the values of geo field are POINT Z or POLYGON Z or MULTIPOLYGON Z
The first table persons contains ~2M rows and the second table locations contains ~500 rows
The query took too long (~2 hours).
The values of max_parallel_processes and max_parallel_workers is 8
Is there something I can do to optimize the query calculation time (2 hours seems too long) ?
Is there a better way to write the query ? or do I need to define the indexes in other way ?

Create new row when condition is not right in postgis

I have executed this query:
SELECT
id, user_id, device_id, date(creation_date_time) as day, shape
from olocations
where user_id = 'd0edfc59-9923-44c3-9c34-ef5aad3cb810'
and device_id = '89984320001811791540'
group by id, creation_date_time
ORDER BY creation_date_time ASC
And it is return this:
All data is ordered by time and ready to distance comparison.
It is possible to check distance between points (point1 with point2 - point2 with point3 and so on) if it was for example more than 10 meter, make new row and points that have correct condition inserted as a group into one row else?
I know that st_dwithin command can check distance but how can i change row when distances between geo points more than 10?

Calculate the trajectories of each ship based on an AIS dataset

I have a table of kinematic data (position reports from vessels). The table has the following rows:
row name: type: Description:
timestamp double precision timestamp in UNIX epochs (seconds from 1/1/1970)
type integer AIS message type
mmsi integer MMSI identifier for vessel
status integer Navigational status
lon double precision Longitude (georeference: WGS 1984)
lat double precision Latitude (georeference: WGS 1984)
heading integer True heading in degrees (0-359), relative to true north
turn double precision Rate of turn, right or left, 0 to 720 degrees per minute
speed double precision Speed over ground in knots (allowed values: 0-102.2 knots)
course double precision Course over ground (allowed values: 0-359.9 degrees).
After I imported the data, I added one more row of type geometry formed using the lon, lat and timestamp rows:
SELECT AddGeometryColumn ('public', 'gis_may', 'geom_time', 4326, 'POINTZ', 3);
UPDATE public.gis_may SET geom_time = ST_Transform(ST_SetSRID(ST_MakePoint(lon, lat, gis_may.timestamp ), 4326),4326);
I have to calculate the trajectories of every ship, based on this data. I tried using the code below:
CREATE TABLE ship_trajectories AS SELECT st_makeline(st_setsrid(st_makepoint(lon::REAL, lat::REAL, gis_may.timestamp), 4326)) as traj
FROM gis_may
GROUP BY mmsi;
…but the problem is, this code returns only one linestring/trajectory based only on the coordinates of each ship and does not take into account the times the ship stopped moving.
I think the solution to this is using the row: status (I searched and found what navigational status is: https://help.marinetraffic.com/hc/en-us/articles/203990998-What-is-the-significance-of-the-AIS-Navigational-Status-Values-). When status is 0 it means the ship is moving and when it is 1 it means it is anchored so it no longer forms a trajectory.
I have worked on a project which solved the same problem by first looking at all AIS-points to determine trajectory breaking-points. If you think that the AIS-variable status is enough for your purposes, then good for you. You could check each instance of status changing to 0. Be aware that AIS-data has other status values too. See this.
What if, within each mmsi, you would like to mark the row when a ship goes from status 1 to status 0. This would create the points of delimitation for your trajectories to begin with. Look at this after first adding your longitude-latitude coordinations as a point geometry called gis_point:
-- Make a point geometry and work with those.
SELECT AddGeometryColumn('public', 'gis_may', 'gis_point', 4326, 'POINT', 2);
UPDATE gis_may SET gis_point = ST_Transform(ST_SetSRID(
ST_MakePoint(longitude, latitude),
4326), 4326);
-- Look ahead to keep stuff within the same mmsi. Create a switch for when the status changes:
CREATE TABLE lookahead AS
SELECT aislag.mmsi, aislag.timestamp1, aislag.gis_point, EXTRACT(EPOCH FROM (aislag.ts2 - aislag.ts1)) AS timediff, aislag.g,
CASE WHEN aislag.status = '1' AND aislag.newstatus ='0' THEN 'Start' ELSE '.' END AS newtrajectory
FROM
(SELECT mmsi, timestamp as timestamp1, status, gis_point,
LEAD(mini_c.timestamp) OVER (ORDER BY mmsi, timestamp) AS timestamp2,
LEAD(mini_c.mmsi) OVER (ORDER BY mmsi, timestamp) AS mmsi2,
mini_c.status AS status,
LEAD(mini_c.status) OVER (ORDER BY mmsi, timestamp) AS newstatus
FROM mini_c) AS aislag
WHERE aislag.mmsi = aislag.mmsi2
I've added a lagged time-variable too. Maybe you would like to enlarge the status-switcher to include observations with obsene time-differences to next-following AIS-point for that same ship. Now that you have your delimiters, you can create a new id for rows in lookahead which follows the mmsi, ts-sorting. Then make a new table trajectories with pairs of those ids as id1 and id2 from a lagged selection on lookahead WHERE newstatus = 'Start' so that you get a row for each distance between switches of status. That table basically has trajectories as observations. Joining on the lookahead you can make your line-geometries per trijectory with ST_Makeline(gis_point) using WHERE newid BETWEEN trajectories.is1 AND trajectories.id2 and GROUP BY mmsi.
Sorry for not writing out all the code.
A more advanced approach is to use the actual AIS-data to look at rolling average speeds and distances over time for each mmsi to determine where the ship appears to be standing still for longer periods. The logic above to distinguish delimiters for trajectories would than apply in the same way, only that the AIS-variable switch of status is exchanged for a calculation of behavioral patterns of ships.
Best of luck

Unit of return value of ST_Distance

I need to calculate the distance between
all buildings and
all hospitals
in a map imported from OSM.
I use following query:
SELECT building_id, hospital_id, ST_Distance(building_centroid, hospital_location)
FROM
(
select planet_osm_polygon.osm_id building_id, ST_Centroid(planet_osm_polygon.way) building_centroid
from planet_osm_polygon
where building = 'yes'
) buildings,
(
select planet_osm_point.osm_id hospital_id, planet_osm_point.way hospital_location
from planet_osm_point
where amenity = 'hospital') hospitals
I get strange results - the distance is always smaller than 1.
How can I get the to know the unit, in which these values are reported?
Update 1: Sample result:
Update 2: This query seems to work
SELECT building_id, hospital_id, ST_Distance_sphere(building_centroid, hospital_location) distance
FROM
(
select planet_osm_polygon.osm_id building_id, ST_Centroid(planet_osm_polygon.way) building_centroid
from planet_osm_polygon
where building = 'yes'
) buildings,
(
select planet_osm_point.osm_id hospital_id, planet_osm_point.way hospital_location
from planet_osm_point
where amenity = 'hospital') hospitals
ORDER BY distance
The general rule for units is that the output length units are the same as the input length units.
The OSM way geometry data has length units of degrees of latitude and longitude (SRID=4326). Therefore, the output units from ST_Distance will also have lenth units of degrees, which are not really useful.
There are several things you can do:
Use ST_Distance_Sphere for fast/approximate distances in metres
Use ST_Distance_Spheroid for accurace distances in metres
Convert the lat/long geometry data types to geography, which automagically makes ST_Distance and other functions to use linear units of metres