Create a Webmap using Koordinates tiles and Mapbox GL
This guide introduces the combination of WMTS tile layers from a Koordinates site into a map using the Mapbox GL web mapping framework.
The code we're using in this guide has been used to make a webmap, published on Codepen here. Our aim with this webmap is to allow users to click on the map to show information about the closest earthquake.
Prerequisites:
- An API key from your Koordinates account. It should have the "Query layer data" and "Access cartographic tile images (WMTS)" scopes.
- An access token from your Mapbox account.
- A layer published to Koordinates with a custom cartography style.
HTML/CSS
The HTML in the example contains:
- imports of the Mapbox GL javascript and stylesheet
- the Mapbox GL map container
- a template for our example popup content
This is similar to the introductory Mapbox GL example. You can also load the Mapbox GL library via your module bundler (npm/webpack).
The CSS in the example contains the sizing for the map container, and some simple styling for the popup.
Javascript
The first step is to create the default map, and specify the Mapbox style that we want to use as a base map, and the initial centre ([Longitude, Latitude]) and zoom level.
var map = new mapboxgl.Map({ container: "map", // container id style: "mapbox://styles/mapbox/outdoors-v11", center: [175, -41], zoom: 6 });
In this example, we want to insert our data between the landuse/landcover layers and the symbol layers (roads, POIs, labels) in the map. We do this by finding the first symbol layer of the basemap style:
// Find the ID of the first symbol layer in the map style // We use this to insert our layer beneath roads/pois/labels/etc var firstSymbolId = map.getStyle().layers.find(l => (l.type === "symbol")).id;
The next step is to define the Koordinates layer as a raster layer and add it to the map. The tiles entry comes from the Koordinates layer's 'Services' tab as the zoom / x / y template.
Remember to insert your own API key, Layer ID, and Style ID in the URL
eg. https://koordinates-tiles-a.global.ssl.fastly.net/services;key={YOURAPIKEY}/tiles/v4/layer={layerID}/EPSG:3857/{z}/{x}/{y}.png for the layer's default style,
or https://koordinates-tiles-a.global.ssl.fastly.net/services;key={YOURAPIKEY}/tiles/v4/layer={layerID},style={styleID}/EPSG:3857/{z}/{x}/{y}.png for a custom style.
map.addLayer({ id: "koordinates-earthquakes", type: "raster", source: { type: "raster", tiles: [ // This is the XYZ template URL from the Koordinates layer // services page: https://labs.koordinates.com/layer/7328-new-zealand-earthquakes/webservices/ "https://koordinates-tiles-a.global.ssl.fastly.net/services;key=50f747d8d7b04646b682cb5efb10f3f8/tiles/v4/layer=7328/EPSG:3857/{z}/{x}/{y}.png" ], tileSize: 256, maxzoom: 22, attribution: "<a href='https://labs.koordinates.com/layer/7328-new-zealand-earthquakes/'>New Zealand Earthquakes</a>" } }, firstSymbolId // Insert the layer beneath the first symbol layer. );
The attribution property contains a link to the data source and license information. This is important for third party layers which often require attribution as part of their license.
Now the Koordinates layer will appear on the map. Next up, query the Koordinates Vector Query API when the user clicks on the map and display a popup with the closest feature's information. We start with the click event from the Mapbox map:
var quakeMarker; map.on("click", function(e) { // construct the query url let queryUrl = new URL('https://labs.koordinates.com/services/query/v1/vector.json'); // documentation of parameters at https://help.koordinates.com/api/query-api/vector-query/ let queryParams = { key: KOORDINATES_API_KEY, // Koordinates API Key layer: 7328, // Koordinates Layer ID x: e.lngLat.lng, // map click location y: e.lngLat.lat, max_results: 1, // find the closest result radius: 1000, // max. search distance 1km geometry: true // Include geometry in the response }; queryUrl.search = new URLSearchParams(queryParams); // make the API request fetch(queryUrl) .then(function(response) { if (!response.ok) { throw new Error("HTTP error, status = " + response.status); } return response.json(); }) .then(function(response) { // extract the first feature from the response const feature = response.vectorQuery.layers['7328'].features[0]; console.log("Feature:", feature); if (feature === undefined) { // no features within 1km if (quakeMarker) { // remove marker from map quakeMarker.remove(); quakeMarker = null; } return; } // position map marker if (!quakeMarker) { // add marker quakeMarker = new mapboxgl.Marker() .setLngLat(feature.geometry.coordinates) .addTo(map); } else { // move existing marker quakeMarker.setLngLat(feature.geometry.coordinates); } // create popup content let popupTemplate = document.getElementById('popup-template'); let popupContent = document.importNode(popupTemplate.content, true); popupContent.querySelector('.origintime').textContent = feature.properties.origintime; popupContent.querySelector('.magnitude').textContent = feature.properties.magnitude; popupContent.querySelector('.depth').textContent = feature.properties.depth; // open map popup let popup = new mapboxgl.Popup({anchor: 'left', offset: 10}) .setDOMContent(popupContent) .setLngLat(feature.geometry.coordinates) .addTo(map); }); }); });
The response from the vector query API is a GeoJSON FeatureCollection for each queried layer, sorted in order of distance from the query location, within the specified radius, and limited to max_results features per layer.
First we extract the closes GeoJSON Feature from the response, then we create or update a Marker on the map in the feature's position. Then we create a Popup showing information formatted from the feature's properties.
And there we have it — our working webmap built from Koordinates tile services!