diff --git a/src/ol/source/Cluster.js b/src/ol/source/Cluster.js index 11b6affd64..33ce42063a 100644 --- a/src/ol/source/Cluster.js +++ b/src/ol/source/Cluster.js @@ -38,10 +38,20 @@ import {getUid} from '../util.js'; * return feature.getGeometry(); * } * ``` - * @property {function(Feature, Array):any} [hydrate] Function called after creating - * cluster feature * See {@link module:ol/geom/Polygon~Polygon#getInteriorPoint} for a way to get a cluster * calculation point for polygons. + * @property {function(Point, Array):Feature} [createClusterFeature] + * Function that takes cluster's ceter {@link module:ol/geom/Point} and array + * of {@link module:ol/Feature} included in this cluster, must return + * {@link module:ol/Feature} that will be used to render. Default implementation is: + * ```js + * function(point, features) { + * return new Feature({ + * geometry: point, + * features: features + * }); + * } + * ``` * @property {VectorSource} [source] Source. * @property {boolean} [wrapX=true] Whether to wrap the world horizontally. */ @@ -111,10 +121,10 @@ class Cluster extends VectorSource { }; /** - * @type {function(Feature, Array):any} + * @type {function(Point, Array):Feature} * @protected */ - this.hydrate = options.hydrate; + this.createClusterFeature = options.createClusterFeature; /** * @type {VectorSource} @@ -188,6 +198,28 @@ class Cluster extends VectorSource { this.updateDistance(this.distance, minDistance); } + /** + * Current function used to create cluster features + * @return {function(Point, Array):Feature | undefined} cluster creation function + * @api + */ + getCreateClusterFeature() { + return this.createClusterFeature; + } + + /** + * Set function used to create cluster features + * @param {function(Point, Array):Feature | undefined} createClusterFeature creation function + * @api + */ + setCreateClusterFeature(createClusterFeature) { + const changed = this.createClusterFeature != createClusterFeature; + this.createClusterFeature = createClusterFeature; + if (changed) { + this.refresh(); + } + } + /** * The configured minimum distance between clusters. * @return {number} The minimum distance in pixels. @@ -302,12 +334,14 @@ class Cluster extends VectorSource { centroid[0] * (1 - ratio) + searchCenter[0] * ratio, centroid[1] * (1 - ratio) + searchCenter[1] * ratio, ]); - const cluster = new Feature(geometry); - if (this.hydrate) { - this.hydrate(cluster, features); + if (this.createClusterFeature) { + return this.createClusterFeature(geometry, features); + } else { + return new Feature({ + geometry, + features, + }); } - cluster.set('features', features, true); - return cluster; } } diff --git a/test/browser/spec/ol/source/cluster.test.js b/test/browser/spec/ol/source/cluster.test.js index 655381b802..961962f2cf 100644 --- a/test/browser/spec/ol/source/cluster.test.js +++ b/test/browser/spec/ol/source/cluster.test.js @@ -75,7 +75,7 @@ describe('ol.source.Cluster', function () { expect(source.getFeatures().length).to.be(1); expect(source.getFeatures()[0].get('features').length).to.be(2); }); - it('hydrates cluster feature with additional fields', function () { + it('custom cluster feature with additional fields', function () { const feature1 = new Feature(new Point([0, 0])); const feature2 = new Feature(new Point([0, 0])); feature1.set('value', 1); @@ -84,14 +84,41 @@ describe('ol.source.Cluster', function () { source: new VectorSource({ features: [feature1, feature2], }), - hydrate: function (clusterFeature, features) { - clusterFeature.set('sum', 3); + createClusterFeature: function (clusterPoint, features) { + let sum = 0; + for (const ft of features) { + sum += ft.get('value'); + } + return new Feature({ + geometry: clusterPoint, + sum: sum, + }); }, }); source.loadFeatures(extent, 1, projection); expect(source.getFeatures().length).to.be(1); expect(source.getFeatures()[0].get('sum')).to.be(3); }); + it('reacts setCreateClusterFeature', function () { + const feature1 = new Feature(new Point([0, 0])); + const feature2 = new Feature(new Point([0, 0])); + const source = new Cluster({ + source: new VectorSource({ + features: [feature1, feature2], + }), + }); + source.loadFeatures(extent, 1, projection); + expect(source.getFeatures().length).to.be(1); + expect(source.getFeatures()[0].get('sum')).to.be(undefined); + source.setCreateClusterFeature(function (clusterPoint, features) { + return new Feature({ + geometry: clusterPoint, + sum: features.length, + }); + }); + expect(source.getFeatures().length).to.be(1); + expect(source.getFeatures()[0].get('sum')).to.be(2); + }); }); describe('#setDistance', function () {