Simple MapTileServer using NodeJs & PostGIS — Part 2: Drawing Vector data on canvas
By: Manivannan K
Tags: Tileserver, GIS

Simple MapTileServer using NodeJs & PostGIS — Part 2: Drawing Vector data on canvas Manivannan K Mar 22·3 min read

In the Part 1, we have seen the fundamental calculations involved in converting geographic coordinates to pixel coordinates. Now, let’s get started with our server code.

Express handler for tile image request:

Our request handler responds to each tile image request(with params: x, y, z, layerId) from the client side. To get features(in the layer-layerId) that should be rendered in the current tile(x, y, z), we will have to convert the tile coordinates(x, y, z from the request) to LatLng coordinates and get the features within the geometric extent of the tile. The below Js function converts the tile coordinates to LatLng coordinates:

Now, moving to our request handler, we will get layerId, x, y and z from the tile image request. Like mentioned above, these params will be used to filter the GIS features in Postgres database. We are using the above mentioned tileToLatLng() to find the extent of the tile. This extent will then be used to filter the GIS features using the ST_Contains and ST_Overlaps PostGIS functions.

The below code snippet shows the express request handler and PostGIS query for filtering the features in the tile. Note: We are yet to implement the canvas.draw() function.

Before going to the canvas rendering/drawing part, check the sample renderer object stored in our firestore db. The symbol object will be used by the canvas.draw() to get the styling information like color, strokeWidth, etc.,. Please note, different attributes are used by different geometry types.

Point and MultiPoint — radius, fill, stroke LineString & MultiLineString — stroke Polygon & MultiPolygon — fill, stroke

Drawing GIS data on canvas:

Now that, we have covered some basic structure of our database models, let’s go the image rendering part.

Note that, features with different geometry types have different geometry structure:

Point — Simplest: a point(lng, lat) MultiPoint — Array of points LineString — Array of points(path) MultiLineString — 2D array of points(array of paths) Polygon — array of rings(rings — array of closed path) — 2D array of points MultiPolygon — array of polygons(3D array)

We have to handle all these cases. In the canvas.draw() code snippet(below), we have defined separate functions to handle each case:

drawPolygon() — Handles MultiPolygon and Polygon type; loops through rings in a Polygon/MultiPolygon and calls drawPath()

in a Polygon/MultiPolygon and calls drawPath() — Handles Lines in a MultiLineString or Rings in a Polygon or MultiPolygon; Loops through each point in the ring/path and calls drawPointInPath()

and calls drawPointInPath() — To handle any point in a path (LineString, MultiLineString, Polygon & MultiPolygon); moves to the calculated pixel coordinates and adds a line from previous position to new in the canvas.

(LineString, MultiLineString, Polygon & MultiPolygon); moves to the calculated and adds a line from previous position to new in the canvas. drawPoint() — To draw Point or MultiPoint features

We have explained latLngToImagePixel() and latLngToTile() in detail in the Part 1. If you haven’t checked, please do :)

You can find a new function canvasCoordinates() in the above snipper. We did find the pixel coordinates with the latLngToImagePixel, What is this function doing? Well, our calculated pixel coordinates(not always correct) doesn’t know whether the location occurs inside of the current tile or not. The above function will use the tile coordinates(x, y) from the request to find that. Hope, it’s clear :)

That’s it!

What’s next:

What we did, is just a simple version of the tile server using NodeJs. The following can be added to this,

Caching images for faster tiles

Custom icon image rendering

Raster data rendering

New renderer types: Categories, Classification,..

If you find any gaps or problems, please do ping me or leave a comment.