1

I am currently working on developing a Node.js API with Express to retrieve vector tiles from a PostGIS database.

I am using the ST_AsMVTGeom function to transform geometries into vector tiles, but I am encountering an issue with coordinates that seem to be incorrect

Here is my query:

SELECT id,
     ST_AsMVTGeom (
             way,
             ST_MakeEnvelope(10.37109375, 36.87962060502676, 10.546875, 37.020098201368114, 4326),
             4096000,
             256,
             false
     ) geom
 FROM planet_osm_line osline WHERE osline.highway IS NOT null order by osline.id

Unfortunately, the generated coordinates seem incorrect, with negative values. I have tried different approaches, including ST_Transform(ST_SetSRID(way, 3857), 4326), but the problem persists.

enter image description here

i have also tried

SELECT ST_AsMVTGeom(way, ST_TileEnvelope(0, 0,0), 409600,256,false) AS geom
               FROM planet_osm_line
               WHERE way && ST_TileEnvelope(0, 0,0) and highway IS NOT null order by id

but shapes are not in the right positions and when I use ST_Transform all geometries are null.

enter image description here

this is my node express code

const app = express();

app.get('/:z/:x/:y', async (req, res) => {
    try {
        const {z, x, y} = req.params;
        const mvt = await generateMVT(z, x, y);
        res.set('Content-Type', 'application/vnd.mapbox-vector-tile');
        res.send(mvt);
    } catch (err) {
        console.error(err);
        res.status(500).send('Internal Server Error');
    }
});


async function generateMVT(z, x, y) {
    try {
        const result = await pool.query(`
            SELECT ST_AsMVT(q, 'planet_osm_line', 4096, 'geom') AS mvt
            FROM (SELECT ST_AsMVTGeom(
                                 ST_Transform(way, 3857),
                                 ST_TileEnvelope(${z}, ${x}, ${y}),
                                 4096,
                                 0,
                                 true
                         ) AS geom
                  FROM planet_osm_line osline
                  WHERE osline.highway IS NOT null AND ST_Intersects(way, ST_Transform(ST_TileEnvelope(${z}
                      , ${x}
                      , ${y})
                      , 3857))) AS q;
        `);
        console.log( result.rows[0])
        return result.rows[0].mvt;
    } catch (err) {
        console.error(err);
        throw new Error('Failed to generate MVT tile');
    }
}

And here is my html openlayers code

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>OpenLayers Vector Tiles</title>
    <!-- CSS/JS for OpenLayers map  -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ol.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/ol.css">
    <style>
        body {
            padding: 0;
            margin: 0;
        }
        html, body, #map {
            height: 100%;
            width: 100%;
            font-family: sans-serif;
        }
    </style>
</head>

<body>
<div id="map"></div>
<script>

    const vectorLayer = new ol.layer.VectorTile({
        source: new ol.source.VectorTile({
            format: new ol.format.MVT(),
            projection: 'EPSG:4326',
            url: 'http://localhost:3000/{z}/{y}/{x}',
        })
    });

    const baseLayer = new ol.layer.Tile({source: new ol.source.XYZ({url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png"})});

    const map = new ol.Map({
        target: 'map',
        view: new ol.View({projection: 'EPSG:4326',center: [10.2732131,36.8615463], zoom: 17,minZoom:0,maxZoom:20}),
        layers: [baseLayer,vectorLayer]
    });
</script>
</body>
</html>


Tiles are loaded but unfortunately I do not see any vector data on the map. enter image description here

enter image description here

Does anyone have suggestions on how to resolve this issue? Should I adjust my SQL query in a specific way, or is there something I am missing in my configuration?

1
  • 1
    Those coordinates are absolutely correct - given the (unsuitable) context of your function calls. The Vector Tile encoding does not follow geographic coordinate semantics, but translates them into a tile inherent coordinate space. I strongly suggest to thoroughly digest the Mapbox Vector Tile specifications as well as the respective PostGIS docs for the set of functions to generate them - specifically regarding tile space, tile dimension and source/target projection required for the default case of EPSG:3857 derived tile schemes.
    – geozelot
    Commented Feb 28 at 8:42

1 Answer 1

0

you need to dynamically pass the Z(zoom level), X(tile column),Y(tile row) values from the frontend map to the node server to create a tile envelope that will be used to intersect the postgres table and create vector tiles of the intersected portion of the table

try something like this - you can combine these two together as well

async function generateMVT(z, x, y) {
  try {
    const result = await query(`
    SELECT
    ST_AsMVT(q, 'planet_osm_line', 4096, 'geom') AS mvt
FROM (
    SELECT
        ST_AsMVTGeom(
            ST_Transform(p.geom, 3857),
            ST_TileEnvelope(${z}, ${x}, ${y}),
            4096,
            0,
            true
        ) AS geom
    FROM
        planet_osm_line osline 
    WHERE osline.highway IS NOT null and ST_Intersects(geom, ST_Transform(ST_TileEnvelope(${z}, ${x}, ${y}), 3857))
) AS q;
`);
    return result.rows[0].mvt;
  } catch (err) {
    console.error(err);
    throw new Error('Failed to generate MVT tile');
  }
}

router.get('/mvt/:z/:x/:y', async (req, res) => {
  try {
    const { z, x, y } = req.params;
    const mvt = await generateMVT(z, x, y);
    res.set('Content-Type', 'application/vnd.mapbox-vector-tile');
    res.send(mvt);
  } catch (err) {
    console.error(err);
    res.status(500).send('Internal Server Error');
  }
});
1
  • I updated the question with all my codes to be more clear
    – AhmeX2
    Commented Mar 1 at 16:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.