All Products
Search
Document Center

PolarDB:Quick start

Last Updated:Mar 28, 2026

This guide walks you through a complete map matching workflow using GanosBase in PolarDB for PostgreSQL: install the extension, load road network data, build a topology workspace, and match a trajectory to the road network.

Key concepts

  • Road network: a structured digital representation of roads, including their locations, types, connections, geometric shapes, and traffic rules.

  • Node: a point where roads intersect or terminate. Nodes are the connection points between edges.

  • Edge: an abstract link between two nodes. In a directed graph, an edge has a travel direction. In an undirected graph, it does not. Edges carry attributes such as length and passability, but their primary role is to define the structural framework of a road network.

  • Segment: a physical road section with geographic attributes — road surface, width, slope, lane configuration, speed limit, and traffic rules. Segments are the basic units in navigation, transportation analysis, and road maintenance.

Prerequisites

Before you begin, make sure you have:

  • A PolarDB for PostgreSQL cluster with GanosBase enabled

  • Database superuser privileges (required to create extensions)

  • (For Example 2) An Object Storage Service (OSS) bucket containing the OSM data file

Install the extension

Run the following commands in your database to install the ganos_mapmatching and ganos_fdw extensions:

CREATE EXTENSION ganos_mapmatching CASCADE;
CREATE EXTENSION ganos_fdw CASCADE;

Installing ganos_mapmatching also installs the ganos_geometry, ganos_spatialref, and ganos_trajectory extensions automatically.

To avoid permission issues, install the extensions in the public schema:
CREATE EXTENSION ganos_mapmatching WITH SCHEMA PUBLIC CASCADE;
CREATE EXTENSION ganos_fdw WITH SCHEMA PUBLIC CASCADE;
If you see ERROR: invalid extension name: "ganos_trajectory", contact Alibaba Cloud support.

Example 1: Match trajectories using your own road network data

This example uses a synthetic road network to walk through the full workflow: load data, build a workspace, and match a trajectory.

Step 1: Load road network data

Create the network table and insert road segment data. Each row defines one directed edge with a start node (source), an end node (target), a cost weight, and a LineString geometry.

CREATE TABLE network (
    fid    bigint,                               -- Unique segment identifier
    source bigint,                               -- Start node ID
    target bigint,                               -- End node ID
    cost   double precision,                     -- Edge weight (not currently used)
    geom   public.geometry(LineString, 4326)     -- Segment geometry
);

