Skip to content

Commit 1927247

Browse files
feat: add clusterMinPoints option Android and iOS (#3601)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 309c33f commit 1927247

File tree

10 files changed

+50
-1
lines changed

10 files changed

+50
-1
lines changed

include/mbgl/style/sources/geojson_source.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct GeoJSONOptions {
2929
bool cluster = false;
3030
uint16_t clusterRadius = 50;
3131
uint8_t clusterMaxZoom = 17;
32+
size_t clusterMinPoints = 2;
3233
using ClusterExpression = std::pair<std::shared_ptr<mbgl::style::expression::Expression>,
3334
std::shared_ptr<mbgl::style::expression::Expression>>;
3435
using ClusterProperties = std::map<std::string, ClusterExpression>;

platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/style/sources/GeoJsonOptions.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ class GeoJsonOptions : HashMap<String?, Any?>() {
100100
return this
101101
}
102102

103+
/**
104+
* Minimum number of points to form a cluster
105+
*
106+
* @param clusterMinPoints minimum number of points to form a cluster - Defaults to 2
107+
* @return the current instance for chaining
108+
*/
109+
fun withClusterMinPoints(clusterMinPoints: Int): GeoJsonOptions {
110+
this["clusterMinPoints"] = clusterMinPoints
111+
return this
112+
}
113+
103114
/**
104115
* An object defining custom properties on the generated clusters if clustering is enabled,
105116
* aggregating values from clustered points. Has the form {"property_name": [operator, [map_expression]]} or

platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/style/GeoJsonClusteringActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class GeoJsonClusteringActivity : AppCompatActivity() {
139139
.withCluster(true)
140140
.withClusterMaxZoom(14)
141141
.withClusterRadius(50)
142+
.withClusterMinPoints(3)
142143
.withClusterProperty(
143144
"max",
144145
Expression.max(Expression.accumulated(), Expression.get("max")),

platform/darwin/src/MLNShapeSource.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ FOUNDATION_EXTERN MLN_EXPORT const MLNShapeSourceOption MLNShapeSourceOptionClus
4444
*/
4545
FOUNDATION_EXTERN MLN_EXPORT const MLNShapeSourceOption MLNShapeSourceOptionClusterRadius;
4646

47+
/**
48+
An `NSNumber` object containing an integer; specifies the minimum number of points to form a
49+
cluster if clustering is enabled. The default value is 2.
50+
51+
This option only affects point features within an ``MLNShapeSource`` object; it
52+
is ignored when creating an ``MLNComputedShapeSource`` object.
53+
*/
54+
FOUNDATION_EXTERN MLN_EXPORT const MLNShapeSourceOption MLNShapeSourceOptionClusterMinPoints;
55+
4756
/**
4857
An `NSDictionary` object where the key is an `NSString`. The dictionary key will
4958
be the feature attribute key. The resulting attribute value is
@@ -233,7 +242,7 @@ MLN_EXPORT
233242
for the source.
234243
235244
This class supports the following options: ``MLNShapeSourceOptionClustered``,
236-
``MLNShapeSourceOptionClusterRadius``,
245+
``MLNShapeSourceOptionClusterRadius``, ``MLNShapeSourceOptionClusterMinPoints``,
237246
``MLNShapeSourceOptionMaximumZoomLevelForClustering``,
238247
``MLNShapeSourceOptionMinimumZoomLevel``, ``MLNShapeSourceOptionMinimumZoomLevel``,
239248
``MLNShapeSourceOptionBuffer``, and

platform/darwin/src/MLNShapeSource.mm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
const MLNShapeSourceOption MLNShapeSourceOptionBuffer = @"MLNShapeSourceOptionBuffer";
2121
const MLNShapeSourceOption MLNShapeSourceOptionClusterRadius = @"MLNShapeSourceOptionClusterRadius";
22+
const MLNShapeSourceOption MLNShapeSourceOptionClusterMinPoints = @"MLNShapeSourceOptionClusterMinPoints";
2223
const MLNShapeSourceOption MLNShapeSourceOptionClustered = @"MLNShapeSourceOptionClustered";
2324
const MLNShapeSourceOption MLNShapeSourceOptionClusterProperties = @"MLNShapeSourceOptionClusterProperties";
2425
const MLNShapeSourceOption MLNShapeSourceOptionMaximumZoomLevel = @"MLNShapeSourceOptionMaximumZoomLevel";
@@ -70,6 +71,14 @@
7071
geoJSONOptions->clusterRadius = value.integerValue;
7172
}
7273

74+
if (NSNumber *value = options[MLNShapeSourceOptionClusterMinPoints]) {
75+
if (![value isKindOfClass:[NSNumber class]]) {
76+
[NSException raise:NSInvalidArgumentException
77+
format:@"MLNShapeSourceOptionClusterMinPoints must be an NSNumber."];
78+
}
79+
geoJSONOptions->clusterMinPoints = value.integerValue;
80+
}
81+
7382
if (NSNumber *value = options[MLNShapeSourceOptionMaximumZoomLevelForClustering]) {
7483
if (![value isKindOfClass:[NSNumber class]]) {
7584
[NSException raise:NSInvalidArgumentException

platform/darwin/test/MLNShapeSourceTests.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ - (void)testGeoJSONOptionsFromDictionary {
1818
NSArray *clusterPropertyArray = @[reduceExpression, mapExpression];
1919
NSDictionary *options = @{MLNShapeSourceOptionClustered: @YES,
2020
MLNShapeSourceOptionClusterRadius: @42,
21+
MLNShapeSourceOptionClusterMinPoints: @3,
2122
MLNShapeSourceOptionClusterProperties: @{@"sumValue": clusterPropertyArray},
2223
MLNShapeSourceOptionMaximumZoomLevelForClustering: @98,
2324
MLNShapeSourceOptionMaximumZoomLevel: @99,
@@ -29,6 +30,7 @@ - (void)testGeoJSONOptionsFromDictionary {
2930
XCTAssertTrue(mbglOptions->cluster);
3031
XCTAssertEqual(mbglOptions->clusterRadius, 42);
3132
XCTAssertEqual(mbglOptions->clusterMaxZoom, 98);
33+
XCTAssertEqual(mbglOptions->clusterMinPoints, 3UL);
3234
XCTAssertEqual(mbglOptions->maxzoom, 99);
3335
XCTAssertEqual(mbglOptions->buffer, 1976);
3436
XCTAssertEqual(mbglOptions->tolerance, 0.42);

platform/ios/MapLibre.docc/For_Style_Authors.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ In style JSON | In the SDK
150150
`tolerance` | `MLNShapeSourceOptionSimplificationTolerance`
151151
`cluster` | `MLNShapeSourceOptionClustered`
152152
`clusterRadius` | `MLNShapeSourceOptionClusterRadius`
153+
`clusterMinPoints` | `MLNShapeSourceOptionClusterMinPoints`
153154
`clusterMaxZoom` | `MLNShapeSourceOptionMaximumZoomLevelForClustering`
154155
`lineMetrics` | `MLNShapeSourceOptionLineDistanceMetrics`
155156

src/mbgl/style/conversion/geojson_options.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ std::optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Conver
8181
}
8282
}
8383

84+
const auto clusterMinPointsValue = objectMember(value, "clusterMinPoints");
85+
if (clusterMinPointsValue) {
86+
if (toNumber(*clusterMinPointsValue)) {
87+
options.clusterMinPoints = static_cast<size_t>(*toNumber(*clusterMinPointsValue));
88+
} else {
89+
error.message = "GeoJSON source clusterMinPoints value must be a number";
90+
return std::nullopt;
91+
}
92+
}
93+
8494
const auto lineMetricsValue = objectMember(value, "lineMetrics");
8595
if (lineMetricsValue) {
8696
if (toBool(*lineMetricsValue)) {

src/mbgl/style/sources/geojson_source_impl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ std::shared_ptr<GeoJSONData> GeoJSONData::create(const GeoJSON& geoJSON,
9999
clusterOptions.maxZoom = options->clusterMaxZoom;
100100
clusterOptions.extent = util::EXTENT;
101101
clusterOptions.radius = static_cast<uint16_t>(::round(scale * options->clusterRadius));
102+
clusterOptions.minPoints = options->clusterMinPoints;
103+
102104
auto feature = std::make_shared<Feature>();
103105
clusterOptions.map = [feature, options](const PropertyMap& properties) -> PropertyMap {
104106
PropertyMap ret{};

test/style/conversion/geojson_options.test.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ TEST(GeoJSONOptions, RetainsDefaults) {
4040
ASSERT_EQ(converted.cluster, defaults.cluster);
4141
ASSERT_EQ(converted.clusterRadius, defaults.clusterRadius);
4242
ASSERT_EQ(converted.clusterMaxZoom, defaults.clusterMaxZoom);
43+
ASSERT_EQ(converted.clusterMinPoints, defaults.clusterMinPoints);
4344
ASSERT_TRUE(converted.clusterProperties.empty());
4445
}
4546

@@ -53,6 +54,7 @@ TEST(GeoJSONOptions, FullConversion) {
5354
"cluster": true,
5455
"clusterRadius": 4,
5556
"clusterMaxZoom": 5,
57+
"clusterMinPoints": 6,
5658
"lineMetrics": true,
5759
"clusterProperties": {
5860
"max": ["max", ["get", "scalerank"]],
@@ -73,6 +75,7 @@ TEST(GeoJSONOptions, FullConversion) {
7375
ASSERT_EQ(converted.cluster, true);
7476
ASSERT_EQ(converted.clusterRadius, 4);
7577
ASSERT_EQ(converted.clusterMaxZoom, 5);
78+
ASSERT_EQ(converted.clusterMinPoints, 6);
7679
ASSERT_EQ(converted.clusterProperties.size(), 3);
7780
ASSERT_EQ(converted.clusterProperties.count("max"), 1);
7881
ASSERT_EQ(converted.clusterProperties.count("sum"), 1);

0 commit comments

Comments
 (0)