Draw GeoJSON points as a layer with MapLibre GL (Mapbox GL)
Geoapify returns results in GeoJSON format which is natively supported by most of the client-side map libraries. Our Geocoding API and Places API return a GeoJSON.FeatureCollection with Features with geometry type Point.
In this code sample, we would like to show how GeoJSON points can be added to a map and handled as a layer. The full version of the code sample you can find on JSFiddle.
1. Create a map
You require a Geoapify API key to make API calls and display a map. Register and get an API key on MyProjects Geoapify.com.
We use MapLibre GL as a client-side library which is an open-source fork of Mapbox GL library before their switch to a non-OSS license.
You can include the library directly to your HTML-page or install it with npm:
<link rel="stylesheet" type="text/css" href="https://cdn.skypack.dev/maplibre-gl/dist/maplibre-gl.css">
Add a map container to your HTML-template and initilize the map:
<div id="my-map"></div>
body {
margin: 0;
padding: 0;
}
#my-map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
import { Map, NavigationControl, Popup } from 'https://cdn.skypack.dev/maplibre-gl';
var bounds = {
// Paris
lat1: 48.88002146841028,
lon1: 2.3410839716279455,
lat2: 48.86395628860821,
lon2: 2.368348737185606
}
var map = new Map({
center: [(bounds.lon1 + bounds.lon2) / 2, (bounds.lat1 + bounds.lat2) / 2],
zoom: 15,
container: 'my-map',
style: `https://maps.geoapify.com/v1/styles/klokantech-basic/style.json?apiKey=${YOUR_API_KEY}`,
});
map.addControl(new NavigationControl());
2. Query places for the given area
We use Geoapify Places API to get places of type 'cafe'. You can query the data with the following code:
var type = "cafe"
// getting cafes for the given boundary (number of results limited by 100)
var placesUrl = `https://api.geoapify.com/v1/places?lat1=${bounds.lat1}&lon1=${bounds.lon1}&lat2=${bounds.lat2}&lon2=${bounds.lon2}&type=${type}&limit=100&apiKey=${YOUR_API_KEY}`;
fetch(placesUrl).then(response => response.json()).then(places => {
console.log(places);
});
3. Add results as a layer to the map
MapLibre GL lets to load an image and set it as an icon for the layer. We use a Geoapify Marker Icon API to generate an icon:
// getting an icon from Geoapify Icons API
map.loadImage(`https://api.geoapify.com/v1/icon/?icon=coffee&color=%23ff9999&size=large&type=awesome&apiKey=${YOUR_API_KEY}`, function(error, image) {
if (error) throw error;
map.addImage('rosa-pin', image); //38x55px, shadow adds 5px
});
Add the places to a map as a layer of type 'symbol':
fetch(placesUrl).then(response => response.json()).then(places => {
showGeoJSONPoints(places, type);
});
function showGeoJSONPoints(geojson, id) {
var layerId = `${id}-layer`;
if (map.getSource(id)) {
// romove first the old one
map.removeLayer(layerId);
map.removeSource(id);
}
map.addSource(id, {
'type': 'geojson',
'data': geojson
});
map.addLayer({
'id': layerId,
'type': 'symbol',
'source': id,
'layout': {
'icon-image': 'rosa-pin',
'icon-anchor': 'bottom',
'icon-offset': [0, 5],
'icon-allow-overlap': true
}
});
}
Note, the icons contain shadow, so we need to specify an icon offset to correct the icon position. Use Marker Icon API Playground to get the correct bottom offset.
Get a notification and show a popup when a user clicks on a place
The big advantage of the layer is that you add only 1 click event handler for the whole layer. However, you need to take care of some details:
- Normalize coordinates;
- Setup cursor value:
function showGeoJSONPoints(geojson, id) {
...
map.on('click', layerId, function(e) {
var coordinates = e.features[0].geometry.coordinates.slice();
var name = e.features[0].properties.name;
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
new Popup({
anchor: 'bottom',
offset: [0, -50]
})
.setLngLat(coordinates)
.setText(name)
.addTo(map);
});
// Change the cursor to a pointer when the mouse is over the places layer.
map.on('mouseenter', layerId, function() {
map.getCanvas().style.cursor = 'pointer';
});
// Change it back to a pointer when it leaves.
map.on('mouseleave', layerId, function() {
map.getCanvas().style.cursor = '';
});
}
Note, the position of the popup also needs to be adjusted according to icon size.