简体   繁体   中英

OpenLayers, clusters with touching polygons. How do I show them separately when zooming in?

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.

If two features share the same InteriorPoint they will always be clustered unless you check for a minimum resolution in the style function, which means it may have to handle more than one originalFeature

function clusterStyle(feature, resolution) {
    var size = feature.get('features').length;
    if (size > 1 && resolution > 10) {
        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 styles = [];
        feature.get('features').forEach(function(originalFeature) {
            Array.prototype.push.apply(styles, clusterMemberStyle(originalFeature));
        });
        return styles;
    }
}

https://jsfiddle.net/k6L7zncv/

When a cluster is displayed as multiple original features you would need to find which one intersects the clicked coordinate

        if (clusterMembers.length > 1) {
            var view = map.getView();
            if (map.getView().getResolution() > 10) {
                // Calculate the extent of the cluster members.
                var extent = ol.extent.createEmpty();
                clusterMembers.forEach((feature) =>
                    ol.extent.extend(extent, feature.getGeometry().getExtent())
                );
                view.fit(extent, { duration: 500, padding: [50, 50, 50, 50] });
            } else {
                // Find the member which was clicked
                var member = clusterMembers.find(function(member) {
                    return member.getGeometry().intersectsCoordinate(event.coordinate);
                });
                view.fit(member.getGeometry(), { duration: 500, padding: [50, 50, 50, 50] });
            }
        }
        else {
            var feature = clusterMembers[0];
            console.log("Suburb has " + suburbs[feature.getId()] + " things");
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM