diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/ClusteredPointsDemo.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/ClusteredPointsDemo.kt index b55305ba..bddb5c3c 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/ClusteredPointsDemo.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/ClusteredPointsDemo.kt @@ -180,7 +180,7 @@ private suspend fun readGbfsData(gbfsFilePath: String): FeatureCollection { "vehicle_type" to (bike["vehicle_type"] ?: JsonNull), "vehicle_type_id" to (bike["vehicle_type_id"] ?: JsonNull), "last_reported" to (bike["last_reported"] ?: JsonNull), - "vehicle_range_meters" to (bike["vehicle_range_meters"] ?: JsonNull), + "current_range_meters" to (bike["current_range_meters"] ?: JsonNull), "is_reserved" to (bike["is_reserved"] ?: JsonNull), "is_disabled" to (bike["is_disabled"] ?: JsonNull), ), diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt index 2785ea3b..78e1e0fd 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt @@ -3,6 +3,7 @@ package dev.sargunv.maplibrecompose.core.source import dev.sargunv.maplibrecompose.core.util.correctedAndroidUri import dev.sargunv.maplibrecompose.core.util.toMLNExpression import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.dsl.Feature import dev.sargunv.maplibrecompose.expressions.dsl.const import io.github.dellisd.spatialk.geojson.GeoJson import org.maplibre.android.style.sources.GeoJsonOptions as MLNGeoJsonOptions @@ -33,7 +34,7 @@ public actual class GeoJsonSource : Source { withClusterProperty( key, const(value.operator).toMLNExpression()!!, - value.mapper.compile(ExpressionContext.None).toMLNExpression()!!, + Feature.get(value.property).compile(ExpressionContext.None).toMLNExpression()!!, ) } } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HillshadeLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HillshadeLayer.kt index e5d1ae13..1d44c43e 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HillshadeLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HillshadeLayer.kt @@ -28,8 +28,8 @@ import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor * gorges. * @param illuminationDirection The direction of the light source used to generate the hillshading * in degrees. A value in the range of `[0..360)`. `0` means - * - the top of the viewport if [illuminationAnchor] = [IlluminationAnchor.Viewport] or - * - north if [illuminationAnchor] = [IlluminationAnchor.Map] + * - the top of the viewport if [illuminationAnchor] = [IlluminationAnchor.Viewport] or + * - north if [illuminationAnchor] = [IlluminationAnchor.Map] * * @param illuminationAnchor Direction of light source when map is rotated. See * [illuminationDirection]. diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonOptions.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonOptions.kt index 7101a7c1..c4dd337d 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonOptions.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonOptions.kt @@ -1,7 +1,6 @@ package dev.sargunv.maplibrecompose.core.source import androidx.compose.runtime.Immutable -import dev.sargunv.maplibrecompose.expressions.ast.Expression /** * @param minZoom Minimum zoom level at which to create vector tiles (lower means more field of view @@ -16,11 +15,12 @@ import dev.sargunv.maplibrecompose.expressions.ast.Expression * @param cluster If the data is a collection of point features, setting this to `true` clusters the * points by radius into groups. Cluster groups become new `Point` features in the source with * additional properties: - * * `cluster`: Is `true` if the point is a cluster - * * `cluster_id`: A unique id for the cluster to be used in conjunction with the cluster - * inspection methods. (TODO which are not implemented yet on [GeoJsonSource] - #80) - * * `point_count`: Number of original points grouped into this cluster - * * `point_count_abbreviated`: An abbreviated point count + * + * - `cluster`: Is `true` if the point is a cluster + * - `cluster_id`: A unique id for the cluster to be used in conjunction with the cluster + * inspection methods. + * - `point_count`: Number of original points grouped into this cluster + * - `point_count_abbreviated`: An abbreviated point count * * @param clusterRadius Radius of each cluster when clustering points, measured in 1/512ths of a * tile. I.e. a value of 512 indicates a radius equal to the width of a tile. @@ -33,10 +33,19 @@ import dev.sargunv.maplibrecompose.expressions.ast.Expression * Minimum number of points necessary to form a cluster if clustering is enabled. * * @param clusterProperties A map defining custom properties on the generated clusters if clustering - * is enabled, aggregating values from clustered points. The keys are the property names, the - * values are expressions. + * is enabled, aggregating values from clustered features. The keys are the property names, the + * values define how the value should be aggregated from the features within one cluster. + * + * Example: + * ```kt + * mapOf( + * "totalCapacity" to ClusterAggregation("+", "capacity") + * ) + * ``` * - * TODO examples missing, see https://maplibre.org/maplibre-style-spec/sources/#clusterproperties + * Let's assume a geojson source consisting of points for parking lots in which each parking lot has + * a `capacity` property. The code above adds the `capacity` values of all parking lots within one + * cluster together into a property named `totalCapacity` of the cluster point. * * @param lineMetrics Whether to calculate line distance metrics. This is required for * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]s that specify a `gradient`. @@ -54,6 +63,16 @@ public data class GeoJsonOptions( val clusterProperties: Map = emptyMap(), val lineMetrics: Boolean = false, ) { - // TODO seems to contradict https://maplibre.org/maplibre-style-spec/sources/#clusterproperties - public data class ClusterProperty(val operator: String, val mapper: Expression<*>) + /** + * Defines how the values from the features within a cluster should be aggregated into a cluster + * property. + * + * @param operator Expression name to aggregate the values from the feature properties. This can + * be any expression function that accepts at least 2 operands but typically is a math function + * like `"+"` or `"max"`. See + * [MapLibre Style Spec][https://maplibre.org/maplibre-style-spec/expressions/#math] for valid + * names for the operator. + * @param property name of the feature property whose value should be aggregated. + */ + public data class ClusterProperty(val operator: String, val property: String) } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt index bf113cf6..ba201427 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt @@ -13,7 +13,8 @@ import cocoapods.MapLibre.MLNShapeSourceOptionSimplificationTolerance import dev.sargunv.maplibrecompose.core.util.toMLNShape import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.expressions.ExpressionContext -import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.dsl.Feature +import dev.sargunv.maplibrecompose.expressions.dsl.const import io.github.dellisd.spatialk.geojson.GeoJson import platform.Foundation.NSNumber import platform.Foundation.NSURL @@ -43,8 +44,11 @@ public actual class GeoJsonSource : Source { put(MLNShapeSourceOptionClusterRadius, NSNumber(options.clusterRadius)) put( MLNShapeSourceOptionClusterProperties, - options.clusterProperties.mapValues { (_, p) -> - FunctionCall.of(p.operator, p.mapper).compile(ExpressionContext.None).toNSExpression() + options.clusterProperties.mapValues { (_, value) -> + arrayOf( + const(value.operator).toNSExpression(), + Feature.get(value.property).compile(ExpressionContext.None).toNSExpression(), + ) }, ) }