Node-red-contrib-web-worldmap - Local mapserver alternative

I've read several discussions in the forum around creating a local WMS map server to support offline use of the node-red-contrib-web-worldmap node. The author recommended the use of mapserver in his node documentation.

I have tried this approach, but given my "limited" skill set it became a major learning effort setting up tiles, .map files etc. I failed to get it to work in the end.

My second approach that looked promising was to use MapProxy to cache the tiles locally. Again I ran into issues and had to abandon the approach.

The next alternative, and one that looks more promising is covered at:
Leaflet Tile Layer Cache.
The approach is to cache the leaflet tiles in a local PouchDB and modify the Leaflet L.TileLayer class to add a "useCache: true" option.

The challenge for me is not knowing how to merge this with the node-red-contrib-web-worldmap node.

So bottom line I'm asking for help from anybody out there that might be able to validate this approach and possibly take on this challenge or at least give me some fairly detailed steps on how to accomplish this.

At a high level I think what is needed is to (I'm sure I'm over simplifying this):

  1. modify worldmaphead.html to link to additional files: </script
  2. modify worldmap.js to add the "useCache" option.
  3. "repackage" and publish the node.

There is a a caveat mentioned about cross-origin that may be an issue but I don't fully understand this:


Due to the tile images being parsed and stored by the browser (technically, extracting data from a canvas in which a external image has been loaded into), the tiles must come from a tile server which allows CORS (Cross-Origin Resource Sharing) on the tiles. So tiles must have a CORS header allowing them to be loaded in the document where you're using this caching layer.
In other words: if chrome shows a grey map, and displays CORS-related messages in the console, make sure that your tileserver adds this header to all tiles:

See also:

Any help would be most appreciated.

You'll need to speak nicely to @dceejay who is the author of the worldmap node. As I've cruelly tagged him, you never know, he might just magically appear :grin:

1 Like

Thanks very much for that. All help is welcome.

That seems to just be adding a cache so you would still need to populate it somehow. Did you see the other options in the docs ? eg running a map server under docker - there are several versions available. What source of maps do you have available ?

Thanks for the quick response.

Again with my limited understanding I thought that it would take the tiles that are downloaded with Leaflet by your node e.g.
request URL:
and selectively cache them in the local database if you add the useCache option to your code e.g.

if (maplist.indexOf("OSMC")!==-1) {
basemaps[layerlookup["OSMC"]] = new L.TileLayer(osmUrl, {
subdomains: ['a','b','c']
In that way I could avoid having to source maps myself which was a large part of my challenge.

There are several options available one of which is to seed the cache:
Options available are as follows:

  • useCache: set to true in order to enable the cache. This option must be set at initialization time.
  • saveToCache: Whether to save new tiles to the cache or not. Defaults to true.
  • useOnlyCache: Whether to fetch tiles from the network or not. Defaults to false.
  • cacheMaxAge: Time, in milliseconds, for any given tile to be considered 'fresh'. Tiles older than this value will be re-requested from the network. Defaults to 24 hours.
  • cacheURLMask: A regular expresstion to mask the URL so that access tokens and the sort won't cause cache misses. (e.g. /access_token=[^&]*/)

New functions available are as follows:

  • seed: Starts seeding the cache for a given bounding box (a L.LatLngBounds), and between the two given zoom levels.

I did not try other options for running a map server although I did read about that.

BTW my application will run in a location with limited or no internet access and the mapping area would be quite small as would the number of tiles I need to cache. Also would only need to do for one layer.

If you don’t mind 2017 data then you can get decent MBTiles sets from Ontario OpenStreetMap Tiles, GeoData and OpenData Maps | MapTiler Data And use the docker container in my readme to serve them.

I had previously followed the steps in your document, as best I could, of installing MapServer and creating a mapserv executable in my /home/pi/.node-red/node_modules/node-red-contrib-web-worldmap directory . I had also downloaded the same tiles you referenced above and used something called mbutil to create/extract the tiles. Unfortunately I got stuck there as mbutil produces something called .pbf files not .png files. I was mostly lost and way out of my comfort zone when it came to configuring the .map and .shp files to properly reference tiles etc. Even with the benefit of your example .map file, which I found in another forum discussion on this topic, I had to capitulate.

I'm not really sure that using the docker approach is going to get me any further at this point as I think I'd face the same challenges in the end.

It seems to me that my proposal here may be much more doable even though I would have to figure out how to modify your code and create a new "modified" node. At least I would avoid having to expend a huge effort to learn the intricacies of mapping - not something I intended - I really just want to make use of your very handy node in my app.

So wondering if you think my suggestion would work and if you have any pointers on how to proceed?

Well I'm sure your suggestion would work - but would be harder than downloading that mbtiles file into a directory and then running docker run --name maptiler -d -v $(pwd):/data -p 1884:8080 maptiler/tileserver-gl -p 8080 to have it serve maps of port 1884... eg

Well I gave it a shot but nothing is ever easy :slight_smile:
Foiled again I'm afraid.
Docker image maptiler/tileserver-gl failed with:
"no matching manifest for linux/arm/v7 in the manifest list entries"

I neglected to tell you I was installing on RPi4 running Buster 32-bit.
Per GitHub issue: I would need to upgrade to 64-bit Bullseye to make it work. Don't believe that is an exercise I can afford to undertake at the moment. I'm afraid I'd break something in my current setup.

I suppose I could backup and try on a copy and see if the upgraded image is running my apps properly....

As a temporary solution I modified your Worldmap index.html and worldmap.js to support local caching in the browser using Indexed.db. I added a button in bottom right corner which will seed the local cache down to zoom leveI 16 for the current viewport and basemap. I found that going down to zoom 18 requires significantly more seed time and storage. Not ideal but it will allow me to download my region of interest before relocating device to a non-wifi location. My device does have 4G cellular access but data costs are significant.

I just tried it on a pi3 with piOS64bit version... all good here...

to install docker I did

   sudo apt-get update
   sudo apt install -y python3-pip libffi-dev
   sudo usermod -aG docker $USER

Then I logged out and back in..
then copied over the mbtiles file
then ran

docker run --name maptiler -d -v $(pwd):/data -p 1884:8080 maptiler/tileserver-gl -p 8080

in the directory where the mbtiles were...

Thanks - at least now I know it can be done successfully. Just need the time to upgrade to 64bit OS.
I really appreciate all your time and help as I know you've had to deal with this particular subject on more than one occasion.

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.