INSERT INTO network(fid, source, target, cost, geom)
VALUES(1, 1, 2, 1, st_geomfromtext('LINESTRING(2 1,2 0)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(2, 2, 1, 1, st_geomfromtext('LINESTRING(2 0,2 1)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(3, 3, 1, 1, st_geomfromtext('LINESTRING(3 1,2 1)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(4, 4, 3, 1, st_geomfromtext('LINESTRING(4 1,3 1)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(5, 1, 5, 1, st_geomfromtext('LINESTRING(2 1,2 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(6, 5, 1, 1, st_geomfromtext('LINESTRING(2 2,2 1)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(7, 3, 6, 1, st_geomfromtext('LINESTRING(3 1,3 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(8, 7, 8, 1, st_geomfromtext('LINESTRING(0 2,1 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(9, 8, 7, 1, st_geomfromtext('LINESTRING(1 2,0 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(10, 5, 8, 3, st_geomfromtext('LINESTRING(2 2,1 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(11, 8, 5, 3, st_geomfromtext('LINESTRING(1 2,2 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(12, 6, 5, 1, st_geomfromtext('LINESTRING(3 2,2 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(13, 5, 6, 1, st_geomfromtext('LINESTRING(2 2,3 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(14, 6, 9, 1, st_geomfromtext('LINESTRING(3 2,4 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(15, 9, 6, 1, st_geomfromtext('LINESTRING(4 2,3 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(16, 10, 5, 1, st_geomfromtext('LINESTRING(2 3,2 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(17, 5, 10, 1, st_geomfromtext('LINESTRING(2 2,2 3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(18, 6, 11, 1, st_geomfromtext('LINESTRING(3 2,3 3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(19, 10, 11, 1, st_geomfromtext('LINESTRING(2 3,3 3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(20, 11, 12, 1, st_geomfromtext('LINESTRING(3 3,4 3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(21, 13, 10, 1, st_geomfromtext('LINESTRING(2 4,2 3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(22, 10, 13, 1, st_geomfromtext('LINESTRING(2 3,2 4)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(23, 9, 12, 1, st_geomfromtext('LINESTRING(4 2,4 3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(24, 12, 9, 1, st_geomfromtext('LINESTRING(4 3,4 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(25, 9, 4, 1, st_geomfromtext('LINESTRING(4 2,4 1)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(26, 4, 9, 1, st_geomfromtext('LINESTRING(4 1,4 2)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(27, 14, 15, 1, st_geomfromtext('LINESTRING(0.5 3.5,2 3.5)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(28, 15, 14, 1, st_geomfromtext('LINESTRING(2 3.5,0.5 3.5)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(29, 16, 17, 1, st_geomfromtext('LINESTRING(3.5 4,3.5 2.3)'));
INSERT INTO network(fid, source, target, cost, geom)
VALUES(30, 17, 16, 1, st_geomfromtext('LINESTRING(3.5 2.3,3.5 4)'));

The resulting road network looks like this:

image

Step 2: Build a map matching workspace

st_createworkspace builds the topological network graph from your road network data and stores it in a named workspace. The second argument is a SQL query that must return fid, source, target, and geom.

SELECT st_createworkspace('mm_ws_test1', 'select fid, source, target, geom from network');

The topology generated from the road network:

image

Step 3: Create trajectory data

CREATE TABLE traj(id int, geom geometry(linestring, 4326));

INSERT INTO traj(id, geom)
VALUES(1, st_geomfromtext('LINESTRING(4.15 1.6,3.47 0.92,2.4 0.92,2.14 1.53,2.14 2.57,2.49 2.98)'));

The input trajectory (GPS points to be matched):

image

Step 4: Run map matching

st_mapmatching snaps the trajectory to the road network using the ST-Matching algorithm. The third argument is a JSON object specifying algorithm parameters.

SELECT st_astext(
    st_mapmatching(
        'mm_ws_test1',
        geom,
        '{"algorithm":"stmatch","k":4,"r":0.25,"e":0.5}'::text
    )
) AS geom
FROM traj;

Algorithm parameters:

ParameterDescriptionValue in this example
algorithmMatching algorithm. Use stmatch for the ST-Matching algorithm.stmatch
kMaximum number of road segment candidates to consider for each GPS point. Higher values improve accuracy but increase processing time.4
rSearch radius in degrees. GPS points outside this radius from a road segment are excluded from candidates. Increase this value if your GPS data has low accuracy.0.25
eHidden Markov Model (HMM) observation error threshold. Controls how much deviation from the road is tolerated.0.5

Expected output:

geom
----------------------------------------------
 LINESTRING(4 1.6,4 1,3 1,2 1,2 2,2 3,2.49 3)
(1 row)

The matched trajectory snapped to the road network:

image
If the result is empty, check that the trajectory coordinates fall within the coverage area of your road network. If no road segment is within the search radius r of any GPS point, the function returns no match.

Example 2: Match trajectories using OpenStreetMap data

This example imports real-world road data from OpenStreetMap (OSM) for Macao (China) and runs map matching against it. Steps 1–3 prepare the data; Steps 4–5 follow the same workspace-and-match pattern as Example 1.

Step 1: Import OSM data from OSS

OSM data must be imported from an OSS bucket. Download the sample OSM file and upload it to your bucket first.

Register the OSM file as a foreign table and configure the OSS endpoint:

-- Register the OSM file as a foreign table.
-- Replace the placeholders with your AccessKey pair, endpoint, bucket name, and folder path.
-- The directory value should be the folder path without a leading slash.
SELECT st_regforeigntables(
    'OSS://<accesskeyid>:<accesskeysecret>@<endpoint>/<bucket>/<directory>/macau-latest.osm.pbf',
    'osmserver',
    'OSMNT'
);
SELECT st_setcplconfig('OSS_ENDPOINT', '<endpoint>');

Replace the following placeholders:

PlaceholderDescription
<accesskeyid>Your Alibaba Cloud AccessKey ID
<accesskeysecret>Your Alibaba Cloud AccessKey secret
<endpoint>The OSS endpoint for the region where your bucket is located
<bucket>Your OSS bucket name
<directory>The folder path within the bucket (omit the leading /)

Step 2: Extract road network node data

Create the nodes table and populate it from the OSM foreign table:

CREATE TABLE macau_network_nodes (
    id    BIGSERIAL,
    fid   BIGINT,
    eout  INTEGER,
    lon   DOUBLE PRECISION,
    lat   DOUBLE PRECISION,
    cnt   INTEGER,
    chk   INTEGER,
    ein   INTEGER,
    name  TEXT,
    tags  TEXT,
    point GEOMETRY(point, 4326)
);

INSERT INTO macau_network_nodes(fid, lon, lat, name, tags, point)
SELECT osm_id::bigint, ST_X(geom), ST_Y(geom), name, to_json(other_tags)::jsonb, geom
FROM points;

Step 3: Extract road network edge data and resolve node references

Create the edges table, populate it from the OSM foreign table, and then link each edge to the node IDs in macau_network_nodes:

CREATE TABLE macau_network_ways (
    gid              BIGSERIAL,
    fid              BIGINT,
    length           DOUBLE PRECISION,
    length_m         DOUBLE PRECISION,
    p                TEXT,
    source           BIGINT,
    target           BIGINT,
    source_osm       BIGINT,
    target_osm       BIGINT,
    cost             DOUBLE PRECISION,
    reverse_cost     DOUBLE PRECISION DEFAULT -1,
    cost_s           DOUBLE PRECISION,
    reverse_cost_s   DOUBLE PRECISION DEFAULT -1,
    rule             TEXT,
    one_way          INT,
    oneway           TEXT,
    x1               DOUBLE PRECISION,
    y1               DOUBLE PRECISION,
    x2               DOUBLE PRECISION,
    y2               DOUBLE PRECISION,
    maxspeed_forward  DOUBLE PRECISION,
    maxspeed_backward DOUBLE PRECISION DEFAULT -1,
    priority         DOUBLE PRECISION DEFAULT 1,
    name             TEXT,
    tags             JSONB,
    linestring       GEOMETRY(LINESTRING, 4326)
);

INSERT INTO macau_network_ways(fid, source_osm, target_osm, name, tags, linestring)
SELECT osm_id::bigint, source_osm, target_osm, name, to_json(other_tags)::jsonb, geom
FROM lines;

-- Resolve OSM node references to internal node IDs.
UPDATE macau_network_ways
SET source = macau_network_nodes.id
FROM macau_network_nodes
WHERE macau_network_ways.source_osm = macau_network_nodes.fid;

UPDATE macau_network_ways
SET target = macau_network_nodes.id
FROM macau_network_nodes
WHERE macau_network_ways.target_osm = macau_network_nodes.fid;

The resolved road network:

image

Step 4: Build a map matching workspace

SELECT st_createworkspace(
    'mm_ws_test2',
    'select fid, source, target, linestring from macau_network_ways'
);

Step 5: Run map matching

Pass the trajectory as an inline WKT string instead of a table reference:

SELECT st_astext(
    st_mapmatching(
        'mm_ws_test2',
        'LINESTRING(113.5492 22.1881,113.5493 22.1881,113.5504 22.1885,113.5505 22.1886,113.5502 22.189,113.5501 22.1891,113.5491 22.1887,113.5489 22.1887)',
        '{"algorithm":"stmatch","k":4,"r":0.25,"e":0.5,"tolerance":0.5,"vmax":1.0,"factor":0.5}'::text
    )
);

Expected output:

LINESTRING(113.5492633 22.1880942,113.5493491 22.1881266,113.5504129 22.1885289,113.5505378 22.1885762,113.5502504 22.1892274,113.5505378 22.1885762,113.5502504 22.1892274,113.5501282 22.1891812,113.5490843 22.1887864,113.5489761 22.1887455)

The matched trajectory on the Macao (China) road network:

image

What's next

  • Review the full st_mapmatching parameter reference to tune matching accuracy for your GPS data quality.

  • Explore the GanosBase trajectory functions (ganos_trajectory) for additional spatial analysis on your matched routes.