Migration
WMS → BeNomad Tiles#
Moving a raster WMS map onto BeNomad vector tiles: what you gain, what you have to change, and the honest list of what you give up.
At a glance#
- WMS sends images rendered by the server. Vector tiles send data rendered by the browser.
- Vector tiles require
bemap.MapLibreMap: there is no Leaflet or OpenLayers path. - Authentication changes: WMS rides your BeMap credentials; tiles use a separate token from the tiles worker.
- Your service calls (routing, geocoding, EV) are untouched. Only the basemap changes.
- You gain 3D, globe, rotation, client-side restyling, sharper text, and a working browser cache.
- You give up server-side
GetFeatureInfoand any WMS style your organisation maintains centrally.
Usage#
The two setups side by side#
Before: WMS on Leaflet
<script src="/bemap/leaflet.js"></script>
<script src="/bemap/bemap-js-api.js"></script>var ctx = new bemap.Context({
host: 'bemap.benomad.com', secure: true,
login: 'your-login', password: 'your-password',
geoserver: 'here'
});
var map = new bemap.LeafletMap(ctx, 'map');
map.defaultLayers();
map.move(2.35, 48.85, 12);After: vector tiles on MapLibre
<link rel="stylesheet" href="/bemap/maplibre-gl.css">
<script src="/bemap/maplibre-gl.js"></script>
<script src="/bemap/pmtiles.js"></script>
<script src="/bemap/bemap-js-api.js"></script>var ctx = new bemap.Context({
host: 'bemap.benomad.com', secure: true,
login: 'your-login', password: 'your-password',
tilesHost: 'mptiles-api.benomad.net' // the one new field
});
var map = new bemap.MapLibreMap(ctx, 'map');
map.move(2.35, 48.85, 12);Three changes: different engine scripts, pmtiles.js added, tilesHost set on the
Context. Everything below that line (markers, polylines, popups, events, routing) is
identical.
defaultLayers() and the background#
On WMS, defaultLayers() creates the background raster layer. On MapLibre the basemap
is the vector style, so there is no background layer to create: defaultLayers()
still makes the overlay layers, but the background half has nothing to do.
backgroundLayers() and switchBackgroundLayer() likewise have no WMS to switch. The
equivalent is swapping the style:
map.setStyle(styleUrlOrObject);
map.fetchAvailableStyles().then(function(styles) { /* … */ });Authentication#
This is the part that catches people out. The BeMap services and the tiles worker are different origins with different authentication:
| WMS | BeNomad Tiles | |
|---|---|---|
| Host | your BeMap host | mptiles-api*.benomad.net |
| Auth | BeMap credentials on every tile URL | one login, then a ~1 h JWT |
| Handled by | the Context | bemap.TilesAuth, automatically |
You do not usually write any of this: setting tilesHost is enough. But it means an
account can be entitled for WMS and not for tiles, and the failure shows up as a 403
on the tiles login while every service call keeps working. See
Tiles authentication.
Pair the hosts. A BeMap host on beta with a tiles host on production yields a 403
that looks like bad credentials and is not.
Styling#
WMS styling is server-side: you request styles: 'traffic' and the server renders it.
Vector-tile styling is client-side and live:
map.setPaintProperty('road-primary', 'line-color', '#ff0000');
map.setLayoutProperty('poi-labels', 'visibility', 'none');
map.setFilter('buildings', ['>', 'height', 20]);For organisation-wide changes, edit the style centrally in BeMaputnik instead of in every application, see Styles & BeMaputnik.
Replacing GetFeatureInfo#
map.onGetFeatureInfo(layer, options) asks the WMS server what is at a point. There is
no server to ask with vector tiles, but the data is already in the browser:
// Before
map.onGetFeatureInfo(wmsLayer, {
beforeCallback: function(data) { showPopup(data); }
});
// After
map.on(bemap.Map.EventType.CLICK, function(evt) {
var hits = map.queryRenderedFeatures({ x: evt.x, y: evt.y });
if (hits.length) showPopup(hits[0].properties, evt.getCoordinate());
});Faster (no round trip) but it only sees what the tiles carry and what the style
renders. If your WMS GetFeatureInfo returned attributes that are not in the vector
tiles, that data is genuinely not available client-side.
Caching#
Vector tiles cache properly in the browser. At the 2.0.2 default
(tilesSliceMode: '200') tiles are ordinary cacheable HTTP 200 responses with a long
max-age, so repeat visits are close to free and no Service Worker is needed.
map.enableBrowserCache();
map.getBrowserCacheStats();See Cache, slices & resilience.
A staged migration#
You do not have to move everything at once.
- Stand up one screen on
MapLibreMapwithtilesHostset, leaving the rest on WMS. - Confirm the tiles login works for your account on your environment.
- Port
onGetFeatureInfousage toqueryRenderedFeatures. - Move any server-side style customisation into the vector style.
- Migrate remaining screens.
- Drop the Leaflet/OpenLayers scripts once nothing constructs those engines.
Both engines can coexist in one application throughout: they are separate classes over the same Context.
Reference#
What changes#
| Concern | WMS | BeNomad Tiles |
|---|---|---|
| Engine | Leaflet, OpenLayers | MapLibre only |
| Extra scripts | - | maplibre-gl.js, pmtiles.js |
| Context field | geoserver | tilesHost (keep geoserver for services) |
| Basemap | bemap.BemapLayer via defaultLayers() | the vector style |
| Restyling | server-side styles | setPaintProperty / setFilter / BeMaputnik |
| Feature info | onGetFeatureInfo | queryRenderedFeatures |
| Rotation / pitch / 3D | no | yes |
| Caching | HTTP cache of images | tile cache, no Service Worker at the default |
What does not change#
- Every service: routing, traceroute, geocoding, autocomplete, POI, EV, ACL.
- Markers, polylines, polygons, circles, popups.
- Events, drawing, layers, camera.
bemap.Context: you add a field, you do not replace it.
What you lose#
| Capability | Notes |
|---|---|
Server-side GetFeatureInfo | Replaced by client-side querying, limited to what the tiles carry. |
| Centrally-rendered WMS styles | Replaced by the vector style, but that is also centrally managed, in BeMaputnik. |
| Leaflet / OpenLayers plugin ecosystem | MapLibre has its own, and it is a different set. |
| Very old browser support | Vector tiles need WebGL. |
Notes#
WebGL is required. MapLibre renders on the GPU. Anything without WebGL (some locked-down corporate environments, some virtualised desktops) cannot show a vector map at all. If that is part of your estate, keep a WMS fallback path.
The library still supports WMS. This is not a deprecation. WMS on Leaflet and OpenLayers is fully supported in 2.0.2 and is the right choice for plenty of applications. Migrate because you want what vector tiles offer, not because you have to.
Gotchas#
- Forgetting
pmtiles.js. WithtilesHostset and PMTiles missing, the library prints a loud red console error naming the tag to add. - Loading
pmtiles.jsafterbemap-js-api.js. Order matters; it must come first. - Mismatched environments.
hoston beta,tilesHoston production →403, which reads like a credentials failure. - Expecting
switchBackgroundLayerto work. There is no WMS background on MapLibre. UsesetStyle. - Expecting
GetFeatureInfoattributes to survive. Only what the tiles contain is queryable. - Keeping both engine bundles forever. Loading Leaflet, OpenLayers and MapLibre triples the payload. Drop what you no longer construct.
- Assuming tiles entitlement follows WMS entitlement. They are separate grants. Verify before migrating a screen.
See also#
- BeNomad Tiles: overview
- Web: PMTiles: the browser path in detail
- Tiles authentication
- Styles & BeMaputnik
- Backgrounds & basemaps: the WMS side
- Choosing an engine
- Tiles troubleshooting