Zooming in on clustered polygons that touch each other will not show the polygons.
I'm using OpenLayers 6 to show a number of suburb polygons. Clicking on the polygon will show some details about that suburb.
It is using clustering, which generally works well. Clicking on a cluster will zoom in to show separate polygons. However, when the suburbs are adjacent to one another, no amount of zooming will show separate suburbs, it will keep showing the cluster even on max zoom.
I assume this is related to the fact that the clustering uses the getFeaturesInExtent method which only checks bounding boxes.
Is there a way that I can have it show separate polygons when zooming in far enough? I tried changing the cluster distance on zoom, but setting the cluster distance to 0 will still show the cluster, so that won't work.
var suburbs = { "1": 6, "2": 8, "3": 4, "4": 3 };
var outerCircleFill = new ol.style.Fill({
color: 'rgba(255, 153, 102, 0.3)',
});
var innerCircleFill = new ol.style.Fill({
color: 'rgba(255, 165, 0, 0.7)',
});
var textFill = new ol.style.Fill({
color: '#fff',
});
var textStroke = new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.6)',
width: 3,
});
var innerCircle = new ol.style.Circle({
radius: 14,
fill: innerCircleFill,
});
var outerCircle = new ol.style.Circle({
radius: 20,
fill: outerCircleFill,
});
function clusterMemberStyle(clusterMember) {
var counter = suburbs[clusterMember.getId()] + "";
return [
new ol.style.Style({
text: new ol.style.Text({
text: counter,
fill: textFill,
stroke: textStroke
})
}),
new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(0,0,0,0.6)',
width: 1.5
}),
fill: new ol.style.Fill({
color: 'rgba(0,0,255,0.2)'
}),
geometry: clusterMember.getGeometry()
}),
];
}
function clusterStyle(feature) {
var size = feature.get('features').length;
if (size > 1) {
var sizes = 0;
for (i = 0; i < size; i++) {
var counter = suburbs[feature.get('features')[i].getId()];
sizes = sizes + counter;
}
return [
new ol.style.Style({
image: outerCircle
}),
new ol.style.Style({
image: innerCircle,
text: new ol.style.Text({
text: sizes.toString(),
fill: textFill,
stroke: textStroke
})
})
];
} else {
var originalFeature = feature.get('features')[0];
return clusterMemberStyle(originalFeature);
}
}
function loader(extent, resolution, projection, success, failure) {
const suburbDetails = [];
suburbDetails[0] = { Id: 1, Details: "POLYGON ((150.75546507852 -34.02685144907, 150.76809769254 -34.03407189893, 150.76809655411 -34.03542803254, 150.76962744947 -34.03660143543, 150.77290300422 -34.03250760379, 150.78390606531 -34.03868346316, 150.78041395874 -34.04294618514, 150.78469491346 -34.0455404595, 150.78876300055 -34.04048700693, 150.79331859368 -34.04561445715, 150.79632493322 -34.04418859274, 150.7986480203 -34.0449897574, 150.79604797314 -34.0437979425, 150.7898970426 -34.03936716382, 150.79256994002 -34.03605307293, 150.78697406807 -34.02944800924, 150.78725790352 -34.02843534844, 150.78603387208 -34.02505975568, 150.78712094288 -34.02318346922, 150.78436585472 -34.01990978794, 150.78175623404 -34.02047352367, 150.78022453192 -34.01974576504, 150.77952106568 -34.01885834897, 150.77948174966 -34.01765901329, 150.77242088553 -34.02506486515, 150.7611784055 -34.02003046104, 150.7597290465 -34.02237218423, 150.75546507852 -34.02685144907))" };
suburbDetails[1] = { Id: 2, Details: "POLYGON ((150.76118402592 -34.02001217452, 150.77242088553 -34.02506486515, 150.77948174966 -34.01765901329, 150.77952106568 -34.01885834897, 150.78022453192 -34.01974576504, 150.78175623404 -34.02047352367, 150.78436585472 -34.01990978794, 150.78712094288 -34.02318346922, 150.78603387208 -34.02505975568, 150.78725790352 -34.02843534844, 150.78697406807 -34.02944800924, 150.79065333357 -34.03374930323, 150.79101751318 -34.02814495135, 150.79007006534 -34.02752078048, 150.78930295484 -34.02359215498, 150.78967521099 -34.02080003663, 150.79081205876 -34.02007193737, 150.79181105845 -34.02007651797, 150.79085300622 -34.01835684119, 150.78954793144 -34.01822016739, 150.78833350042 -34.01756323317, 150.7876617577 -34.01538141842, 150.79471864183 -34.01147991792, 150.79557545487 -34.00944602677, 150.79423995635 -34.00801248919, 150.79450101433 -34.00650188828, 150.79637722012 -34.00423568662, 150.78772196876 -33.99364885, 150.78521503942 -33.99654532701, 150.77195795872 -34.00806560973, 150.76929176636 -34.01121462894, 150.76473043628 -34.01498343564, 150.76118402592 -34.02001217452))" };
suburbDetails[2] = { Id: 3, Details: "POLYGON ((150.802551331513 -34.0469804690657, 150.80799642036 -34.0532367801557, 150.812517744198 -34.0512843984657, 150.815688294885 -34.0491293232647, 150.818959869625 -34.0461982811533, 150.821195226229 -34.0429080971916, 150.816571246895 -34.0421635490303, 150.814057029843 -34.0428874441822, 150.812970559637 -34.0418513784342, 150.810928556077 -34.0415870217062, 150.809739762041 -34.0410203816518, 150.807345895383 -34.0414587830114, 150.806789483229 -34.042174798825, 150.80426984297 -34.0424319485132, 150.80402708047 -34.0436342423024, 150.804481383929 -34.0437653136149, 150.804382511232 -34.044319646125, 150.802551331513 -34.0469804690657))" };
suburbDetails[3] = { Id: 4, Details: "POLYGON ((150.79362775238 -34.05768761126, 150.79844556881 -34.06324639922, 150.79516971826 -34.0654042174, 150.800160414 -34.06620779337, 150.80251684708 -34.06801931508, 150.80431829328 -34.06689969608, 150.80766775604 -34.06338478514, 150.80925553682 -34.06238012022, 150.80896010197 -34.0620806964, 150.80950772046 -34.06139920087, 150.80758583423 -34.05921715306, 150.80570515541 -34.06029846001, 150.80494958156 -34.05941479985, 150.8017261165 -34.06136956595, 150.7995214436 -34.05877535435, 150.80059771279 -34.05813393677, 150.79883721406 -34.05611263104, 150.79362775238 -34.05768761126))" };
var wktReader = new ol.format.WKT();
suburbDetails.forEach(function (item) {
var feature = wktReader.readFeature(item["Details"]);
feature.setId(item.Id);
feature.getGeometry().transform('EPSG:4326', 'EPSG:3857');
vectorSource.addFeature(feature);
});
var extent = vectorSource.getExtent();
map.getView().setCenter(ol.extent.getCenter(extent));
map.getView().fit(extent, map.getSize());
}
var vectorSource = new ol.source.Vector({
loader: loader
});
var clusterSource = new ol.source.Cluster({
distance: 20,
source: vectorSource,
geometryFunction: function (feature) {
return feature.getGeometry().getInteriorPoint();
}
});
var layer = new ol.layer.Vector({
source: vectorSource,
style: clusterMemberStyle
});
var clusters = new ol.layer.Vector({
source: clusterSource,
style: clusterStyle
});
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
layers: [raster, clusters],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2,
maxZoom: 19
})
});
map.on('click', (event) => {
clusters.getFeatures(event.pixel).then((features) => {
if (features.length > 0) {
var clusterMembers = features[0].get('features');
if (clusterMembers.length > 1) {
// Calculate the extent of the cluster members.
var extent = ol.extent.createEmpty();
clusterMembers.forEach((feature) =>
ol.extent.extend(extent, feature.getGeometry().getExtent())
);
var view = map.getView();
view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });
}
else {
var feature = clusterMembers[0];
console.log("Suburb has " + suburbs[feature.getId()] + " things");
}
}
});
});
map.on('pointermove', function (e) {
var pixel = map.getEventPixel(e.originalEvent);
var hit = map.hasFeatureAtPixel(pixel);
map.getViewport().style.cursor = hit ? 'pointer' : '';
});
This shows them separately, but kinda need the clustering or the suburbs become invisible when zoomed out.
var map = new ol.Map({
layers: [raster, layer],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2,
maxZoom: 19
})
});
Map with clustering: https://jsfiddle.net/yrf5q2vu/4/
Same map without clustering: https://jsfiddle.net/txcs2w58/
I would like the first map to look like the second at that zoom level.
For the record, the loader method will normally pull from an API.