OpenLayers & OpenStreetMap Examples
HTML
Website
jQuery
OpenLayers
OpenStreetMap
Some examples of how to use OpenStreetMap and OpenLayers to show/add GPX functionalities to a map and other interactions.
See the OpenLayers Documentation for more info and examples.
Code Snippets
Add the following snippet code to the html of a page.
<script src="https://cdn.maptiler.com/ol/v6.12.0/ol.js"></script>
<script src="vdwwd-openlayers.js"></script>
<div id="map-container">
<div class="ol-map-container">
<div id="map" class="ol-map"></div>
<div id="map-popup" class="ol-popup"></div>
<div id="map-popup-click" class="ol-popup"></div>
</div>
</div>
And to initialize the map and add functions execute on Document Ready.
initMap(); addGpxFromFileUpload(); addGpxFromPoints(demotrack, 'Demo Track', true); addClickEvent();
The CSS
.ol-map-container {
width: 100%;
height: 600px
}
.ol-map {
width: 100%;
height: 100%
}
.ol-popup {
position: absolute;
background-color: #fff;
filter: drop-shadow(0 3px 3px rgba(0,0,0,.4));
padding: 10px;
border-radius: 10px;
border: 1px solid #282c41 !important;
bottom: 12px;
left: -50px;
min-width: 100px;
white-space: nowrap;
z-index: 9999;
color: #000;
font-size: .9rem
}
.ol-popup:after, .ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none
}
.ol-popup:after {
border-top-color: #fff;
border-width: 10px;
left: 48px;
margin-left: -10px
}
.ol-popup:before {
border-top-color: #ccc;
border-width: 11px;
left: 48px;
margin-left: -11px
}
The code inside vdwwd-openlayers.js
var map;
var overlay;
var interaction;
var featureArr;
var $popup;
var styles = {
'track-red': new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 3,
}),
}),
'track-black': new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#000000',
width: 3,
}),
}),
};
//initialize the map
function initMap() {
if ($('#map').length === 0)
return;
$popup = $('#map-popup');
//create the map object
map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([5.6461, 52.1008]),
zoom: 7,
maxZoom: 16,
constrainResolution: true
}),
controls: []
});
//add a custom layer to the map
//olms.apply(map, 'https://api.maptiler.com/maps/basic/style.json?key=xxsAuwkPOcpryqPQdtC8');
//create an overlay to anchor the hover popup to the map
overlay = new ol.Overlay({
element: document.getElementById('map-popup'),
autoPan: {
animation: {
duration: 250,
}
}
});
//add the overlay to the map
map.addOverlay(overlay);
//add a mousemove function to the map to detect a marker and if found show popup
map.on('pointermove', function (e) {
var feature = map.forEachFeatureAtPixel(e.pixel, function (feat, layer) {
return feat;
});
//get the current position of the mouse in pixels relative to the map div container
//console.log(e.pixel)
//show the marker info if there is a name property and it has contents
if (feature && typeof feature.get('name') != 'undefined' && feature.get('name') != null && feature.get('name') !== '') {
$popup.html(feature.get('name'));
overlay.setPosition(e.coordinate);
} else {
overlay.setPosition(null);
}
});
}
//add a marker to the map
function addMarker(id, name, color, lat, lng, draggable, custom_marker, custom_layer) {
//create a feature
var feature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([lng, lat])),
name: name,
id: id,
type: 'marker'
});
var markerimg = '/images/marker-' + color + '.png';
if (custom_marker != null && custom_marker !== '') {
markerimg = custom_marker;
}
//create an icon style with an image as marker
var style = new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 0.5],
src: markerimg
}))
});
//or create an icon style with a shape as marker
//var style = new ol.style.Style({
// image: new ol.style.Circle({
// radius: 10,
// stroke: new ol.style.Stroke({
// color: 'black',
// width: 3
// }),
// fill: new ol.style.Fill({
// color: 'white'
// }),
// })
//});
//add the style to the feature
feature.setStyle(style);
//create a layer and add the feature
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [feature]
}),
name: name,
type: 'marker',
zIndex: 100
});
//is there a custom layer
if (custom_layer != null) {
custom_layer.getSource().addFeature(feature);
layer = custom_layer;
} else {
//add the layer to the map
map.addLayer(layer);
}
//no draggable marker
if (!draggable)
return;
//add a modifier
var modify = new ol.interaction.Modify({
hitDetection: layer,
source: layer.getSource()
});
//needed variables
var container = document.getElementById('map-container');
var overlay = modify.getOverlay().getSource();
//modify handlers
modify.on(['modifystart', 'modifyend'], function (e) {
container.style.cursor = e.type === 'modifystart' ? 'grabbing' : 'pointer';
});
overlay.on(['addfeature', 'removefeature'], function (e) {
container.style.cursor = e.type === 'addfeature' ? 'pointer' : '';
});
//get the coordinates during drag
feature.on('change', function () {
//console.log(ol.proj.toLonLat(this.getGeometry().getCoordinates()));
}, feature);
//add the modifier to the map
map.addInteraction(modify);
}
//load a gpx file from an url
function addGpxFromUrl(url) {
//create a layer
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
url: url,
format: new ol.format.GPX()
}),
style: function () {
return styles['track-red'];
},
name: 'GpxFromUrl',
type: 'track',
zIndex: 100
});
//add the layer to the map
map.addLayer(layer);
//bind a listeret to the layer (needed becaue loading the source from an url is async)
layer.getSource().on('addfeature', function (e) {
//get the coordinates from the layer
var coords = e.feature.getGeometry().clone().transform('EPSG:3857', 'EPSG:4326');
//put the coordinates in a html element (to use elsewhere or post it to the server)
$('#map-gpx').val(JSON.stringify(coords.getCoordinates()[0]));
//add a marker to the first coordinate
addMarker(1, 'Starting Point', 'blue', coords.getFirstCoordinate()[1], coords.getFirstCoordinate()[0], false);
//zoom and fit bounds
var geom = ol.geom.Polygon.fromExtent(layer.getSource().getExtent())
geom.scale(1.1);
map.getView().fit(geom, { size: map.getSize() });
});
}
//load a gpx file from a string variable
function addGpxFromString(gpx, name, color) {
//remove the existing layer
removeLayer(name);
//create a layer
var layer = new ol.layer.Vector({
source: new ol.source.Vector({}),
style: function () {
return styles['track-' + color];
},
name: name,
type: 'track',
zIndex: 100
});
//read the features from the gpx
var features = new ol.format.GPX().readFeatures(gpx, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
//add the features to the layer
layer.getSource().addFeatures(features);
//add the layer to the map
map.addLayer(layer);
//get the coordinates from the layer
var coords = features[0].getGeometry().clone().transform('EPSG:3857', 'EPSG:4326');
//put the coordinates in a html element (to use elsewhere or post it to the server)
var $mapgpx = $('#map-gpx');
$mapgpx.val(JSON.stringify(coords.getCoordinates()));
$mapgpx.show();
//add a marker to the first coordinate
addMarker(1, 'Starting Point', color, coords.getFirstCoordinate()[1], coords.getFirstCoordinate()[0], false);
//zoom and fit bounds
var geom = ol.geom.Polygon.fromExtent(layer.getSource().getExtent())
geom.scale(1.1);
map.getView().fit(geom, { size: map.getSize() });
}
//enable dragging and dropping a gpx track on the map
function addGpxFromDragDrop() {
//if an interaction exists remove it first
if (interaction) {
map.removeInteraction(interaction);
}
//create the interaction
interaction = new ol.interaction.DragAndDrop({
formatConstructors: [
ol.format.GPX
],
});
//on drag event
interaction.on('addfeatures', function (e) {
//create a layer
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: e.features,
}),
style: function () {
return styles['track-red'];
},
name: 'GpxFromDragDrop',
type: 'track',
zIndex: 100
});
//add the layer to the map
map.addLayer(layer);
//get the coordinates from the layer
var coords = e.features[0].getGeometry().clone().transform('EPSG:3857', 'EPSG:4326');
//put the coordinates in a html element (to use elsewhere or post it to the server)
$('#map-gpx').val(JSON.stringify(coords.getCoordinates()[0]));
//add a marker to the first coordinate
addMarker(1, 'Starting Point', 'blue', coords.getFirstCoordinate()[1], coords.getFirstCoordinate()[0], false);
//zoom and fit bounds
var geom = ol.geom.Polygon.fromExtent(layer.getSource().getExtent())
geom.scale(1.1);
map.getView().fit(geom, { size: map.getSize() });
});
//add the interaction to the map
map.addInteraction(interaction);
}
//add a gpx with a html file upload control
function addGpxFromFileUpload() {
//bind a change event to the control
$('#map-upload').bind('change', function () {
var $this = $(this);
var gpxfile = document.getElementById($this.prop('id')).files[0];
//create a reader
var reader = new FileReader();
reader.onload = function (e) {
addGpxFromString(e.target.result, 'GpxFromFileUpload', 'red');
};
reader.readAsText(gpxfile);
$this.val('');
});
}
//add a gpx track from an array of single points [[52.8058, 6.7958], [52.8056, 6.7957], ... ]
function addGpxFromPoints(gpx, name, animate) {
//openlayers uses [lon, lat], not [lat, lon]
if (gpx[0][0] > 20 && gpx[0][1] < 20) {
gpx.map(function (l) {
return l.reverse();
});
}
//create a geometry from the coordinates
var geometry = new ol.geom.LineString(gpx).transform('EPSG:4326', 'EPSG:3857');
//create a layer
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: geometry,
name: name
})
]
}),
style: function () {
return styles['track-black'];
},
name: name,
type: 'track',
zIndex: 100
});
//add the layer to the map
map.addLayer(layer);
//add a marker to the first coordinate
addMarker(2, name + ' - Start', 'black', gpx[0][1], gpx[0][0], false);
//zoom and fit bounds
var geom = ol.geom.Polygon.fromExtent(layer.getSource().getExtent())
geom.scale(1.1);
map.getView().fit(geom, { size: map.getSize() });
//add a line animation
if (animate) {
animateLine(geometry, name + ' - Rijrichting');
}
}
//add multiple markers from an array
function addMultipleMarkers(markerArr, soort) {
var markerimg = '/images/site/marker-' + soort + '.png';
var markerimg_hover = '/images/site/marker-' + soort + '-red.png';
var url = '/toertochten/';
if (soort === 'club')
url = '/clubs/';
else if (soort == 'track')
url = '/routes/';
var normalstyle = new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 0.5],
src: markerimg
})),
zIndex: 100
});
var hoverstyle = new ol.style.Style({
image: new ol.style.Icon(({
anchor: [0.5, 0.5],
src: markerimg_hover
})),
zIndex: 1000
});
//create a new layer
var layer = new ol.layer.Vector({
source: new ol.source.Vector(),
style: normalstyle
});
//loop all the markers to add popover
for (var i = 0; i < markerArr.length; i++) {
var item = markerArr[i];
layer.getSource().addFeature(
new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([parseFloat(item.lng), parseFloat(item.lat)])),
id: item.id,
soort: 'icon',
name: '' + item.naam + '
' + item.regel2
})
);
}
featureArr = layer.getSource().getFeatures();
//maak de marker rood on hover
var hover = null;
map.on('pointermove', function (evt) {
map.getTargetElement().style.cursor = map.hasFeatureAtPixel(evt.pixel) ? 'pointer' : '';
if (hover != null) {
hover.setStyle(normalstyle);
hover = null;
}
map.forEachFeatureAtPixel(evt.pixel, function (f) {
if (f.get('soort') === 'icon') {
hover = f;
return true;
} else {
return false;
}
});
if (hover) {
hover.setStyle(hoverstyle);
}
});
//voeg de laag toe aan de map
map.addLayer(layer);
//maak de marker clickable
map.on('click', (e) => {
map.forEachFeatureAtPixel(e.pixel, function (f) {
if (f.get('soort') === 'icon') {
location.href = url + hover.get('id');
return true;
} else {
return false;
}
});
});
//zoom and fit bounds
//var geom = ol.geom.Polygon.fromExtent(layer.getSource().getExtent())
//geom.scale(1.1);
//map.getView().fit(geom, { size: map.getSize() });
}
//add a click event to the map to get the clicked coordinate
function addClickEvent() {
//create an overlay to anchor the hover popup to the map
var clickOverlay = new ol.Overlay({
element: document.getElementById('map-popup-click'),
autoPan: {
animation: {
duration: 250,
}
}
});
//add the overlay to the map
map.addOverlay(clickOverlay);
//add the click event and show the popup
var $popupclick = $('#map-popup-click');
map.on('singleclick', function (e) {
//if a popup is open then close it on map click
if (clickOverlay.getPosition()) {
clickOverlay.setPosition(null);
} else {
$popupclick.html('You clicked here:
' + ol.proj.toLonLat(e.coordinate) + '');
clickOverlay.setPosition(e.coordinate);
}
});
}
//add a moving marker to a route
function animateLine(route, name) {
var speed = 30;
var distance = 0;
var lasttime;
var position = new ol.geom.Point(route.clone().getFirstCoordinate());
var feature = new ol.Feature({
geometry: position,
});
var startMarker = new ol.Feature({
geometry: position,
});
var endMarker = new ol.Feature({
geometry: position
});
//create an icon style with a shape as marker
var style = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: 'black',
width: 2
}),
fill: new ol.style.Fill({
color: 'red'
})
})
});
//create a layer with the marker that will move
var layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [feature, startMarker, endMarker],
}),
name: name,
zIndex: 200
});
//add the layer to the map
map.addLayer(layer);
//function to move the marker
function moveFeature(e) {
var time = e.frameState.time;
distance = (distance + (speed * (time - lasttime)) / 1e6) % 2;
lasttime = time;
//if the distance > 1 then the end has been reached so restart
if (distance > 1) {
stopAnimation();
startAnimation();
}
//set the new coordinate for the marker
position.setCoordinates(route.getCoordinateAt(distance));
//draw it on the map
var context = ol.render.getVectorContext(e);
context.setStyle(style);
context.drawGeometry(position);
map.render();
}
//start the animation
function startAnimation() {
distance = 0;
lasttime = Date.now();
layer.on('postrender', moveFeature);
feature.setGeometry(null);
}
//stop the animation
function stopAnimation() {
feature.setGeometry(position);
layer.un('postrender', moveFeature);
}
//start
startAnimation();
}
//remove a layer from the map
function removeLayer(name) {
map.getLayers().forEach(layer => {
if (layer && layer.get('name') === name) {
map.removeLayer(layer);
}
});
}
//add multiple markers from an array and make them clustered
function addMultipleMarkersClustered(markerArr, soort) {
var markerimg = '/images/site/marker-' + soort + '.png';
var markerimg_hover = '/images/site/marker-' + soort + '-red.png';
var url = '';
//sort the array
markerArr.sort((a, b) => a.lat - b.lat)
//create the features
featureArr = new Array(markerArr.length);
var prevmarker;
var offsetdefault = 0.0009;
var offsettotal = offsetdefault;
for (let i = 0; i < markerArr.length; ++i) {
var item = markerArr[i];
var point = new ol.geom.Point(ol.proj.fromLonLat([parseFloat(item.lng), parseFloat(item.lat)]));
//if the features have the same coordinates move them slightly so they appear next to each other
if (item.lng.toFixed(3) + '_' + item.lat.toFixed(3) === prevmarker) {
point = new ol.geom.Point(ol.proj.fromLonLat([parseFloat(item.lng) + offsettotal, parseFloat(item.lat)]));
offsettotal += offsetdefault;
} else {
offsettotal = offsetdefault;
}
featureArr[i] = new ol.Feature({
geometry: point,
id: item.id,
soort: 'icon',
name: '' + item.naam + '
' + item.regel2
});
prevmarker = item.lng.toFixed(3) + '_' + item.lat.toFixed(3);
}
//create the source
var clusterSource = new ol.source.Cluster({
distance: 40,
minDistance: 10,
source: new ol.source.Vector({
features: featureArr,
})
});
//clustered style
var zindex = 100;
var clusters = new ol.layer.Vector({
zIndex: 1,
source: clusterSource,
style: function (feature) {
const size = feature.get('features').length;
//if there are no more clusters you can return a different icon
if (size === 1) {
//var style = new ol.style.Style();
//return style;
}
var radius = 12;
var textsize = 1;
var text = size.toString();
//dynamic cluster marker size calculations
if (size > 100) {
radius = (size * 0.2);
textsize = size / 60;
} else if (size > 50) {
radius = (size * 0.4);
textsize = size / 25;
} else if (size > 25) {
radius = (size * 0.6);
textsize = size / 20;
} else if (size > 1) {
radius = 15;
textsize = 1.3;
}
//max markercluster size
if (radius > 75) {
radius = 75;
textsize = 5;
}
var style = new ol.style.Style({
image: new ol.style.Circle({
radius: radius,
fill: new ol.style.Fill({
color: 'black'
}),
stroke: new ol.style.Stroke({
color: 'white',
width: 1
}),
}),
text: new ol.style.Text({
text: text,
fill: new ol.style.Fill({
color: '#fff',
}),
scale: textsize,
offsetY: size > 50 ? 2 : 1
}),
zIndex: zindex
});
zindex++;
return style;
},
});
//create a tile layer
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
//add the layers to the map
map.addLayer(raster);
map.addLayer(clusters);
//make the marker clickable
map.on('click', (e) => {
clusters.getFeatures(e.pixel).then((clickedFeatures) => {
if (clickedFeatures.length) {
//get the clustered coordinates
const features = clickedFeatures[0].get('features');
//when there are multiple features do a zoom otherwise handle the click
if (features.length > 1) {
const extent = ol.extent.boundingExtent(
features.map((r) => r.getGeometry().getCoordinates())
);
map.getView().fit(extent, { duration: 1000, padding: [50, 50, 50, 50] });
} else {
location.href = url + features[0].get('id');
}
}
});
});
//show a mouse pointer and popup on hover
map.on('pointermove', function (e) {
map.getTargetElement().style.cursor = map.hasFeatureAtPixel(e.pixel) ? 'pointer' : '';
clusters.getFeatures(e.pixel).then((hoveredFeatures) => {
if (hoveredFeatures.length) {
//get the clustered coordinates
const features = hoveredFeatures[0].get('features');
//when there is a single features show the popup
if (features.length === 1) {
if (features[0] && typeof features[0].get('name') != 'undefined' && features[0].get('name') != null && features[0].get('name') !== '') {
$popup.html(features[0].get('name'));
overlay.setPosition(e.coordinate);
} else {
overlay.setPosition(null);
}
}
}
});
});
}