[GIS] Forming queries with node.js, PostGIS and GeoServer

geoservernode-jsopenlayerspostgisweb-mapping

I am not sure If I got the overall web-mapping architecture right. I can easily find tutorials about each part, but nothing on how to connect them.

I have openlayers in the front-end, node.js as a server and postGIS as a DB.

An action is done in the openlayers part, the data are sent to node, they define a query. Node sends this query to postGIS, postGIS answers it. The new data now travels back to the front-end via node to be rendered on openlayers. Openlayers fetch OSM as a base layer and I only save points/lines/polygons in postGIS.

(User clicked on the map, get lon/lat, use them as query parameters, find 4 nearest neighbors, send neighbors back, render them on the map).

Geoserver confuses me. I cannot understand its role in this mix. I cannot see how queries are related to it. I see SQL in all the postGIS tutorials and in OpenLayers and Geoserver I see CQL, that is not on the same level.

Do queries get formed in node but go to postGIS via Geoserver or do they get formed in node, they go to postGIS via node and then postGIS sends the answer in Geoserver to alter the map?

Best Answer

It is not easy to get the open source based overall web-mapping architecture. When we use open source, we can combine many tools. Sometimes, we just get one or two features of a tool. We have many, many ways to combine the amazing available tools.

Even for the same use case, you can get different software stacks, equally good.

In your use case, you might not need Geoserver at all.

Minimal scenario

In the minimal scenario, you can use OpenLayers on the client side with an OpenStreetMap layer, and node.js on the server side. If you want to display some other features on the map, you can style the features using OpenOpenlayers and get the data as GeoJSON from node.js. Node.js will receive requests, create a proper query and get the data from Postgresql. If you need to create or edit features on the client side, you send those changes to node.js as GeoJSON. Then use node.js to send it back to Postgresql to create or edit records on the database. Using just OpenLayers and node.js, you have a very efficient solution.

Let me show some OpenLayers code. To display additional features:

Server.myFeaturesAsGeoJson({
    foo: form.get('foo'),
    bar: form.get('bar')
}, function (result, event) {
    if (result.success) {
        if (result.data.features) {
            var features = (new ol.format.GeoJSON()).readFeatures(result.data);
            myFeaturesLayer.getSource().addFeatures(features);

If you change the layer on the client side using OpenLayers ol.interaction.Draw or ol.interaction.Modify, you need to pass those changes to node.js with:

var features = myFeaturesLayer.getSource().getFeatures();
if (features.length) {
    var details = (new ol.format.GeoJSON()).writeFeatures(features);
    Server.saveGeoJsonDetail({
        features: details
    }, function (result, event) {
        if (result.success) {
            console.log('Saved!');

Some notes on node.js code:

To write the geographic features on the database, you need to prepare the query in node.js, but Postgresql will handle the GeoJSON format. So for writing a feature is simple as:

"ST_GeomFromGeoJSON(" + JSON.stringify(element["geometry"]) + ")"

In node.js, to send features as GeoJSON to OpenLayers, you can create the proper FeatureCollection structure in you code, but I prefer to write a more complicated query and get the GeoJSON out of the database with something like:

var sql = '';
sql += 'SELECT row_to_json(fc) as geojson ';
sql += "FROM ( SELECT 'FeatureCollection' As type, array_to_json(array_agg(f)) As features ";
sql += 'FROM ( ';
sql += "SELECT 'Feature' As type, ST_AsGeoJSON(lgeom.the_geom)::json As geometry, row_to_json(lprop) As properties ";
sql += 'FROM plantas.pedidodetail As lgeom ';
sql += 'INNER JOIN ( ';
sql += 'SELECT id, gid FROM plantas.pedidodetail ';
sql += 'where gid = ' + gid;
sql += ' ) As lprop ';
sql += 'ON lgeom.gid = lprop.gid ';
sql += ') As f )  As fc';

You can also use ogr2ogr node module to return data as GeoJSON from Postgresql to OpenLayers.

Why Geoserver?

Get your own raster or vector data as WMS

You need Geoserver or another map server if you need other layers from your own data. You create the layer on Geoserver and add it to the map using OpenLayers.

Performance

You need a map server if you have many features. Reading and writing features in node.js, as I showed, is feasible for a few number of features. If you have many features to show, you need to have a WMS layer to show them at large scales. When the user zooms in, and the number of features in the view is small, you can disable the WMS layer and switch to the GeoJSON based vector layer. When the user zooms out, you disable the vector layer and show the WMS layer again.

If you have a large number of features, the features (or the style) are too complex to style, or if you have a group of layers, you might consider to use GeoWebCache well integrated in Geoserver. For performance issues, you need a tile cache.

WFS support

If you don't want to write your own code to manage the GeoJSON staff, you can use the Geoserver's WFS support. Using WFS, you don't need to do the queries in node.js to read or write the features. WFS will handle that. You can create a WFS layer with:

var vectorSource = new ol.source.Vector({
    format: new ol.format.GeoJSON({
        defaultDataProjection: 'EPSG:3763'
    }),
    url: function (extent) {
        return 'http://geoserver.domain.pt:8080/geoserver/wfs?service=WFS&' +
            'version=1.1.0&request=GetFeature&typename=marte:edificado&' +
            'outputFormat=application/json&srsname=EPSG:3763&' +
            'EXCEPTIONS=application/json&' +
            'bbox=' + extent.join(',') + ',EPSG:3763';
    },
    strategy: ol.loadingstrategy.tile(ol.tilegrid.createXYZ())
});
var vectorWFS = new ol.layer.Vector({
    source: vectorSource,
    style: simpleStyleFunction,
    maxResolution: 1.09956468849 // The maximum resolution (exclusive) below which this layer will be visible.
});
olMap.addLayer(vectorWFS);

WFS limitations

If you have some kind of relations between feature classes, WFS might not be able to handle that. WFS works with simple features. Support for complex features is under development by the Geoserver team, but many client do not support these complex features yet.

WFS primary key limitation

WFS requests must return features with unique ids. If you don't have unique ids, features will be repeated in OpenLayers and you will have problems updating features. For those cases, check how to add unique ids to your WFS layers on the Geoserver documentation.

Geoserver memory (and CPU) requirements

Geoserver is written in JAVA. Geoserver's memory footprint is (very) high. When you buy a dedicated server, the price will change if you use Geoserver or not.