[GIS] Map-matching GPS points to the road network!

gpsmap-matchingpostgis

I am quite new to Postgres and PostGIS.

I am trying to do a simple map-matching (or path-inference if you wish) of some GPS points. I am using postgres(postgis).

I have a table in my database containing my GPS points:
gpspoints (Lat, Long, time, speed, azimuth, geometry, …)

I have imported a shapefile of the road network into my database as well:
routes (gid, idrte,version, nomrte, norte, clsrte, geom, …) — I DO NOT HAVE the azimuth of the links, and I am not sure how can I calculate it using the postgis function ST_azimuth. All I have is a shapefile of the road network containing columns enumerated above.

I want to associate each GPS point to the nearest link (in a buffer of 20 meters around the point) only if the direction of the link agrees with the azimuth of the GPS point (+ or -15 degrees) and retrieve the projected position. Otherwise I want it to search for the NEXT nearest link, within the 20 meter buffer, which has an acceptable azimuth! (Just like the picture!)

I want the new coordinates of the projected GPS points to be added in the table "gpspoints" as "projectedLat" and "projectedLong".

(In the picture below, actual points are demonstrated using a direction while projected points do not have any direction mark)
enter image description here

Best Answer

I don't have a fully worked out answer, but maybe enough to get you started. These functions might help:

ST_Line_Locate_Point() gives the distance as a portion of the total line length of a point along a line. ST_Line_Interpolate_Point() returns a point geometry for a point at a given distance (again as a portion of the total length) along a line.

What I suggest it to get the point geometry of a location 'new_pts' on your line which are closest to your gpspoints (in a subquery below, formulated as a WITH clause). Then use that point to find the nearest point actually on the line, but at a very slightly smaller distance, so it will be closer to the beginning. Next use this found point, together with the 'new_pt' to get the azimuth between them. This would more or less represent the tangent to the line at that point.

Then you can compare this angle to the azimuth of your gpspoints to determine whether to add the gpspoint or not.

with new_pts AS (
    SELECT ST_ClosestPoint(geom, gpspoints) AS geompt
    FROM routes JOIN gpspoints ON ST_Distance(routes.geom, gpspoints.geom)<20
    )


SELECT ST_Azimuth(new_pts.geompt, 
    ST_Line_Interpolate_Point(routes.geom, 
        ST_Line_Locate_Point(routes.geom, new_pts.geompt)*0.999))

FROM routes JOIN new_pts ON ST_Distance(routes.geom,new_pts.geompt)<20;

Again, not tested, but I hope it will be some help.

Related Question