In this example a Popup will show either a list of all "Sub-Features" (when it's within a cluster) or properties from the feature when it's not within a cluster. Also, it will display a popup, even if the feature is not clustered at the moment (in a visual way).
Ol3 offers a clustering-feature, which allows to keep the map clean on lower zoom levels when you got a layer with a lot of single features.
To get a single-features name displayed on popups you need a function which "digs" through the Cluster and all its containing features. Having a Popup-possibility for unclustered features (like in the default popup-example) COMBINED with a cluster-feature-popup needs an additionally logic.
When combining the default Popup-Example and the Cluster-Example from the repository it's necessary to know that the clustered features are part of the new created Cluster-Object.
Problem here: On lower zoom-levels the clustered features are displayed in a non-clustered way, like "normal" features. But still, they are part of the parent-cluster-object.
Solution: Add a simple typeof-construction to check if the current clicked feature is part of a cluster or not.
<!DOCTYPE html>
<html>
<head>
<title>Popups on clusterd and unclusterd Features</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/ol3/3.7.0/ol.css" type="text/css">
<script src="//cdnjs.cloudflare.com/ajax/libs/ol3/3.7.0/ol.js"></script>
<style>
.popup {
background-color:#FFF;
border: 1px solid #CCC;
padding: 0.1em 1em 0.5em 0.5em;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<h3>Display a Popup on clustered and unclustered Features in the same map</h3>
<div class="col-lg-12 col-md-12 col-sm-12">
<div id="map" class="map"></div>
<div id="popup" class="popup">
<a href="/" id="popup-closer">Close</a>
<div id="popup-content"></div>
</div>
</div>
</div>
</div>
<script>
function elem_id(id) {
return document.getElementById(id);
}
var view = new ol.View({
center: [0, 0],
zoom: 2
});
var count = 100;
var features = new Array(count);
var e = 4500000;
for (var i = 0; i < count; ++i) {
var coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e];
features[i] = new ol.Feature({
geometry: new ol.geom.Point(coordinates),
name: 'random' + [i]
});
}
var source = new ol.source.Vector({
features: features
});
var clusterSource = new ol.source.Cluster({
distance: 40,
source: source
});
var styleCache = {};
var clusters = new ol.layer.Vector({
source: clusterSource,
style: function (feature, resolution) {
var size = feature.get('features').length;
var style = styleCache[size];
if (!style) {
style = [new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
stroke: new ol.style.Stroke({
color: '#fff'
}),
fill: new ol.style.Fill({
color: '#3399CC'
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: '#fff'
})
})
})];
styleCache[size] = style;
}
return style;
}
});
var map = new ol.Map({
layers: [clusters,
new ol.layer.Vector({
source: new ol.source.Vector({
url: 'data/countries.geojson',
format: new ol.format.GeoJSON()
})
})
],
target: 'map',
controls: ol.control.defaults({
attributionOptions: /** @type {olx.control.AttributionOptions} */
({
collapsible: false
})
}),
view: view
});
var popup = elem_id('popup');
var popup_closer = elem_id('popup-closer');
var popup_content = elem_id('popup-content');
var olpopup = new ol.Overlay({
element: popup,
autoPan: false
});
map.addOverlay(olpopup);
popup_closer.onclick = function () {
olpopup.setPosition(undefined);
return false;
};
var OpenPopup = function (evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function (feature, layer) {
if (feature) {
var coord = map.getCoordinateFromPixel(evt.pixel);
if (typeof feature.get('features') === 'undefined') {
popup_content.innerHTML = '<h5><b>' + feature.get('name') + '</b></h5><i>this is an <b>unclustered</b> feature</i>';
} else {
var cfeatures = feature.get('features');
if (cfeatures.length > 1) {
popup_content.innerHTML = '<h5><strong>all "Sub-Features"</strong></h5>';
for (var i = 0; i < cfeatures.length; i++) {
$(popup_content).append('<article><strong>' + cfeatures[i].get('name') + '</article>');
}
}
if (cfeatures.length == 1) {
popup_content.innerHTML = '<h5><strong>' + cfeatures[0].get('name') + '</strong></h5><i>this is a single, but <b>clustered</b> feature</i>';
}
}
olpopup.setPosition(coord);
} else {
olpopup.setPosition(undefined);
}
});
};
map.on('click', OpenPopup);
</script>
</body>
</html>