Create new row when condition is not right in postgis - postgresql

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?

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 ?

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

Postgis nearest coordinates

I'm trying to make a REST service that returns a list of places ordered by distance from the user coordinate. I found this query using postgis:
SELECT *
FROM your_table
ORDER BY your_table.geom <-> "your location..."
LIMIT 5;
But I'm not able to apply this to my actual database. I have a table that contains these columns:
title, address, description, latitude, longitude
all these values as Strings.
I'll be very happy if someone help me. Thx!
I dont know why, but ORDER BY <-> isnt exact. Sometime the closest link is on the 3rd position. So I get 101 element and then use distance to selected the closest one.
CREATE OR REPLACE FUNCTION map.get_near_link(
x numeric,
y numeric)
RETURNS TABLE(Link_ID int, distance int) AS
$BODY$
DECLARE
strPoint text;
BEGIN
strPoint = 'POINT('|| X || ' ' || Y || ')';
With CTE AS (
SELECT Link_ID,
TRUNC(ST_Distance(ST_GeomFromText(strPoint,4326), geom )*100000)::integer as distance
FROM map.vzla_seg S
ORDER BY
geom <-> ST_GeomFromText(strPoint, 4326)
LIMIT 101
)
SELECT *
FROM CTE
ORDER BY distance
LIMIT 5
In order to use PostGIS you have to enable the extension in the database. Ideally, you just run the CREATE EXTENSION postgis; command and it works. NOTE form the install page: DO NOT INSTALL it in the database called postgres. For more information visit the site.
Adding a geometry column (spatial data can be stored in this type of columns) to your table:
SELECT AddGeometryColumn(
'your_schema',
'your_table',
'geom', -- name of the column
4326, -- SRID, for GPS coordinates you can use this, for more information https://en.wikipedia.org/wiki/Spatial_reference_system
'POINT', -- type of geometry eg. POINT, POLYGON etc.
2 -- number of dimension (2 xy - 3 xyz)
);
UPDATE yourtable t SET t.geom = ST_SetSRID(ST_MakePoint(t.x, t.y), 4326)
-- the x and y is the latitude and longitude
Now you can use spatial queries on your table like this:
SELECT
*
FROM
your_table
ORDER BY
your_table.geom <-> ST_SetSRID(ST_MakePoint(x, y), 4326)
LIMIT 5;
NOTE: as others mentioned, below PostgreSQL 9.5 <-> isn't always reliable.

Distance between sequential points postgres

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;