diff --git a/build.gradle.kts b/build.gradle.kts index d3dfc4bb..6fb7bbe3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,6 +46,7 @@ tasks.register("generateDocs") { dependencies { dokka(project(":lib:maplibre-compose:")) + dokka(project(":lib:maplibre-compose-expressions:")) dokka(project(":lib:maplibre-compose-material3:")) } diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/AnimatedLayerDemo.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/AnimatedLayerDemo.kt index 1ee2efb6..6e80fca3 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/AnimatedLayerDemo.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/AnimatedLayerDemo.kt @@ -18,18 +18,18 @@ import dev.sargunv.maplibrecompose.compose.rememberCameraState import dev.sargunv.maplibrecompose.compose.rememberStyleState import dev.sargunv.maplibrecompose.compose.source.rememberGeoJsonSource import dev.sargunv.maplibrecompose.core.CameraPosition -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.exponential -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.interpolate -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.zoom -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin import dev.sargunv.maplibrecompose.demoapp.DEFAULT_STYLE import dev.sargunv.maplibrecompose.demoapp.Demo import dev.sargunv.maplibrecompose.demoapp.DemoMapControls import dev.sargunv.maplibrecompose.demoapp.DemoOrnamentSettings import dev.sargunv.maplibrecompose.demoapp.DemoScaffold import dev.sargunv.maplibrecompose.demoapp.generated.Res +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.exponential +import dev.sargunv.maplibrecompose.expressions.dsl.interpolate +import dev.sargunv.maplibrecompose.expressions.dsl.zoom +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin import io.github.dellisd.spatialk.geojson.Position import org.jetbrains.compose.resources.ExperimentalResourceApi diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraFollowDemo.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraFollowDemo.kt index 7c801769..c916dd1b 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraFollowDemo.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/CameraFollowDemo.kt @@ -33,8 +33,6 @@ import dev.sargunv.maplibrecompose.compose.rememberStyleState import dev.sargunv.maplibrecompose.compose.source.rememberGeoJsonSource import dev.sargunv.maplibrecompose.core.CameraMoveReason import dev.sargunv.maplibrecompose.core.CameraPosition -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.offset import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.demoapp.DEFAULT_STYLE import dev.sargunv.maplibrecompose.demoapp.Demo @@ -42,6 +40,8 @@ import dev.sargunv.maplibrecompose.demoapp.DemoMapControls import dev.sargunv.maplibrecompose.demoapp.DemoOrnamentSettings import dev.sargunv.maplibrecompose.demoapp.DemoScaffold import dev.sargunv.maplibrecompose.demoapp.PositionVectorConverter +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.offset import io.github.dellisd.spatialk.geojson.Point import io.github.dellisd.spatialk.geojson.Position import kotlin.math.roundToInt 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 2ebe2f71..eb57841c 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 @@ -21,13 +21,6 @@ import dev.sargunv.maplibrecompose.compose.rememberCameraState import dev.sargunv.maplibrecompose.compose.rememberStyleState import dev.sargunv.maplibrecompose.compose.source.rememberGeoJsonSource import dev.sargunv.maplibrecompose.core.CameraPosition -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.asNumber -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.asString -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.feature -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.not -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.offset -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.step import dev.sargunv.maplibrecompose.core.source.GeoJsonOptions import dev.sargunv.maplibrecompose.demoapp.DEFAULT_STYLE import dev.sargunv.maplibrecompose.demoapp.Demo @@ -35,6 +28,13 @@ import dev.sargunv.maplibrecompose.demoapp.DemoMapControls import dev.sargunv.maplibrecompose.demoapp.DemoOrnamentSettings import dev.sargunv.maplibrecompose.demoapp.DemoScaffold import dev.sargunv.maplibrecompose.demoapp.generated.Res +import dev.sargunv.maplibrecompose.expressions.dsl.asNumber +import dev.sargunv.maplibrecompose.expressions.dsl.asString +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.feature +import dev.sargunv.maplibrecompose.expressions.dsl.not +import dev.sargunv.maplibrecompose.expressions.dsl.offset +import dev.sargunv.maplibrecompose.expressions.dsl.step import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.FeatureCollection import io.github.dellisd.spatialk.geojson.Point diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/MarkersDemo.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/MarkersDemo.kt index e066ed9c..66c5afd2 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/MarkersDemo.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/MarkersDemo.kt @@ -20,13 +20,6 @@ import dev.sargunv.maplibrecompose.compose.rememberCameraState import dev.sargunv.maplibrecompose.compose.rememberStyleState import dev.sargunv.maplibrecompose.compose.source.rememberGeoJsonSource import dev.sargunv.maplibrecompose.core.CameraPosition -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.Feature.get -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.asString -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.format -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.image -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.offset -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.span import dev.sargunv.maplibrecompose.demoapp.DEFAULT_STYLE import dev.sargunv.maplibrecompose.demoapp.Demo import dev.sargunv.maplibrecompose.demoapp.DemoMapControls @@ -34,6 +27,13 @@ import dev.sargunv.maplibrecompose.demoapp.DemoOrnamentSettings import dev.sargunv.maplibrecompose.demoapp.DemoScaffold import dev.sargunv.maplibrecompose.demoapp.generated.Res import dev.sargunv.maplibrecompose.demoapp.generated.marker +import dev.sargunv.maplibrecompose.expressions.dsl.Feature.get +import dev.sargunv.maplibrecompose.expressions.dsl.asString +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.format +import dev.sargunv.maplibrecompose.expressions.dsl.image +import dev.sargunv.maplibrecompose.expressions.dsl.offset +import dev.sargunv.maplibrecompose.expressions.dsl.span import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position import org.jetbrains.compose.resources.painterResource diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Layers.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Layers.kt index 21fd55c9..2b8fdf85 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Layers.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/docs/Layers.kt @@ -12,13 +12,13 @@ import dev.sargunv.maplibrecompose.compose.layer.CircleLayer import dev.sargunv.maplibrecompose.compose.layer.LineLayer import dev.sargunv.maplibrecompose.compose.source.getBaseSource import dev.sargunv.maplibrecompose.compose.source.rememberGeoJsonSource -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.exponential -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.interpolate -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.zoom -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin import dev.sargunv.maplibrecompose.demoapp.generated.Res +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.exponential +import dev.sargunv.maplibrecompose.expressions.dsl.interpolate +import dev.sargunv.maplibrecompose.expressions.dsl.zoom +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin import org.jetbrains.compose.resources.ExperimentalResourceApi @Composable diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0f23f2cc..05a38f6b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ androidx-activity = "1.9.3" androidx-composeUi = "1.7.6" androidx-navigation = "2.8.0-alpha11" +webview = "1.9.40" kermit = "2.0.5" kotlinx-coroutines = "1.9.0" ktor = "3.0.3" @@ -23,6 +24,7 @@ gradle-spotless = "7.0.0.BETA4" androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-composeUi-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-composeUi" } androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } +webview = { module = "io.github.kevinnzou:compose-webview-multiplatform", version.ref = "webview" } kermit = { group = "co.touchlab", name = "kermit", version.ref = "kermit" } kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } diff --git a/lib/maplibre-compose-expressions/MODULE.md b/lib/maplibre-compose-expressions/MODULE.md new file mode 100644 index 00000000..1f173df5 --- /dev/null +++ b/lib/maplibre-compose-expressions/MODULE.md @@ -0,0 +1,16 @@ +# Module maplibre-compose-expressions + +Utilities for creating MapLibre expressions with a type-safe Kotlin DSL. + +# Package dev.sargunv.maplibrecompose.expressions.ast + +The abstract syntax tree (AST) for the expression language. + +# Package dev.sargunv.maplibrecompose.expressions.dsl + +The Kotlin DSL for creating MapLibre expressions. This is the primary API you'll +be using to create expressions. + +# Package dev.sargunv.maplibrecompose.expressions.value + +The interfaces and enums defining the type system for MapLibre expressions. diff --git a/lib/maplibre-compose-expressions/build.gradle.kts b/lib/maplibre-compose-expressions/build.gradle.kts new file mode 100644 index 00000000..17489d28 --- /dev/null +++ b/lib/maplibre-compose-expressions/build.gradle.kts @@ -0,0 +1,54 @@ +@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalComposeLibrary::class) + +import org.jetbrains.compose.ExperimentalComposeLibrary +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree + +plugins { + id("library-conventions") + id("android-library-conventions") + id(libs.plugins.kotlin.multiplatform.get().pluginId) + id(libs.plugins.android.library.get().pluginId) + id(libs.plugins.compose.get().pluginId) + id(libs.plugins.mavenPublish.get().pluginId) +} + +android { namespace = "dev.sargunv.maplibrecompose.expressions" } + +mavenPublishing { + pom { + name = "MapLibre Compose Expressions" + description = "MapLibre expressions DSL for MapLibre Compose." + url = "https://github.com/sargunv/maplibre-compose" + } +} + +kotlin { + androidTarget { + compilerOptions { jvmTarget.set(JvmTarget.JVM_11) } + instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test) + publishLibraryVariants("release", "debug") + } + iosArm64() + iosSimulatorArm64() + iosX64() + jvm("desktop") + + sourceSets { + commonMain.dependencies { implementation(compose.foundation) } + + commonTest.dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-common")) + implementation(kotlin("test-annotations-common")) + } + + androidUnitTest.dependencies { implementation(compose.desktop.currentOs) } + + androidInstrumentedTest.dependencies { + implementation(compose.desktop.uiTestJUnit4) + implementation(libs.androidx.composeUi.testManifest) + } + } +} diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/utils.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/Defaults.kt similarity index 58% rename from lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/utils.kt rename to lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/Defaults.kt index 895569e6..f5097aec 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/utils.kt +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/Defaults.kt @@ -1,14 +1,16 @@ -package dev.sargunv.maplibrecompose.core.expression +package dev.sargunv.maplibrecompose.expressions import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.heatmapDensity -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.interpolate -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.linear - -// helpers for default expression values +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.heatmapDensity +import dev.sargunv.maplibrecompose.expressions.dsl.interpolate +import dev.sargunv.maplibrecompose.expressions.dsl.linear +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue public val ZeroPadding: PaddingValues.Absolute = PaddingValues.Absolute(0.dp) diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ExpressionContext.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ExpressionContext.kt new file mode 100644 index 00000000..970b5326 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ExpressionContext.kt @@ -0,0 +1,43 @@ +package dev.sargunv.maplibrecompose.expressions + +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.value.FloatValue + +/** + * The context used while converting a high-level [Expression] to a low-level [CompiledExpression]. + * + * It defines how to resolve certain expressions (TextUnit, bitmaps) to their MapLibre counterparts. + * MapLibre Compose users should not need to implement this interface; it is used internally by the + * MapLibre Compose library. + */ +public interface ExpressionContext { + /** The scale factor to convert EMs to the desired unit */ + public val emScale: Expression + + /** The scale factor to convert SPs to the desired unit */ + public val spScale: Expression + + /** @return the resolved identifier for the [bitmap]. */ + public fun resolveBitmap(bitmap: ImageBitmap): String + + /** @return the resolved identifier for the [painter]. */ + public fun resolvePainter(painter: Painter): String + + /** A context where no complex types can be resolved. */ + public object None : ExpressionContext { + override val emScale: Expression + get() = error("TextUnit not allowed in this context") + + override val spScale: Expression + get() = error("TextUnit not allowed in this context") + + override fun resolveBitmap(bitmap: ImageBitmap): String = + error("Bitmap not allowed in this context") + + override fun resolvePainter(painter: Painter): String = + error("Painter not allowed in this context") + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/BitmapLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/BitmapLiteral.kt new file mode 100644 index 00000000..4c9b6cec --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/BitmapLiteral.kt @@ -0,0 +1,21 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.graphics.ImageBitmap +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.StringValue + +/** + * A [Literal] representing an [ImageBitmap] value, which will be loaded as an image into the style + * upon compilation. + */ +public data class BitmapLiteral private constructor(override val value: ImageBitmap) : + Literal { + override fun compile(context: ExpressionContext): StringLiteral = + StringLiteral.of(context.resolveBitmap(value)) + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + public fun of(value: ImageBitmap): BitmapLiteral = BitmapLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/BooleanLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/BooleanLiteral.kt new file mode 100644 index 00000000..7d8478e0 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/BooleanLiteral.kt @@ -0,0 +1,16 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue + +/** A [Literal] representing a [Boolean] value. */ +public data class BooleanLiteral private constructor(override val value: Boolean) : + CompiledLiteral { + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val True: BooleanLiteral = BooleanLiteral(true) + private val False: BooleanLiteral = BooleanLiteral(false) + + public fun of(value: Boolean): BooleanLiteral = if (value) True else False + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/ColorLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/ColorLiteral.kt new file mode 100644 index 00000000..bedd49a4 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/ColorLiteral.kt @@ -0,0 +1,24 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.graphics.Color +import dev.sargunv.maplibrecompose.expressions.value.ColorValue + +/** A [Literal] representing a [Color] value. */ +public data class ColorLiteral private constructor(override val value: Color) : + CompiledLiteral { + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val black = ColorLiteral(Color.Black) + private val white = ColorLiteral(Color.White) + private val transparent = ColorLiteral(Color.Transparent) + + public fun of(value: Color): ColorLiteral = + when (value) { + Color.Black -> black + Color.White -> white + Color.Transparent -> transparent + else -> ColorLiteral(value) + } + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledExpression.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledExpression.kt new file mode 100644 index 00000000..74b2b260 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledExpression.kt @@ -0,0 +1,16 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue + +/** + * An [Expression] reduced to only those data types supported by the MapLibre SDKs. This can be + * thought of as an intermediate representation between the high level expression DSL and the + * platform-specific encoding. + */ +public sealed interface CompiledExpression : Expression { + override fun compile(context: ExpressionContext): CompiledExpression = this + + @Suppress("UNCHECKED_CAST") + override fun cast(): CompiledExpression = this as CompiledExpression +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledFunctionCall.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledFunctionCall.kt new file mode 100644 index 00000000..bd621450 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledFunctionCall.kt @@ -0,0 +1,24 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue + +/** A [Literal] representing an function call with args all of [CompiledExpression] */ +public data class CompiledFunctionCall +private constructor( + val name: String, + val args: List>, + val isLiteralArg: (Int) -> Boolean, +) : CompiledExpression { + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + args.forEach { it.visit(block) } + } + + public companion object { + public fun of( + name: String, + args: List>, + isLiteralArg: (Int) -> Boolean = { false }, + ): CompiledFunctionCall = CompiledFunctionCall(name, args, isLiteralArg) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledListLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledListLiteral.kt new file mode 100644 index 00000000..fcb5917b --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledListLiteral.kt @@ -0,0 +1,20 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue + +/** A [Literal] representing a JSON array with elements all [CompiledLiteral]. */ +public data class CompiledListLiteral +private constructor(override val value: List>) : + CompiledLiteral, List>> { + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.forEach { it.visit(block) } + } + + public companion object { + internal fun of( + value: List> + ): CompiledListLiteral = CompiledListLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledLiteral.kt new file mode 100644 index 00000000..5fc122ce --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledLiteral.kt @@ -0,0 +1,14 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue + +/** An [Expression] representing a constant literal value of a type supported by MapLibre. */ +public sealed interface CompiledLiteral : + Literal, CompiledExpression { + + override fun compile(context: ExpressionContext): CompiledLiteral = this + + @Suppress("UNCHECKED_CAST") + override fun cast(): CompiledLiteral = this as CompiledLiteral +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledMapLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledMapLiteral.kt new file mode 100644 index 00000000..fafbc86c --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledMapLiteral.kt @@ -0,0 +1,20 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.MapValue + +/** A [Literal] representing a JSON object with values all [CompiledLiteral]. */ +public data class CompiledMapLiteral +private constructor(override val value: Map>) : + CompiledLiteral, Map>> { + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.values.forEach { it.visit(block) } + } + + public companion object { + internal fun of( + value: Map> + ): CompiledMapLiteral = CompiledMapLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledOptions.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledOptions.kt new file mode 100644 index 00000000..a0f39484 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/CompiledOptions.kt @@ -0,0 +1,20 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.MapValue + +/** An [Expression] representing a JSON object with values all [CompiledExpression]. */ +public data class CompiledOptions +private constructor(val value: Map>) : + CompiledExpression> { + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.values.forEach { it.visit(block) } + } + + public companion object { + internal fun of( + value: Map> + ): CompiledOptions = CompiledOptions(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpLiteral.kt new file mode 100644 index 00000000..0557ed2b --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpLiteral.kt @@ -0,0 +1,21 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.DpValue + +/** A [Literal] representing a [Dp] value. */ +public data class DpLiteral private constructor(override val value: Dp) : Literal { + + override fun compile(context: ExpressionContext): CompiledLiteral = + FloatLiteral.of(value.value).cast() + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val cache = FloatCache { DpLiteral(it.dp) } + + public fun of(value: Dp): DpLiteral = cache[value.value] + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpOffsetLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpOffsetLiteral.kt new file mode 100644 index 00000000..e200440b --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpOffsetLiteral.kt @@ -0,0 +1,22 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.unit.DpOffset +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue + +/** A [Literal] representing a [DpOffset] value. */ +public data class DpOffsetLiteral private constructor(override val value: DpOffset) : + Literal { + override fun compile(context: ExpressionContext): CompiledLiteral = + OffsetLiteral.of(Offset(value.x.value, value.y.value)).cast() + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val zero = DpOffsetLiteral(DpOffset.Zero) + + public fun of(value: DpOffset): DpOffsetLiteral = + if (value == DpOffset.Zero) zero else DpOffsetLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpPaddingLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpPaddingLiteral.kt new file mode 100644 index 00000000..0ddd0485 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/DpPaddingLiteral.kt @@ -0,0 +1,18 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.foundation.layout.PaddingValues +import dev.sargunv.maplibrecompose.expressions.ZeroPadding +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue + +/** A [Literal] representing a [PaddingValues] value. */ +public data class DpPaddingLiteral private constructor(override val value: PaddingValues.Absolute) : + CompiledLiteral { + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val zero = DpPaddingLiteral(ZeroPadding) + + public fun of(value: PaddingValues.Absolute): DpPaddingLiteral = + if (value == ZeroPadding) zero else DpPaddingLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/EnumLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/EnumLiteral.kt new file mode 100644 index 00000000..f6782287 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/EnumLiteral.kt @@ -0,0 +1,17 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.EnumValue + +/** A [Literal] representing an enum value of type [T]. */ +public data class EnumLiteral> private constructor(override val value: T) : + Literal { + override fun compile(context: ExpressionContext): CompiledLiteral = + value.literal.cast() + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + internal fun > of(value: T): EnumLiteral = EnumLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Expression.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Expression.kt new file mode 100644 index 00000000..26191bd3 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Expression.kt @@ -0,0 +1,15 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue + +/** An [Expression] that evaluates to a value of type [T]. */ +public sealed interface Expression { + /** Transform this expression into the equivalent [CompiledExpression]. */ + public fun compile(context: ExpressionContext): CompiledExpression + + public fun visit(block: (Expression<*>) -> Unit) + + @Suppress("UNCHECKED_CAST") + public fun cast(): Expression = this as Expression +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/FloatCache.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/FloatCache.kt new file mode 100644 index 00000000..701f560f --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/FloatCache.kt @@ -0,0 +1,24 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +internal class FloatCache(val init: (Float) -> T) { + private val smallInts = List(SIZE) { init(it.toFloat()) } + private val smallFloats = List(SIZE) { init(it.toFloat() * RESOLUTION) } + + operator fun get(float: Float): T { + return when { + float.isSmallInt() -> smallInts[float.toInt()] + (float / RESOLUTION).isSmallInt() -> smallFloats[(float / RESOLUTION).toInt()] + + else -> init(float) + } + } + + companion object { + const val SIZE = 512 + const val RESOLUTION = 0.05f + + internal fun Float.isSmallInt() = toInt().toFloat() == this && toInt().isSmallInt() + + internal fun Int.isSmallInt() = this in 0.. { + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val cache = FloatCache(::FloatLiteral) + + public fun of(value: Float): FloatLiteral = cache[value] + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/FunctionCall.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/FunctionCall.kt new file mode 100644 index 00000000..c20e781a --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/FunctionCall.kt @@ -0,0 +1,28 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue + +/** An [Expression] representing a function call. */ +public data class FunctionCall +private constructor( + val name: String, + val args: List>, + val isLiteralArg: (Int) -> Boolean, +) : Expression { + override fun compile(context: ExpressionContext): CompiledExpression = + CompiledFunctionCall.of(name, args.map { it.compile(context) }, isLiteralArg) + + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + args.forEach { it.visit(block) } + } + + public companion object { + public fun of( + name: String, + vararg args: Expression<*>, + isLiteralArg: (Int) -> Boolean = { false }, + ): FunctionCall = FunctionCall(name, args.asList(), isLiteralArg) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/IntCache.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/IntCache.kt new file mode 100644 index 00000000..6bb51273 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/IntCache.kt @@ -0,0 +1,18 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +internal class IntCache(val init: (Int) -> T) { + private val smallInts = List(SIZE) { init(it) } + + operator fun get(int: Int): T { + return when { + int.isSmallInt() -> smallInts[int] + else -> init(int) + } + } + + companion object { + const val SIZE = 512 + + internal fun Int.isSmallInt() = this in 0.. { + + override fun compile(context: ExpressionContext): CompiledLiteral = + FloatLiteral.of(value.toFloat()).cast() + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val cache = IntCache(::IntLiteral) + + public fun of(value: Int): IntLiteral = cache[value] + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/ListLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/ListLiteral.kt new file mode 100644 index 00000000..edd27d69 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/ListLiteral.kt @@ -0,0 +1,24 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue + +/** A [Literal] representing a JSON array. */ +public data class ListLiteral +private constructor(override val value: List>) : + Literal, List>> { + + override fun compile(context: ExpressionContext): CompiledListLiteral = + CompiledListLiteral.of(value.map { it.compile(context) }) + + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.forEach { it.visit(block) } + } + + public companion object { + internal fun of(value: List>): ListLiteral = + ListLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Literal.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Literal.kt new file mode 100644 index 00000000..fca9f063 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Literal.kt @@ -0,0 +1,15 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue + +/** An [Expression] representing a constant literal value. */ +public sealed interface Literal : Expression { + + public val value: L + + override fun compile(context: ExpressionContext): CompiledLiteral + + @Suppress("UNCHECKED_CAST") + override fun cast(): Literal = this as Literal +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/MapLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/MapLiteral.kt new file mode 100644 index 00000000..7050e6db --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/MapLiteral.kt @@ -0,0 +1,25 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.MapValue + +/** A [Literal] representing a JSON object. */ +public data class MapLiteral +private constructor(override val value: Map>) : + Literal, Map>> { + + override fun compile(context: ExpressionContext): CompiledMapLiteral { + return CompiledMapLiteral.of(value.mapValues { it.value.compile(context) }) + } + + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.values.forEach { it.visit(block) } + } + + public companion object { + internal fun of(value: Map>): MapLiteral = + MapLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/MillisecondsLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/MillisecondsLiteral.kt new file mode 100644 index 00000000..59ddcd19 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/MillisecondsLiteral.kt @@ -0,0 +1,22 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +/** A [Literal] representing a [Duration] value. */ +public data class MillisecondsLiteral private constructor(override val value: Duration) : + Literal { + + override fun compile(context: ExpressionContext): CompiledLiteral = + FloatLiteral.of(value.inWholeMilliseconds.toFloat()).cast() + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val cache = IntCache { MillisecondsLiteral(it.milliseconds) } + + public fun of(value: Duration): MillisecondsLiteral = cache[value.inWholeMilliseconds.toInt()] + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/NullLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/NullLiteral.kt new file mode 100644 index 00000000..f9d47f5d --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/NullLiteral.kt @@ -0,0 +1,8 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +/** A [Literal] representing a `null` value. */ +public data object NullLiteral : CompiledLiteral { + override val value: Nothing? = null + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/OffsetLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/OffsetLiteral.kt new file mode 100644 index 00000000..0edc538c --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/OffsetLiteral.kt @@ -0,0 +1,17 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.geometry.Offset +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue + +/** A [Literal] representing a [Offset] value. */ +public data class OffsetLiteral private constructor(override val value: Offset) : + CompiledLiteral { + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val zero = OffsetLiteral(Offset.Zero) + + public fun of(value: Offset): OffsetLiteral = + if (value == Offset.Zero) zero else OffsetLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Options.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Options.kt new file mode 100644 index 00000000..9c7b6d63 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/Options.kt @@ -0,0 +1,23 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.MapValue + +/** An [Expression] representing a JSON object with values all [Expression]. */ +public data class Options +private constructor(val value: Map>) : Expression> { + + override fun compile(context: ExpressionContext): CompiledOptions = + CompiledOptions.of(value.mapValues { it.value.compile(context) }) + + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.values.forEach { it.visit(block) } + } + + public companion object { + internal fun build(block: MutableMap>.() -> Unit) = + Options(mutableMapOf>().apply(block).mapValues { it.value }) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/PainterLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/PainterLiteral.kt new file mode 100644 index 00000000..77fe364c --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/PainterLiteral.kt @@ -0,0 +1,21 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.graphics.painter.Painter +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.value.StringValue + +/** + * A [Literal] representing a [Painter] value, which will be drawn to a bitmap and loaded as an + * image into the style upon compilation. + */ +public data class PainterLiteral private constructor(override val value: Painter) : + Literal { + override fun compile(context: ExpressionContext): StringLiteral = + StringLiteral.of(context.resolvePainter(value)) + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + public fun of(value: Painter): PainterLiteral = PainterLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/StringLiteral.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/StringLiteral.kt new file mode 100644 index 00000000..eb8c2ec6 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/StringLiteral.kt @@ -0,0 +1,16 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import dev.sargunv.maplibrecompose.expressions.value.StringValue + +/** A [Literal] representing a [String] value. */ +public data class StringLiteral private constructor(override val value: String) : + CompiledLiteral { + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + private val empty = StringLiteral("") + + public fun of(value: String): StringLiteral = + if (value.isEmpty()) empty else StringLiteral(value) + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/TextUnitCalculation.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/TextUnitCalculation.kt new file mode 100644 index 00000000..76fdb0a8 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/TextUnitCalculation.kt @@ -0,0 +1,43 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.dsl.times +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TextUnitValue + +/** + * An [Expression] representing a [TextUnit] value in EM or SP, which may be transformed into + * multiplication function call to convert to the needed units upon compilation. + */ +public data class TextUnitCalculation +private constructor(val value: Expression, val type: TextUnitType) : + Expression { + override fun compile(context: ExpressionContext): CompiledExpression { + val scale = + when (type) { + TextUnitType.Sp -> context.spScale + TextUnitType.Em -> context.emScale + else -> error("Unrecognized TextUnitType: $type") + } + return (value * scale).compile(context).cast() + } + + override fun visit(block: (Expression<*>) -> Unit) { + block(this) + value.visit(block) + } + + public companion object { + public fun of(value: TextUnit): TextUnitCalculation { + require(value.type != TextUnitType.Unspecified) { "TextUnit type must be specified" } + return TextUnitCalculation(FloatLiteral.of(value.value), value.type) + } + + public fun of(value: Expression, type: TextUnitType): TextUnitCalculation { + require(type != TextUnitType.Unspecified) { "TextUnit type must be specified" } + return TextUnitCalculation(value, type) + } + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/TextUnitOffsetCalculation.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/TextUnitOffsetCalculation.kt new file mode 100644 index 00000000..92ade455 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/ast/TextUnitOffsetCalculation.kt @@ -0,0 +1,49 @@ +package dev.sargunv.maplibrecompose.expressions.ast + +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import androidx.compose.ui.unit.isSpecified +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.dsl.interpolate +import dev.sargunv.maplibrecompose.expressions.dsl.linear +import dev.sargunv.maplibrecompose.expressions.dsl.offset +import dev.sargunv.maplibrecompose.expressions.value.TextUnitOffsetValue + +/** + * An [Expression] representing a [TextUnitOffsetValue] in EM or SP, which may be transformed into + * an interpolation function call to convert to the needed units upon compilation. + */ +public data class TextUnitOffsetCalculation private constructor(val x: TextUnit, val y: TextUnit) : + Expression { + override fun compile(context: ExpressionContext): CompiledExpression { + val scale = + when (x.type) { + TextUnitType.Sp -> context.spScale + TextUnitType.Em -> context.emScale + else -> error("Unrecognized TextUnitType: ${x.type}") + } + + // some reasonably large number to bound the interpolation + val maxScale = 1000f + + return interpolate( + type = linear(), + input = scale, + 0f to offset(0f, 0f), + 1f to offset(x.value, y.value), + maxScale to offset(x.value * maxScale, y.value * maxScale), + ) + .compile(context) + .cast() + } + + override fun visit(block: (Expression<*>) -> Unit): Unit = block(this) + + public companion object { + public fun of(x: TextUnit, y: TextUnit): TextUnitOffsetCalculation { + require(x.isSpecified && y.isSpecified) { "TextUnit type must be specified" } + require(x.type == y.type) { "X and Y text units must have the same type" } + return TextUnitOffsetCalculation(x, y) + } + } +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/collections.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/collections.kt new file mode 100644 index 00000000..86205242 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/collections.kt @@ -0,0 +1,70 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.IntValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.MapValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import kotlin.jvm.JvmName + +/** Returns the item at [index]. */ +@JvmName("getAt") +public operator fun Expression>.get( + index: Expression +): Expression = FunctionCall.of("at", index, this).cast() + +/** Returns whether this list contains the [item]. */ +@JvmName("containsList") +public fun Expression>.contains( + item: Expression +): Expression = FunctionCall.of("in", item, this).cast() + +/** + * Returns the first index at which the [item] is located in this list, or `-1` if it cannot be + * found. Accepts an optional [startIndex] from where to begin the search. + */ +@JvmName("indexOfList") +public fun Expression>.indexOf( + item: Expression, + startIndex: Expression? = null, +): Expression { + val args = buildList { + add(item) + add(this@indexOf) + startIndex?.let { add(it) } + } + return FunctionCall.of("index-of", *args.toTypedArray()).cast() +} + +/** + * Returns the items in this list from the [startIndex] (inclusive) to the end of this list if + * [endIndex] is not specified or `null`, otherwise to [endIndex] (exclusive). + */ +public fun Expression>.slice( + startIndex: Expression, + endIndex: Expression? = null, +): Expression> { + val args = buildList { + add(this@slice) + add(startIndex) + endIndex?.let { add(it) } + } + return FunctionCall.of("slice", *args.toTypedArray()).cast() +} + +/** Gets the length of a this list. */ +@JvmName("lengthOfList") +public fun Expression>.length(): Expression = + FunctionCall.of("length", this).cast() + +/** Returns the value corresponding the given [key] or `null` if it is not present in this map. */ +public operator fun Expression>.get( + key: Expression +): Expression = FunctionCall.of("get", key, this).cast() + +/** Returns whether the given [key] is in this map. */ +public fun Expression>.has(key: Expression): Expression = + FunctionCall.of("has", key, this).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/color.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/color.kt new file mode 100644 index 00000000..ed886ab3 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/color.kt @@ -0,0 +1,34 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IntValue +import dev.sargunv.maplibrecompose.expressions.value.VectorValue + +/** + * Returns a four-element list, containing the color's red, green, blue, and alpha components, in + * that order. + */ +public fun Expression.toRgbaComponents(): Expression> = + FunctionCall.of("to-rgba", this).cast() + +/** + * Creates a color value from [red], [green], and [blue] components, which must range between 0 and + * 255, and optionally an [alpha] component which must range between 0 and 1. + * + * If any component is out of range, the expression is an error. + */ +public fun rgbColor( + red: Expression, + green: Expression, + blue: Expression, + alpha: Expression? = null, +): Expression = + if (alpha != null) { + FunctionCall.of("rgba", red, green, blue, alpha) + } else { + FunctionCall.of("rgb", red, green, blue) + } + .cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/decision.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/decision.kt new file mode 100644 index 00000000..1eacfe21 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/decision.kt @@ -0,0 +1,301 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CollatorValue +import dev.sargunv.maplibrecompose.expressions.value.ComparableValue +import dev.sargunv.maplibrecompose.expressions.value.EquatableValue +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.GeoJsonValue +import dev.sargunv.maplibrecompose.expressions.value.MatchableValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import kotlin.jvm.JvmName + +/** + * Selects the first output from the given [conditions] whose corresponding test condition evaluates + * to `true`, or the [fallback] value otherwise. + * + * Example: + * ``` + * switch( + * condition( + * test = feature.has(const("color1")) and feature.has(const("color2")), + * output = interpolate( + * linear(), + * zoom(), + * 1 to feature.get(const("color1")).convertToColor(), + * 20 to feature.get(const("color2")).convertToColor() + * ), + * ), + * condition( + * test = feature.has(const("color")), + * output = feature.get(const("color")).convertToColor(), + * ), + * fallback = const(Color.Red), + * ) + * ``` + * + * If the feature has both a "color1" and "color2" property, the result is an interpolation between + * these two colors based on the zoom level. Otherwise, if the feature has a "color" property, that + * color is returned. If the feature has none of the three, the color red is returned. + */ +public fun switch( + vararg conditions: Condition, + fallback: Expression, +): Expression = + FunctionCall.of( + "case", + *conditions.foldToArgs { (test, output) -> + add(test) + add(output) + }, + fallback, + ) + .cast() + +public data class Condition +internal constructor( + internal val test: Expression, + internal val output: Expression, +) + +/** Create a [Condition], see [case] */ +public fun condition( + test: Expression, + output: Expression, +): Condition = Condition(test, output) + +/** + * Selects the output from the given [cases] whose label value matches the [input], or the + * [fallback] value if no match is found. + * + * Each label must be unique. If the input type does not match the type of the labels, the result + * will be the [fallback] value. + * + * Example: + * ``` + * switch( + * input = feature.get(const("building_type")).asString(), + * case( + * label = "residential", + * output = const(Color.Cyan), + * ), + * case( + * label = listOf("commercial", "industrial"), + * output = const(Color.Yellow), + * ), + * fallback = const(Color.Red), + * ) + * ``` + * + * If the feature has a property "building_type" with the value "residential", cyan is returned. + * Otherwise, if the value of that property is either "commercial" or "industrial", yellow is + * returned. If none of that is true, the fallback is returned, i.e. red. + */ +public fun switch( + input: Expression, + vararg cases: Case, + fallback: Expression, +): Expression = + FunctionCall.of( + "match", + input, + *cases.foldToArgs { (label, output) -> + add(label) + add(output) + }, + fallback, + isLiteralArg = { i -> + // label positions are odd, starting from 1 and not including the fallback + i in 1..(cases.size * 2) && i % 2 == 1 + }, + ) + .cast() + +public data class Case<@Suppress("unused") I : MatchableValue, O : ExpressionValue> +internal constructor(internal val label: Expression<*>, internal val output: Expression) + +/** Create a [Case], see [switch] */ +public fun case(label: String, output: Expression): Case = + Case(const(label), output) + +/** Create a [Case], see [switch] */ +public fun case(label: Number, output: Expression): Case = + Case(const(label.toFloat()), output) + +/** Create a [Case], see [switch] */ +@JvmName("stringsCase") +public fun case( + label: List, + output: Expression, +): Case = Case(const(label), output) + +/** Create a [Case], see [switch] */ +@JvmName("numbersCase") +public fun case( + label: List, + output: Expression, +): Case = Case(const(label), output) + +/** + * Evaluates each expression in [values] in turn until the first non-null value is obtained, and + * returns that value. + */ +public fun coalesce(vararg values: Expression): Expression = + FunctionCall.of("coalesce", *values).cast() + +/** Returns whether this expression is equal to [other]. */ +public infix fun Expression.eq( + other: Expression +): Expression = FunctionCall.of("==", this, other).cast() + +/** + * Returns whether the [left] string expression is equal to the [right] string expression. An + * optional [collator] (see [collator] function) can be specified to control locale-dependent string + * comparisons. + */ +public fun eq( + left: Expression, + right: Expression, + collator: Expression, +): Expression = FunctionCall.of("==", left, right, collator).cast() + +/** Returns whether this expression is not equal to [other]. */ +public infix fun Expression.neq( + other: Expression +): Expression = FunctionCall.of("!=", this, other).cast() + +/** + * Returns whether the [left] string expression is not equal to the [right] string expression. An + * optional [collator] (see [collator]) can be specified to control locale-dependent string + * comparisons. + */ +public fun neq( + left: Expression, + right: Expression, + collator: Expression, +): Expression = FunctionCall.of("!=", left, right, collator).cast() + +/** + * Returns whether this expression is strictly greater than [other]. + * + * Strings are compared lexicographically (`"b" > "a"`). + */ +public infix fun Expression>.gt( + other: Expression> +): Expression = FunctionCall.of(">", this, other).cast() + +/** + * Returns whether the [left] string expression is strictly greater than the [right] string + * expression. An optional [collator] (see [collator]) can be specified to control locale-dependent + * string comparisons. + * + * Strings are compared lexicographically (`"b" > "a"`). + */ +public fun gt( + left: Expression, + right: Expression, + collator: Expression, +): Expression = FunctionCall.of(">", left, right, collator).cast() + +/** + * Returns whether this expression is strictly less than [other]. + * + * Strings are compared lexicographically (`"a" < "b"`). + */ +public infix fun Expression>.lt( + other: Expression> +): Expression = FunctionCall.of("<", this, other).cast() + +/** + * Returns whether the [left] string expression is strictly less than the [right] string expression. + * An optional [collator] (see [collator]) can be specified to control locale-dependent string + * comparisons. + * + * Strings are compared lexicographically (`"a" < "b"`). + */ +public fun lt( + left: Expression, + right: Expression, + collator: Expression, +): Expression = FunctionCall.of("<", left, right, collator).cast() + +/** + * Returns whether this expression is greater than or equal to [other]. + * + * Strings are compared lexicographically (`"b" >= "a"`). + */ +public infix fun Expression>.gte( + other: Expression> +): Expression = FunctionCall.of(">=", this, other).cast() + +/** + * Returns whether the [left] string expression is greater than or equal to the [right] string + * expression. An optional [collator] (see [collator]) can be specified to control locale-dependent + * string comparisons. + * + * Strings are compared lexicographically (`"b" >= "a"`). + */ +public fun gte( + left: Expression, + right: Expression, + collator: Expression, +): Expression = FunctionCall.of(">=", left, right, collator).cast() + +/** + * Returns whether this string expression is less than or equal to [other]. + * + * Strings are compared lexicographically (`"a" <= "b"`). + */ +public infix fun Expression>.lte( + other: Expression> +): Expression = FunctionCall.of("<=", this, other).cast() + +/** + * Returns whether the [left] string expression is less than or equal to the [right] string + * expression. An optional [collator] (see [collator]) can be specified to control locale-dependent + * string comparisons. + * + * Strings are compared lexicographically (`"a" < "b"`). + */ +public fun lte( + left: Expression, + right: Expression, + collator: Expression, +): Expression = FunctionCall.of("<=", left, right, collator).cast() + +/** Returns whether all [expressions] are `true`. */ +public fun all(vararg expressions: Expression): Expression = + FunctionCall.of("all", *expressions).cast() + +/** Returns whether both this and [other] expressions are `true`. */ +public infix fun Expression.and( + other: Expression +): Expression = all(this, other) + +/** Returns whether any [expressions] are `true`. */ +public fun any(vararg expressions: Expression): Expression = + FunctionCall.of("any", *expressions).cast() + +/** Returns whether any of this or the [other] expressions are `true`. */ +public infix fun Expression.or( + other: Expression +): Expression = any(this, other) + +/** Negates this expression. */ +@JvmName("notOperator") +public operator fun Expression.not(): Expression = + FunctionCall.of("!", this).cast() + +/** + * Returns true if the evaluated feature is fully contained inside a boundary of the input geometry, + * false otherwise. The input value can be a valid GeoJSON of type Polygon, MultiPolygon, Feature, + * or FeatureCollection. Supported features for evaluation: + * - Point: Returns false if a point is on the boundary or falls outside the boundary. + * - LineString: Returns false if any part of a line falls outside the boundary, the line intersects + * the boundary, or a line's endpoint is on the boundary. + */ +public fun within(geometry: Expression): Expression = + FunctionCall.of("within", geometry).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/feature.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/feature.kt new file mode 100644 index 00000000..bae1f5f4 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/feature.kt @@ -0,0 +1,73 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.GeometryType +import dev.sargunv.maplibrecompose.expressions.value.MapValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue + +/** Object to access feature-related data, see [feature] */ +public object Feature { + /** + * Returns the value corresponding to the given [key] in the current feature's properties or + * `null` if it is not present. + */ + public fun get(key: Expression): Expression<*> = FunctionCall.of("get", key) + + /** Tests for the presence of a property value [key] in the current feature's properties. */ + public fun has(key: Expression): Expression = + FunctionCall.of("has", key).cast() + + /** + * Gets the feature properties object. Note that in some cases, it may be more efficient to use + * [get]`("property_name")` directly. + */ + public fun properties(): Expression> = FunctionCall.of("properties").cast() + + /** + * **Note: Not supported on native platforms. See + * [maplibre-native#1698](https://github.com/maplibre/maplibre-native/issues/1698)** + * + * Retrieves a property value from the current feature's state. Returns `null` if the requested + * property is not present on the feature's state. + * + * A feature's state is not part of the GeoJSON or vector tile data, and must be set + * programmatically on each feature. + * + * When `source.promoteId` is not provided, features are identified by their `id` attribute, which + * must be an integer or a string that can be cast to an integer. When `source.promoteId` is + * provided, features are identified by their `promoteId` property, which may be a number, string, + * or any primitive data type. Note that [state] can only be used with layer properties that + * support data-driven styling. + */ + // TODO: document which layer properties support feature state expressions on which platforms + public fun state(key: Expression): Expression = + FunctionCall.of("feature-state", key).cast() + + /** Gets the feature's geometry type. */ + public fun type(): Expression = FunctionCall.of("geometry-type").cast() + + /** Gets the feature's id, if it has one. */ + public fun id(): Expression = FunctionCall.of("id").cast() + + /** + * Gets the progress along a gradient line. Can only be used in the `gradient` property of a line + * layer, see [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]. + */ + public fun lineProgress(value: Expression): Expression = + FunctionCall.of("line-progress", value).cast() + + /** + * Gets the value of a cluster property accumulated so far. Can only be used in the + * `clusterProperties` option of a clustered GeoJSON source, see + * [GeoJsonOptions][dev.sargunv.maplibrecompose.core.source.GeoJsonOptions]. + */ + public fun accumulated(key: Expression): Expression = + FunctionCall.of("accumulated", key).cast() +} + +/** Accesses to feature-related data */ +public val feature: Feature = Feature diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/format.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/format.kt new file mode 100644 index 00000000..850feb51 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/format.kt @@ -0,0 +1,69 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.ast.Options +import dev.sargunv.maplibrecompose.expressions.value.FormattableValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.TextUnitValue + +/** + * Returns a formatted string for displaying mixed-format text in the `textField` property (see + * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]). The input may contain a + * string literal or expression, including an [image] expression. + * + * Example: + * ``` + * format( + * span( + * feature.get("name").asString().substring(const(0), const(1)).uppercase(), + * textScale = const(1.5f), + * ), + * span(feature.get("name").asString().substring(const(1))) + * ) + * ``` + * + * Capitalizes the first letter of the features' property "name" and formats it to be extra-large, + * the rest of the name is written normally. + */ +public fun format(vararg spans: FormatSpan): Expression = + FunctionCall.of( + "format", + *spans.foldToArgs { span -> + add(span.value) + add(span.options) + }, + ) + .cast() + +/** Configures a span of text in a [format] expression. */ +public fun span( + value: Expression, + textFont: Expression? = null, + textColor: Expression? = null, + textSize: Expression? = null, +): FormatSpan = + FormatSpan(value = value, textFont = textFont, textColor = textColor, textSize = textSize) + +/** Configures an image in a [format] expression. */ +public fun span(value: Expression): FormatSpan = FormatSpan(value = value) + +/** Represents a component of a [format] expression. See [span]. */ +public data class FormatSpan +internal constructor( + val value: Expression, + val textFont: Expression? = null, + val textColor: Expression? = null, + val textSize: Expression? = null, +) { + internal val options + get() = + Options.build( + fun MutableMap>.() { + textFont?.let { put("text-font", it) } + textColor?.let { put("text-color", it) } + textSize?.let { put("font-scale", it) } + } + ) +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/image.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/image.kt new file mode 100644 index 00000000..212a8316 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/image.kt @@ -0,0 +1,62 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import dev.sargunv.maplibrecompose.expressions.ast.BitmapLiteral +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.ast.PainterLiteral +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue + +/** + * Returns an image type for use in `iconImage` (see + * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]), `pattern` entries (see + * [BackgroundLayer][dev.sargunv.maplibrecompose.compose.layer.BackgroundLayer], + * [FillLayer][dev.sargunv.maplibrecompose.compose.layer.FillLayer], + * [FillExtrusionLayer][dev.sargunv.maplibrecompose.compose.layer.FillExtrusionLayer], + * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]) and as a section in the + * [format] expression. + * + * If set, the image argument will check that the requested image exists in the style and will + * return either the resolved image name or `null`, depending on whether or not the image is + * currently in the style. This validation process is synchronous and requires the image to have + * been added to the style before requesting it in the image argument. + */ +public fun image(value: Expression): Expression = + FunctionCall.of("image", value).cast() + +/** + * Returns an image type for use in `iconImage` (see + * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]), `pattern` entries (see + * [BackgroundLayer][dev.sargunv.maplibrecompose.compose.layer.BackgroundLayer], + * [FillLayer][dev.sargunv.maplibrecompose.compose.layer.FillLayer], + * [FillExtrusionLayer][dev.sargunv.maplibrecompose.compose.layer.FillExtrusionLayer], + * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]) and as a section in the + * [format] expression. + * + * The [ImageBitmap] will be registered with the style when it's referenced by a layer, and + * unregistered from the style if it's no longer referenced by any layer. An ID referencing the + * bitmap will be generated automatically and inserted into the expression. + */ +public fun image(value: ImageBitmap): Expression = + FunctionCall.of("image", BitmapLiteral.of(value)).cast() + +/** + * Returns an image type for use in `iconImage` (see + * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]), `pattern` entries (see + * [BackgroundLayer][dev.sargunv.maplibrecompose.compose.layer.BackgroundLayer], + * [FillLayer][dev.sargunv.maplibrecompose.compose.layer.FillLayer], + * [FillExtrusionLayer][dev.sargunv.maplibrecompose.compose.layer.FillExtrusionLayer], + * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]) and as a section in the + * [format] expression. + * + * The [Painter] will be drawn to an [ImageBitmap] and registered with the style when it's + * referenced by a layer, and unregistered from the style if it's no longer referenced by any layer. + * An ID referencing the bitmap will be generated automatically and inserted into the expression. + * + * The bitmap will be created with the intrinsic size of the painter, or 16x16 DP if the painter + * does not have an intrinsic size. + */ +public fun image(value: Painter): Expression = + FunctionCall.of("image", PainterLiteral.of(value)).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/inputs.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/inputs.kt new file mode 100644 index 00000000..19a6799b --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/inputs.kt @@ -0,0 +1,20 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.FloatValue + +/** + * Gets the current zoom level. Note that in layer style properties, [zoom] may only appear as the + * input to a top-level [step] or [interpolate] (, [interpolateHcl], [interpolateLab], ...) + * expression. + */ +public fun zoom(): Expression = FunctionCall.of("zoom").cast() + +/** + * Gets the kernel density estimation of a pixel in a heatmap layer, which is a relative measure of + * how many data points are crowded around a particular pixel. Can only be used in the expression + * for the `color` parameter in a HeatmapLayer + * [HeatmapLayer][dev.sargunv.maplibrecompose.compose.layer.HeatmapLayer]. + */ +public fun heatmapDensity(): Expression = FunctionCall.of("heatmap-density").cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/literals.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/literals.kt new file mode 100644 index 00000000..303bb987 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/literals.kt @@ -0,0 +1,133 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import dev.sargunv.maplibrecompose.expressions.ast.BooleanLiteral +import dev.sargunv.maplibrecompose.expressions.ast.ColorLiteral +import dev.sargunv.maplibrecompose.expressions.ast.DpLiteral +import dev.sargunv.maplibrecompose.expressions.ast.DpOffsetLiteral +import dev.sargunv.maplibrecompose.expressions.ast.DpPaddingLiteral +import dev.sargunv.maplibrecompose.expressions.ast.EnumLiteral +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FloatLiteral +import dev.sargunv.maplibrecompose.expressions.ast.IntLiteral +import dev.sargunv.maplibrecompose.expressions.ast.ListLiteral +import dev.sargunv.maplibrecompose.expressions.ast.Literal +import dev.sargunv.maplibrecompose.expressions.ast.MillisecondsLiteral +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.ast.OffsetLiteral +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral +import dev.sargunv.maplibrecompose.expressions.ast.TextUnitCalculation +import dev.sargunv.maplibrecompose.expressions.ast.TextUnitOffsetCalculation +import dev.sargunv.maplibrecompose.expressions.value.EnumValue +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.TextUnitOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextVariableAnchorOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.VectorValue +import kotlin.jvm.JvmName +import kotlin.time.Duration + +/** Creates a literal expression for a [String] value. */ +public fun const(string: String): StringLiteral = StringLiteral.of(string) + +/** Creates a literal expression for an enum value implementing [EnumValue]. */ +public fun > const(value: T): EnumLiteral = EnumLiteral.of(value) + +/** Creates a literal expression for a dimensionless [Float] value. */ +public fun const(float: Float): FloatLiteral = FloatLiteral.of(float) + +/** Creates a literal expression for an dimensionless [Int] value. */ +public fun const(int: Int): IntLiteral = IntLiteral.of(int) + +/** Creates a literal expression for a [Dp] value. */ +public fun const(dp: Dp): DpLiteral = DpLiteral.of(dp) + +/** + * Creates a literal expression for a specified [TextUnit] value in SP or EM. It can be provided in + * either unit, and will resolve to one at runtime depending on the property it is used in. + */ +public fun const(textUnit: TextUnit): TextUnitCalculation = TextUnitCalculation.of(textUnit) + +/** + * Creates a literal expression for a [Duration] value. + * + * The duration will be rounded to the nearest whole milliseconds. + */ +public fun const(duration: Duration): MillisecondsLiteral = MillisecondsLiteral.of(duration) + +/** Creates a literal expression for a [Boolean] value. */ +public fun const(bool: Boolean): BooleanLiteral = BooleanLiteral.of(bool) + +/** Creates a literal expression for a [Color] value. */ +public fun const(color: Color): ColorLiteral = ColorLiteral.of(color) + +/** Creates a literal expression for an [Offset] value. */ +public fun const(offset: Offset): OffsetLiteral = OffsetLiteral.of(offset) + +/** Creates a literal expression for a [DpOffset] value. */ +public fun const(dpOffset: DpOffset): DpOffsetLiteral = DpOffsetLiteral.of(dpOffset) + +/** Creates a literal expression for a [PaddingValues.Absolute] value. */ +public fun const(padding: PaddingValues.Absolute): DpPaddingLiteral = DpPaddingLiteral.of(padding) + +/** Creates a literal expression for a list. */ +public fun const(list: List>): ListLiteral = + ListLiteral.of(list) + +/** Creates a literal expression for a list of strings. */ +@JvmName("constStringList") +public fun const(list: List): ListLiteral = const(list.map { const(it) }) + +/** Creates a literal expression for a list of numbers. */ +@JvmName("constNumberList") +public fun const(list: List): Literal, *> = + const(list.map { const(it.toFloat()) }).cast() + +/** + * Creates a literal expression for [TextVariableAnchorOffsetValue], used by + * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]'s `textVariableAnchorOffset` + * parameter. + * + * The offset is measured in a multipler of the text size (EM). It's in [Offset] instead of [offset] + * because of technical limitations in MapLibre. + */ +public fun textVariableAnchorOffset( + vararg pairs: Pair +): Literal> { + val elements = buildList { + pairs.forEach { (anchor, offset) -> + add(anchor.literal) + add(const(offset)) + } + } + return const(elements).cast() +} + +/** Creates a literal expression for a 2D [Offset]. */ +public fun offset(x: Float, y: Float): OffsetLiteral = OffsetLiteral.of(Offset(x, y)) + +/** Creates a literal expression for a 2D [DpOffset]. */ +public fun offset(x: Dp, y: Dp): DpOffsetLiteral = DpOffsetLiteral.of(DpOffset(x, y)) + +/** + * Creates a literal expression for a 2D [TextUnit] offset. + * + * Both [x] and [y] must have the same [TextUnitType]. + */ +public fun offset(x: TextUnit, y: TextUnit): Expression = + TextUnitOffsetCalculation.of(x, y) + +/** + * Creates a literal expression for a `null` value. + * + * For simplicity, the expression type system does not encode nullability, so the return value of + * this function is assignable to any kind of expression. + */ +public fun nil(): Expression = NullLiteral.cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/math.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/math.kt new file mode 100644 index 00000000..0cd57ba7 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/math.kt @@ -0,0 +1,147 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.GeoJsonValue +import dev.sargunv.maplibrecompose.expressions.value.IntValue +import dev.sargunv.maplibrecompose.expressions.value.NumberValue +import kotlin.jvm.JvmName + +/** Returns mathematical constant ln(2) = natural logarithm of 2. */ +public fun ln2(): Expression = FunctionCall.of("ln2").cast() + +/** Returns the mathematical constant π */ +public fun pi(): Expression = FunctionCall.of("pi").cast() + +/** Returns the mathematical constant e */ +public fun e(): Expression = FunctionCall.of("e").cast() + +/** Returns the sum of this number expression with [other]. */ +public operator fun > Expression.plus( + other: Expression +): Expression = FunctionCall.of("+", this, other).cast() + +/** Returns the product of this number expression with [other]. */ +@JvmName("timesUnitLeft") +public operator fun > Expression.times( + other: Expression +): Expression = FunctionCall.of("*", this, other).cast() + +/** Returns the product of this number expression with [other]. */ +@JvmName("timesUnitRight") +public operator fun > Expression.times( + other: Expression +): Expression = FunctionCall.of("*", this, other).cast() + +/** Returns the product of this number expression with [other]. */ +public operator fun Expression.times( + other: Expression +): Expression = FunctionCall.of("*", this, other).cast() + +/** Returns the result of subtracting [other] from this number expression. */ +public operator fun > Expression.minus( + other: Expression> +): Expression = FunctionCall.of("-", this, other).cast() + +/** Negates this number expression. */ +public operator fun > Expression.unaryMinus(): Expression = + FunctionCall.of("-", this).cast() + +/** Returns the result of floating point division of this number expression by [divisor]. */ +@JvmName("divUnitBoth") +public operator fun > Expression.div( + divisor: Expression +): Expression = FunctionCall.of("/", this, divisor).cast() + +/** Returns the result of floating point division of this number expression by [divisor]. */ +@JvmName("divUnitLeftOnly") +public operator fun > Expression.div( + divisor: Expression +): Expression = FunctionCall.of("/", this, divisor).cast() + +/** Returns the result of floating point division of this number expression by [divisor]. */ +public operator fun Expression.div( + divisor: Expression +): Expression = FunctionCall.of("/", this, divisor).cast() + +/** Returns the remainder after integer division of this number expression by [divisor]. */ +public operator fun > Expression.rem( + divisor: Expression +): Expression = FunctionCall.of("%", this, divisor).cast() + +/** Returns the result of raising this number expression to the power of [exponent]. */ +public fun Expression.pow(exponent: Expression): Expression = + FunctionCall.of("^", this, exponent).cast() + +/** Returns the square root of [value]. */ +public fun sqrt(value: Expression): Expression = + FunctionCall.of("sqrt", value).cast() + +/** Returns the base-ten logarithm of [value]. */ +public fun log10(value: Expression): Expression = + FunctionCall.of("log10", value).cast() + +/** Returns the natural logarithm of [value]. */ +public fun ln(value: Expression): Expression = + FunctionCall.of("ln", value).cast() + +/** Returns the base-two logarithm of [value]. */ +public fun log2(value: Expression): Expression = + FunctionCall.of("log2", value).cast() + +/** Returns the sine of [value]. */ +public fun sin(value: Expression): Expression = + FunctionCall.of("sin", value).cast() + +/** Returns the cosine of [value]. */ +public fun cos(value: Expression): Expression = + FunctionCall.of("cos", value).cast() + +/** Returns the tangent of [value]. */ +public fun tan(value: Expression): Expression = + FunctionCall.of("tan", value).cast() + +/** Returns the arcsine of [value]. */ +public fun asin(value: Expression): Expression = + FunctionCall.of("asin", value).cast() + +/** Returns the arccosine of [value]. */ +public fun acos(value: Expression): Expression = + FunctionCall.of("acos", value).cast() + +/** Returns the arctangent of [value]. */ +public fun atan(value: Expression): Expression = + FunctionCall.of("atan", value).cast() + +/** Returns the smallest of all given [numbers]. */ +public fun > min(vararg numbers: Expression): Expression = + FunctionCall.of("min", *numbers).cast() + +/** Returns the greatest of all given [numbers]. */ +public fun > max(vararg numbers: Expression): Expression = + FunctionCall.of("max", *numbers).cast() + +/** Returns the absolute value of [value], i.e. always a positive value. */ +public fun > abs(value: Expression): Expression = + FunctionCall.of("abs", value).cast() + +/** + * Rounds [value] to the nearest integer. Halfway values are rounded away from zero. + * + * For example `round(const(-1.5))` evaluates to `-2`. + */ +public fun round(value: Expression): Expression = + FunctionCall.of("round", value).cast() + +/** Returns the smallest integer that is greater than or equal to [value]. */ +public fun ceil(value: Expression): Expression = + FunctionCall.of("ceil", value).cast() + +/** Returns the largest integer that is less than or equal to [value]. */ +public fun floor(value: Expression): Expression = + FunctionCall.of("floor", value).cast() + +/** Returns the shortest distance in meters between the evaluated feature and [geometry]. */ +public fun distance(geometry: Expression): Expression = + FunctionCall.of("distance", geometry).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/ramps.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/ramps.kt new file mode 100644 index 00000000..f823d13f --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/ramps.kt @@ -0,0 +1,148 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.InterpolateableValue +import dev.sargunv.maplibrecompose.expressions.value.InterpolationValue + +/** + * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs + * of input and output values ([stops]). Returns the output value of the stop just less than the + * [input], or the [fallback] if the input is less than the first stop. + * + * Example: + * ``` + * step(zoom(), const(0), 10 to const(2.5), 20 to const(10.5)) + * ``` + * + * returns 0 if the zoom is less than 10, 2.5 if the zoom is between 10 and less than 20, 10.5 if + * the zoom is greater than or equal 20. + */ +public fun step( + input: Expression, + fallback: Expression, + vararg stops: Pair>, +): Expression = + FunctionCall.of( + "step", + input, + fallback, + *stops + .sortedBy { it.first.toFloat() } + .foldToArgs { + add(const(it.first.toFloat())) + add(it.second) + }, + ) + .cast() + +private fun > interpolateImpl( + name: String, + type: Expression, + input: Expression, + vararg stops: Pair>, +): Expression = + FunctionCall.of( + name, + type, + input, + *stops + .sortedBy { it.first.toDouble() } + .foldToArgs { + add(const(it.first.toFloat())) + add(it.second) + }, + ) + .cast() + +/** + * Produces continuous, smooth results by interpolating between pairs of input and output values + * ([stops]), given the [input] value. + * + * Requires the [type] of interpolation to use. Use [linear], [exponential], or [cubicBezier]. + * + * Example: + * ``` + * interpolate( + * exponential(2), zoom(), + * 16 to const(1), + * 24 to const(256), + * ) + * ``` + * + * interpolates exponentially from 1 to 256 in zoom levels 16 to 24. Below zoom 16, it is 1, above + * zoom 24, it is 256. Applied to for example line width, this has the visual effect that the line + * stays the same width in meters on the map (rather than on the viewport). + */ +public fun > interpolate( + type: Expression, + input: Expression, + vararg stops: Pair>, +): Expression = interpolateImpl("interpolate", type, input, *stops) + +/** + * Produces continuous, smooth results by interpolating between pairs of input and output values + * ([stops]), given the [input] value. Works like [interpolate], but the interpolation is performed + * in the [Hue-Chroma-Luminance color space](https://en.wikipedia.org/wiki/HCL_color_space). + * + * Requires the [type] of interpolation to use. Use [linear], [exponential], or [cubicBezier]. + * + * Example: + * ``` + * interpolateHcl( + * linear(), + * zoom(), + * 1 to const(Color.Red), + * 5 to const(Color.Blue), + * 10 to const(Color.Green) + * ) + * ``` + * + * interpolates linearly from red to blue between in zoom levels 1 to 5, then interpolates linearly + * from blue to green in zoom levels 5 to 10, which it where it remains until maximum zoom. + */ +public fun interpolateHcl( + type: Expression, + input: Expression, + vararg stops: Pair>, +): Expression = interpolateImpl("interpolate-hcl", type, input, *stops) + +/** + * Produces continuous, smooth results by interpolating between pairs of input and output values + * ([stops]), given the [input] value. Works like [interpolate], but the interpolation is performed + * in the [CIELAB color space](https://en.wikipedia.org/wiki/CIELAB_color_space). + * + * Requires the [type] of interpolation to use. Use [linear], [exponential], or [cubicBezier]. + */ +public fun interpolateLab( + type: Expression, + input: Expression, + vararg stops: Pair>, +): Expression = interpolateImpl("interpolate-lab", type, input, *stops) + +/** Interpolates linearly between the pairs of stops. */ +public fun linear(): Expression = FunctionCall.of("linear").cast() + +/** + * Interpolates exponentially between the stops. + * + * @param [base] controls the rate at which the output increases: higher values make the output + * increase more towards the high end of the range. With values close to 1 the output increases + * linearly. + */ +public fun exponential(base: Expression): Expression = + FunctionCall.of("exponential", base).cast() + +/** + * Interpolates using the cubic bezier curve defined by the given control points between the pairs + * of stops. + */ +public fun cubicBezier( + x1: Expression, + y1: Expression, + x2: Expression, + y2: Expression, +): Expression = FunctionCall.of("cubic-bezier", x1, y1, x2, y2).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/string.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/string.kt new file mode 100644 index 00000000..ef6949e9 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/string.kt @@ -0,0 +1,95 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CollatorValue +import dev.sargunv.maplibrecompose.expressions.value.IntValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import kotlin.jvm.JvmName + +/** Returns whether this string contains the [substring]. */ +@JvmName("containsString") +public fun Expression.contains( + substring: Expression +): Expression = FunctionCall.of("in", substring, this).cast() + +/** + * Returns the first index at which the [substring] is located in this string, or `-1` if it cannot + * be found. Accepts an optional [startIndex] from where to begin the search. + */ +@JvmName("indexOfString") +public fun Expression.indexOf( + substring: Expression, + startIndex: Expression? = null, +): Expression { + val args = buildList { + add(substring) + add(this@indexOf) + startIndex?.let { add(it) } + } + return FunctionCall.of("index-of", *args.toTypedArray>()).cast() +} + +/** + * Returns a substring from this string from the [startIndex] (inclusive) to the end of the string + * if [endIndex] is not specified or `null`, otherwise to [endIndex] (exclusive). + * + * A UTF-16 surrogate pair counts as a single position. + */ +public fun Expression.substring( + startIndex: Expression, + endIndex: Expression? = null, +): Expression { + val args = buildList { + add(this@substring) + add(startIndex) + endIndex?.let { add(it) } + } + return FunctionCall.of("slice", *args.toTypedArray>()).cast() +} + +/** + * Gets the length of this string. + * + * A UTF-16 surrogate pair counts as a single position. + */ +@JvmName("lengthOfString") +public fun Expression.length(): Expression = + FunctionCall.of("length", this).cast() + +/** + * Returns `true` if this string is expected to render legibly. Returns `false` if this string + * contains sections that cannot be rendered without potential loss of meaning (e.g. Indic scripts + * that require complex text shaping). + */ +public fun Expression.isScriptSupported(): Expression = + FunctionCall.of("is-supported-script", this).cast() + +/** + * Returns this string converted to uppercase. Follows the Unicode Default Case Conversion algorithm + * and the locale-insensitive case mappings in the Unicode Character Database. + */ +public fun Expression.uppercase(): Expression = + FunctionCall.of("upcase", this).cast() + +/** + * Returns this string converted to lowercase. Follows the Unicode Default Case Conversion algorithm + * and the locale-insensitive case mappings in the Unicode Character Database. + */ +public fun Expression.lowercase(): Expression = + FunctionCall.of("downcase", this).cast() + +/** Concatenates this string expression with [other]. */ +@JvmName("concat") +public operator fun Expression.plus( + other: Expression +): Expression = FunctionCall.of("concat", this, other).cast() + +/** + * Returns the IETF language tag of the locale being used by the provided [collator]. This can be + * used to determine the default system locale, or to determine if a requested locale was + * successfully loaded. + */ +public fun resolvedLocale(collator: Expression): Expression = + FunctionCall.of("resolved-locale", collator).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/types.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/types.kt new file mode 100644 index 00000000..9637bfae --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/types.kt @@ -0,0 +1,253 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import androidx.compose.ui.unit.TextUnitType +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.ast.Options +import dev.sargunv.maplibrecompose.expressions.ast.TextUnitCalculation +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CollatorValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.EnumValue +import dev.sargunv.maplibrecompose.expressions.value.ExpressionType +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IntValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.MapValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.NumberValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.TextUnitValue +import kotlin.enums.enumEntries + +/** Returns a string describing the type of this expression. */ +public fun Expression<*>.type(): Expression = FunctionCall.of("typeof", this).cast() + +/** + * Asserts that this is a list (optionally with a specific item [type] and [length]). + * + * If, when the input expression is evaluated, it is not of the asserted type, then this assertion + * will cause the whole expression to be aborted. + */ +public fun Expression<*>.asList( + type: Expression = nil(), + length: Expression = nil(), +): Expression> = FunctionCall.of("array", this, type, length).cast() + +/** + * Asserts that this is a list of numbers, optionally with a specific [length]. + * + * If, when the input expression is evaluated, it is not of the asserted type, then this assertion + * will cause the whole expression to be aborted. + */ +public fun > Expression<*>.asVector( + length: Expression = nil() +): Expression = asList(const(ExpressionType.Number), length).cast() + +/** + * Asserts that this is a list of numbers of length 2. + * + * If, when the input expression is evaluated, it is not of the asserted type, then this assertion + * will cause the whole expression to be aborted. + */ +public fun Expression<*>.asOffset(): Expression = + asList(const(ExpressionType.Number), const(2)).cast() + +/** + * Asserts that this is a list of numbers of length 2. + * + * If, when the input expression is evaluated, it is not of the asserted type, then this assertion + * will cause the whole expression to be aborted. + */ +public fun Expression<*>.asDpOffset(): Expression = + asList(const(ExpressionType.Number), const(2)).cast() + +/** + * Asserts that this is a list of numbers of length 4. + * + * If, when the input expression is evaluated, it is not of the asserted type, then this assertion + * will cause the whole expression to be aborted. + */ +public fun Expression<*>.asPadding(): Expression = + asList(const(ExpressionType.Number), const(2)).cast() + +/** + * Asserts that this value is a string. + * + * In case this expression is not a string, each of the [fallbacks] is evaluated in order until a + * string is obtained. If none of the inputs are strings, the expression is an error. + */ +public fun Expression<*>.asString(vararg fallbacks: Expression<*>): Expression = + FunctionCall.of("string", this, *fallbacks).cast() + +/** + * Asserts that this value is an entry of the enum specified by [T]. + * + * In case this expression is not an entry of the enum, each of the [fallbacks] is evaluated in + * order until a match is obtained. If none of the inputs match, the expression is an error. + */ +public inline fun Expression<*>.asEnum( + vararg fallbacks: Expression<*> +): Expression where T : Enum, T : EnumValue { + val entries = const(enumEntries().map { it.name }) + return switch( + condition(entries.contains(this), this), + *fallbacks.map { condition(entries.contains(it), it) }.toTypedArray(), + fallback = nil(), // should always error .asString(), which is what we want as per kdoc + ) + .asString() + .cast() +} + +/** + * Asserts that this value is a number. + * + * In case this expression is not a number, each of the [fallbacks] is evaluated in order until a + * number is obtained. If none of the inputs are numbers, the expression is an error. + */ +public fun Expression<*>.asNumber(vararg fallbacks: Expression<*>): Expression = + FunctionCall.of("number", this, *fallbacks).cast() + +/** + * Asserts that this value is a boolean. + * + * In case this expression is not a boolean, each of the [fallbacks] is evaluated in order until a + * boolean is obtained. If none of the inputs are booleans, the expression is an error. + */ +public fun Expression<*>.asBoolean(vararg fallbacks: Expression<*>): Expression = + FunctionCall.of("boolean", this, *fallbacks).cast() + +/** + * Asserts that this value is a map. + * + * In case this expression is not a map, each of the [fallbacks] is evaluated in order until a map + * is obtained. If none of the inputs are maps, the expression is an error. + */ +public fun Expression<*>.asMap(vararg fallbacks: Expression<*>): Expression> = + FunctionCall.of("object", this, *fallbacks).cast() + +/** + * Returns a collator for use in locale-dependent comparison operations. The [caseSensitive] and + * [diacriticSensitive] options default to `false`. The [locale] argument specifies the IETF + * language tag of the locale to use. If none is provided, the default locale is used. If the + * requested locale is not available, the collator will use a system-defined fallback locale. Use + * [resolvedLocale] to test the results of locale fallback behavior. + */ +public fun collator( + caseSensitive: Expression? = null, + diacriticSensitive: Expression? = null, + locale: Expression? = null, +): Expression = + FunctionCall.of( + "collator", + Options.build( + fun MutableMap>.() { + caseSensitive?.let { put("case-sensitive", it) } + diacriticSensitive?.let { put("diacritic-sensitive", it) } + locale?.let { put("locale", it) } + } + ), + ) + .cast() + +/** + * Converts this number into a string representation using the provided formatting rules. + * + * @param locale BCP 47 language tag for which locale to use + * @param currency an ISO 4217 code to use for currency-style formatting + * @param minFractionDigits minimum fractional digits to include + * @param maxFractionDigits maximum fractional digits to include + */ +public fun Expression>.formatToString( + locale: Expression? = null, + currency: Expression? = null, + minFractionDigits: Expression? = null, + maxFractionDigits: Expression? = null, +): Expression = + FunctionCall.of( + "number-format", + this, + Options.build( + fun MutableMap>.() { + locale?.let { put("locale", it) } + currency?.let { put("currency", it) } + minFractionDigits?.let { put("min-fraction-digits", it) } + maxFractionDigits?.let { put("max-fraction-digits", it) } + } + ), + ) + .cast() + +/** + * Converts this expression to a string. + * + * If this is ... + * - `null`, the result is `""` + * - a boolean, the result is `"true"` or `"false"` + * - a number, it is converted to a string as specified by the "NumberToString" algorithm of the + * ECMAScript Language Specification. + * - a color, it is converted to a string of the form `"rgba(r,g,b,a)"`, where `r`, `g`, and `b` are + * numerals ranging from 0 to 255, and `a` ranges from 0 to 1. + * + * Otherwise, the input is converted to a string in the format specified by the JSON.stringify + * function of the ECMAScript Language Specification. + */ +public fun Expression<*>.convertToString(): Expression = + FunctionCall.of("to-string", this).cast() + +/** + * Converts this expression to a number. + * + * If this expression is `null` or `false`, the result is `0`. If this is `true`, the result is `1`. + * If the input is a string, it is converted to a number as specified by the "ToNumber Applied to + * the String Type" algorithm of the ECMAScript Language Specification. + * + * In case this expression cannot be converted to a number, each of the [fallbacks] is evaluated in + * order until the first successful conversion is obtained. If none of the inputs can be converted, + * the expression is an error. + */ +public fun Expression<*>.convertToNumber(vararg fallbacks: Expression<*>): Expression = + FunctionCall.of("to-number", this, *fallbacks).cast() + +/** + * Converts this expression to a boolean expression. + * + * The result is `false` when then this is an empty string, `0`, `false`,`null` or `NaN`; otherwise + * it is `true`. + */ +public fun Expression<*>.convertToBoolean(): Expression = + FunctionCall.of("to-boolean", this).cast() + +/** + * Converts this expression to a color expression. + * + * In case this expression cannot be converted to a color, each of the [fallbacks] is evaluated in + * order until the first successful conversion is obtained. If none of the inputs can be converted, + * the expression is an error. + */ +public fun Expression<*>.convertToColor(vararg fallbacks: Expression<*>): Expression = + FunctionCall.of("to-color", this, *fallbacks).cast() + +/** Converts a numeric [Expression] to a [DpValue] expression. */ +public val Expression.dp: Expression + get() = this.cast() + +/** Converts a numeric [Expression] in milliseconds to a [MillisecondsValue] expression. */ +public val Expression.milliseconds: Expression + get() = this.cast() + +/** Converts a numeric [Expression] in seconds to a [MillisecondsValue] expression. */ +public val Expression.seconds: Expression + get() = (this * const(1000f)).cast() + +/** Converts a numeric [Expression] to an [TextUnitValue] expression in SP. */ +public val Expression.sp: Expression + get() = TextUnitCalculation.of(this, TextUnitType.Sp) + +/** Converts a numeric [Expression] to an [TextUnitValue] expression in EM */ +public val Expression.em: Expression + get() = TextUnitCalculation.of(this, TextUnitType.Em) diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/util.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/util.kt new file mode 100644 index 00000000..cb052f9c --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/util.kt @@ -0,0 +1,13 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression + +internal inline fun Array.foldToArgs( + block: MutableList>.(element: T) -> Unit +) = + fold(mutableListOf>()) { acc, element -> acc.apply { block(element) } } + .toTypedArray() + +internal inline fun List.foldToArgs(block: MutableList>.(element: T) -> Unit) = + fold(mutableListOf>()) { acc, element -> acc.apply { block(element) } } + .toTypedArray() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/variables.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/variables.kt new file mode 100644 index 00000000..19c731a1 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/dsl/variables.kt @@ -0,0 +1,42 @@ +package dev.sargunv.maplibrecompose.expressions.dsl + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.FunctionCall +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import kotlin.jvm.JvmInline + +/** + * Binds expression [value] to a [Variable] with the given [name], which can then be referenced + * inside the block using [use]. For example: + * ```kt + * val result = withVariable("x", const(5)) { x -> + * x.use() + const(3) + * } + * ``` + * + * Variable names starting with `__` are reserved for internal use by the library. + */ +public inline fun withVariable( + name: String, + value: Expression, + block: (Variable) -> Expression, +): Expression { + require(!name.startsWith("__")) { "Variable names starting with '__' are reserved." } + return Variable(name).let { it.bind(value, block(it)) } +} + +/** References a [Variable] bound in [withVariable]. */ +public fun Variable.use(): Expression = + FunctionCall.of("var", const(name)).cast() + +/** Represents a variable bound with [withVariable]. Reference the bound expression with [use]. */ +@JvmInline +public value class Variable<@Suppress("unused") T : ExpressionValue> +@PublishedApi +internal constructor(public val name: String) + +@PublishedApi +internal fun Variable.bind( + value: Expression, + expression: Expression, +): Expression = FunctionCall.of("let", const(name), value, expression).cast() diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/CirclePitchAlignment.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/CirclePitchAlignment.kt new file mode 100644 index 00000000..73b60f8b --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/CirclePitchAlignment.kt @@ -0,0 +1,13 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Orientation of circles when the map is pitched. */ +public enum class CirclePitchAlignment(override val literal: StringLiteral) : + EnumValue { + /** Circles are aligned to the plane of the map, i.e. flat on top of the map. */ + Map(StringLiteral.of("map")), + + /** Circles are aligned to the plane of the viewport, i.e. facing the camera. */ + Viewport(StringLiteral.of("viewport")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/CirclePitchScale.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/CirclePitchScale.kt new file mode 100644 index 00000000..5370f171 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/CirclePitchScale.kt @@ -0,0 +1,16 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Scaling behavior of circles when the map is pitched. */ +public enum class CirclePitchScale(override val literal: StringLiteral) : + EnumValue { + /** + * Circles are scaled according to their apparent distance to the camera, i.e. as if they are on + * the map. + */ + Map(StringLiteral.of("map")), + + /** Circles are not scaled, i.e. as if glued to the viewport. */ + Viewport(StringLiteral.of("viewport")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/ExpressionType.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/ExpressionType.kt new file mode 100644 index 00000000..5a5d0ca5 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/ExpressionType.kt @@ -0,0 +1,14 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral +import dev.sargunv.maplibrecompose.expressions.dsl.type + +/** The type of value resolved from an expression, as returned by [type]. */ +public enum class ExpressionType(override val literal: StringLiteral) : EnumValue { + Number(StringLiteral.of("number")), + String(StringLiteral.of("string")), + Object(StringLiteral.of("object")), + Boolean(StringLiteral.of("boolean")), + Color(StringLiteral.of("color")), + Array(StringLiteral.of("array")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/GeometryType.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/GeometryType.kt new file mode 100644 index 00000000..5a537480 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/GeometryType.kt @@ -0,0 +1,13 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Type of a GeoJson feature, as returned by [Feature.type]. */ +public enum class GeometryType(override val literal: StringLiteral) : EnumValue { + Point(StringLiteral.of("Point")), + LineString(StringLiteral.of("LineString")), + Polygon(StringLiteral.of("Polygon")), + MultiPoint(StringLiteral.of("MultiPoint")), + MultiLineString(StringLiteral.of("MultiLineString")), + MultiPolygon(StringLiteral.of("MultiPolygon")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconPitchAlignment.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconPitchAlignment.kt new file mode 100644 index 00000000..6120c0f0 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconPitchAlignment.kt @@ -0,0 +1,16 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Orientation of icon when map is pitched. */ +public enum class IconPitchAlignment(override val literal: StringLiteral) : + EnumValue { + /** The icon is aligned to the plane of the map. */ + Map(StringLiteral.of("map")), + + /** The icon is aligned to the plane of the viewport, i.e. as if glued to the screen */ + Viewport(StringLiteral.of("viewport")), + + /** Automatically matches the value of [IconRotationAlignment] */ + Auto(StringLiteral.of("auto")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconRotationAlignment.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconRotationAlignment.kt new file mode 100644 index 00000000..4b1d9b85 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconRotationAlignment.kt @@ -0,0 +1,25 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** In combination with [SymbolPlacement], determines the rotation behavior of icons. */ +public enum class IconRotationAlignment(override val literal: StringLiteral) : + EnumValue { + /** + * For [SymbolPlacement.Point], aligns icons east-west. Otherwise, aligns icon x-axes with the + * line. + */ + Map(StringLiteral.of("map")), + + /** + * Produces icons whose x-axes are aligned with the x-axis of the viewport, regardless of the + * [SymbolPlacement]. + */ + Viewport(StringLiteral.of("viewport")), + + /** + * For [SymbolPlacement.Point], this is equivalent to [IconRotationAlignment.Viewport]. Otherwise, + * this is equivalent to [IconRotationAlignment.Map]. + */ + Auto(StringLiteral.of("auto")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconTextFit.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconTextFit.kt new file mode 100644 index 00000000..45ea91f0 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IconTextFit.kt @@ -0,0 +1,18 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Scales the icon to fit around the associated text. */ +public enum class IconTextFit(override val literal: StringLiteral) : EnumValue { + /** The icon is displayed at its intrinsic aspect ratio. */ + None(StringLiteral.of("none")), + + /** The icon is scaled in the x-dimension to fit the width of the text. */ + Width(StringLiteral.of("width")), + + /** The icon is scaled in the y-dimension to fit the height of the text. */ + Height(StringLiteral.of("height")), + + /** The icon is scaled in both x- and y-dimensions. */ + Both(StringLiteral.of("both")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IlluminationAnchor.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IlluminationAnchor.kt new file mode 100644 index 00000000..6f4bc68e --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/IlluminationAnchor.kt @@ -0,0 +1,14 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Direction of light source when map is rotated. */ +public enum class IlluminationAnchor(override val literal: StringLiteral) : + EnumValue { + + /** The hillshade illumination is relative to the north direction. */ + Map(StringLiteral.of("map")), + + /** The hillshade illumination is relative to the top of the viewport. */ + Viewport(StringLiteral.of("viewport")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/LineCap.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/LineCap.kt new file mode 100644 index 00000000..717eba29 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/LineCap.kt @@ -0,0 +1,21 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Display of line endings */ +public enum class LineCap(override val literal: StringLiteral) : EnumValue { + /** A cap with a squared-off end which is drawn to the exact endpoint of the line. */ + Butt(StringLiteral.of("butt")), + + /** + * A cap with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half + * of the line's width and centered on the endpoint of the line. + */ + Round(StringLiteral.of("round")), + + /** + * A cap with a squared-off end which is drawn beyond the endpoint of the line at a distance of + * one-half of the line's width. + */ + Square(StringLiteral.of("square")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/LineJoin.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/LineJoin.kt new file mode 100644 index 00000000..300d1acd --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/LineJoin.kt @@ -0,0 +1,24 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Display of joined lines */ +public enum class LineJoin(override val literal: StringLiteral) : EnumValue { + /** + * A join with a squared-off end which is drawn beyond the endpoint of the line at a distance of + * one-half of the line's width. + */ + Bevel(StringLiteral.of("bevel")), + + /** + * A join with a rounded end which is drawn beyond the endpoint of the line at a radius of + * one-half of the line's width and centered on the endpoint of the line. + */ + Round(StringLiteral.of("round")), + + /** + * A join with a sharp, angled corner which is drawn with the outer sides beyond the endpoint of + * the path until they meet. + */ + Miter(StringLiteral.of("miter")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/RasterResampling.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/RasterResampling.kt new file mode 100644 index 00000000..a8bdd9f2 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/RasterResampling.kt @@ -0,0 +1,22 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** + * The resampling/interpolation method to use for overscaling, also known as texture magnification + * filter + */ +public enum class RasterResampling(override val literal: StringLiteral) : + EnumValue { + /** + * (Bi)linear filtering interpolates pixel values using the weighted average of the four closest + * original source pixels creating a smooth but blurry look when overscaled + */ + Linear(StringLiteral.of("linear")), + + /** + * Nearest neighbor filtering interpolates pixel values using the nearest original source pixel + * creating a sharp but pixelated look when overscaled + */ + Nearest(StringLiteral.of("nearest")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolAnchor.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolAnchor.kt new file mode 100644 index 00000000..42028bd6 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolAnchor.kt @@ -0,0 +1,33 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Part of the icon/text placed closest to the anchor. */ +public enum class SymbolAnchor(override val literal: StringLiteral) : EnumValue { + /** The center of the icon is placed closest to the anchor. */ + Center(StringLiteral.of("center")), + + /** The left side of the icon is placed closest to the anchor. */ + Left(StringLiteral.of("left")), + + /** The right side of the icon is placed closest to the anchor. */ + Right(StringLiteral.of("right")), + + /** The top of the icon is placed closest to the anchor. */ + Top(StringLiteral.of("top")), + + /** The bottom of the icon is placed closest to the anchor. */ + Bottom(StringLiteral.of("bottom")), + + /** The top left corner of the icon is placed closest to the anchor. */ + TopLeft(StringLiteral.of("top-left")), + + /** The top right corner of the icon is placed closest to the anchor. */ + TopRight(StringLiteral.of("top-right")), + + /** The bottom left corner of the icon is placed closest to the anchor. */ + BottomLeft(StringLiteral.of("bottom-left")), + + /** The bottom right corner of the icon is placed closest to the anchor. */ + BottomRight(StringLiteral.of("bottom-right")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolOverlap.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolOverlap.kt new file mode 100644 index 00000000..7ef56da3 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolOverlap.kt @@ -0,0 +1,20 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Controls whether to show an icon/text when it overlaps other symbols on the map. */ +public enum class SymbolOverlap(override val literal: StringLiteral) : EnumValue { + /** The icon/text will be hidden if it collides with any other previously drawn symbol. */ + Never(StringLiteral.of("never")), + + /** The icon/text will be visible even if it collides with any other previously drawn symbol. */ + Always(StringLiteral.of("always")), + + /** + * If the icon/text collides with another previously drawn symbol, the overlap mode for that + * symbol is checked. If the previous symbol was placed using never overlap mode, the new + * icon/text is hidden. If the previous symbol was placed using always or cooperative overlap + * mode, the new icon/text is visible. + */ + Cooperative(StringLiteral.of("cooperative")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolPlacement.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolPlacement.kt new file mode 100644 index 00000000..cf4b9502 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolPlacement.kt @@ -0,0 +1,23 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Symbol placement relative to its geometry. */ +public enum class SymbolPlacement(override val literal: StringLiteral) : + EnumValue { + /** The label is placed at the point where the geometry is located. */ + Point(StringLiteral.of("point")), + + /** + * The label is placed along the line of the geometry. Can only be used on LineString and Polygon + * geometries. + */ + Line(StringLiteral.of("line")), + + /** + * The label is placed at the center of the line of the geometry. Can only be used on LineString + * and Polygon geometries. Note that a single feature in a vector tile may contain multiple line + * geometries. + */ + LineCenter(StringLiteral.of("line-center")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolZOrder.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolZOrder.kt new file mode 100644 index 00000000..28f013fa --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/SymbolZOrder.kt @@ -0,0 +1,30 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** + * Determines whether overlapping symbols in the same layer are rendered in the order that they + * appear in the data source or by their y-position relative to the viewport. To control the order + * and prioritization of symbols otherwise, use `sortKey`. + */ +public enum class SymbolZOrder(override val literal: StringLiteral) : EnumValue { + /** + * Sorts symbols by `sortKey` if set. Otherwise, sorts symbols by their y-position relative to the + * viewport if `iconAllowOverlap` or `textAllowOverlap` is set to `true` or `iconIgnorePlacement` + * or `textIgnorePlacement` is `false`. + */ + Auto(StringLiteral.of("auto")), + + /** + * Sorts symbols by their y-position relative to the viewport if `iconAllowOverlap` or + * `textAllowOverlap` is set to `true` or `iconIgnorePlacement` or `textIgnorePlacement` is + * `false`. + */ + ViewportY(StringLiteral.of("viewport-y")), + + /** + * Sorts symbols by `sortKey` if set. Otherwise, no sorting is applied; symbols are rendered in + * the same order as the source data. + */ + Source(StringLiteral.of("source")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextJustify.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextJustify.kt new file mode 100644 index 00000000..1614e5b5 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextJustify.kt @@ -0,0 +1,18 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Text justification options. */ +public enum class TextJustify(override val literal: StringLiteral) : EnumValue { + /** The text is aligned towards the anchor position. */ + Auto(StringLiteral.of("auto")), + + /** The text is aligned to the left. */ + Left(StringLiteral.of("left")), + + /** The text is centered. */ + Center(StringLiteral.of("center")), + + /** The text is aligned to the right. */ + Right(StringLiteral.of("right")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextPitchAlignment.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextPitchAlignment.kt new file mode 100644 index 00000000..49ca8ac6 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextPitchAlignment.kt @@ -0,0 +1,16 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Orientation of text when map is pitched. */ +public enum class TextPitchAlignment(override val literal: StringLiteral) : + EnumValue { + /** The text is aligned to the plane of the map. */ + Map(StringLiteral.of("map")), + + /** The text is aligned to the plane of the viewport, i.e. as if glued to the screen */ + Viewport(StringLiteral.of("viewport")), + + /** Automatically matches the value of [TextRotationAlignment] */ + Auto(StringLiteral.of("auto")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextRotationAlignment.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextRotationAlignment.kt new file mode 100644 index 00000000..d4a8ca97 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextRotationAlignment.kt @@ -0,0 +1,37 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** + * In combination with [SymbolPlacement], determines the rotation behavior of the individual glyphs + * forming the text. + */ +public enum class TextRotationAlignment(override val literal: StringLiteral) : + EnumValue { + /** + * For [SymbolPlacement.Point], aligns text east-west. Otherwise, aligns text x-axes with the + * line. + */ + Map(StringLiteral.of("map")), + + /** + * Produces glyphs whose x-axes are aligned with the x-axis of the viewport, regardless of the + * [SymbolPlacement]. + */ + Viewport(StringLiteral.of("viewport")), + + /** + * For [SymbolPlacement.Point], this is equivalent to [TextRotationAlignment.Viewport]. Otherwise, + * aligns glyphs to the x-axis of the viewport and places them along the line. + * + * **Note**: This value not supported on native platforms yet, see + * [maplibre-native#250](https://github.com/maplibre/maplibre-native/issues/250)** + */ + ViewportGlyph(StringLiteral.of("viewport-glyph")), + + /** + * For [SymbolPlacement.Point], this is equivalent to [TextRotationAlignment.Viewport]. Otherwise, + * this is equivalent to [TextRotationAlignment.Map]. + */ + Auto(StringLiteral.of("auto")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextTransform.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextTransform.kt new file mode 100644 index 00000000..f128e953 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextTransform.kt @@ -0,0 +1,15 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Specifies how to capitalize text, similar to the CSS text-transform property. */ +public enum class TextTransform(override val literal: StringLiteral) : EnumValue { + /** The text is not altered. */ + None(StringLiteral.of("none")), + + /** Forces all letters to be displayed in uppercase. */ + Uppercase(StringLiteral.of("uppercase")), + + /** Forces all letters to be displayed in lowercase. */ + Lowercase(StringLiteral.of("lowercase")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextWritingMode.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextWritingMode.kt new file mode 100644 index 00000000..6094acb3 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TextWritingMode.kt @@ -0,0 +1,19 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** How the text will be laid out. */ +public enum class TextWritingMode(override val literal: StringLiteral) : + EnumValue { + /** + * If a text's language supports horizontal writing mode, symbols with point placement would be + * laid out horizontally. + */ + Horizontal(StringLiteral.of("horizontal")), + + /** + * If a text's language supports vertical writing mode, symbols with point placement would be laid + * out vertically. + */ + Vertical(StringLiteral.of("vertical")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TranslateAnchor.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TranslateAnchor.kt new file mode 100644 index 00000000..93fdd930 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/TranslateAnchor.kt @@ -0,0 +1,13 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral + +/** Frame of reference for offsetting geometry. */ +public enum class TranslateAnchor(override val literal: StringLiteral) : + EnumValue { + /** Offset is relative to the map */ + Map(StringLiteral.of("map")), + + /** Offset is relative to the viewport */ + Viewport(StringLiteral.of("viewport")), +} diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/unions.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/unions.kt new file mode 100644 index 00000000..7fc027a2 --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/unions.kt @@ -0,0 +1,40 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.eq +import dev.sargunv.maplibrecompose.expressions.dsl.format +import dev.sargunv.maplibrecompose.expressions.dsl.gt +import dev.sargunv.maplibrecompose.expressions.dsl.gte +import dev.sargunv.maplibrecompose.expressions.dsl.interpolate +import dev.sargunv.maplibrecompose.expressions.dsl.lt +import dev.sargunv.maplibrecompose.expressions.dsl.lte +import dev.sargunv.maplibrecompose.expressions.dsl.neq +import dev.sargunv.maplibrecompose.expressions.dsl.switch + +/** Represents and [Expression] that resolves to a value that can be an input to [format]. */ +public sealed interface FormattableValue : ExpressionValue + +/** + * Represents an [Expression] that resolves to a value that can be compared for equality. See [eq] + * and [neq]. + */ +public sealed interface EquatableValue : ExpressionValue + +/** Union type for an [Expression] that resolves to a value that can be matched. See [switch]. */ +public sealed interface MatchableValue : ExpressionValue + +/** + * Union type for an [Expression] that resolves to a value that can be ordered with other values of + * its type. See [gt], [lt], [gte], and [lte]. + * + * @param T the type of the value that can be compared against for ordering. + */ +public sealed interface ComparableValue : ExpressionValue + +/** + * Union type for an [Expression] that resolves to a value that can be interpolated. See + * [interpolate]. + * + * @param T the type of values that can be interpolated between. + */ +public sealed interface InterpolateableValue : ExpressionValue diff --git a/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/values.kt b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/values.kt new file mode 100644 index 00000000..655a3e6a --- /dev/null +++ b/lib/maplibre-compose-expressions/src/commonMain/kotlin/dev/sargunv/maplibrecompose/expressions/value/values.kt @@ -0,0 +1,182 @@ +package dev.sargunv.maplibrecompose.expressions.value + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.TextUnit +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral +import dev.sargunv.maplibrecompose.expressions.dsl.collator +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.cubicBezier +import dev.sargunv.maplibrecompose.expressions.dsl.exponential +import dev.sargunv.maplibrecompose.expressions.dsl.format +import dev.sargunv.maplibrecompose.expressions.dsl.image +import dev.sargunv.maplibrecompose.expressions.dsl.linear +import dev.sargunv.maplibrecompose.expressions.dsl.offset +import kotlin.time.Duration + +/** + * Represents a value that an [Expression] can resolve to. Many of these types are never actually + * instantiated at runtime; they're only used as type parameters to hint at the type of an + * [Expression]. + */ +public sealed interface ExpressionValue + +/** Represents an [ExpressionValue] that resolves to a true or false value. See [const]. */ +public sealed interface BooleanValue : ExpressionValue, EquatableValue + +/** + * Represents an [ExpressionValue] that resolves to a numeric quantity. Corresponds to numbers in + * the JSON style spec. Use [const] to create a literal [NumberValue]. + * + * @param U the unit type of the number. For dimensionless quantities, use [Number]. + */ +public sealed interface NumberValue : + ExpressionValue, + MatchableValue, + InterpolateableValue, + ComparableValue>, + EquatableValue + +/** Represents an [ExpressionValue] that resolves to a dimensionless quantity. See [const]. */ +public typealias FloatValue = NumberValue + +/** + * Represents an [ExpressionValue] that resolves to an integer dimensionless quantity. See [const]. + */ +public sealed interface IntValue : NumberValue + +/** + * Represents an [ExpressionValue] that resolves to device-independent pixels ([Dp]). See [const]. + */ +public typealias DpValue = NumberValue + +/** + * Represents an [ExpressionValue] that resolves to scalable pixels or em ([TextUnit]). See [const]. + * + * Which unit it resolves to is determined by the style property it's used in. + */ +public typealias TextUnitValue = NumberValue + +/** + * Represents an [ExpressionValue] that resolves to an amount of time with millisecond precision + * ([Duration]). See [const]. + */ +public typealias MillisecondsValue = NumberValue + +/** Represents an [ExpressionValue] that resolves to a string value. See [const]. */ +public sealed interface StringValue : + ExpressionValue, + MatchableValue, + ComparableValue, + EquatableValue, + FormattableValue, + FormattedValue + +/** + * Represents an [ExpressionValue] that resolves to an enum string. See [const]. + * + * @param T The [EnumValue] descendent type that this value represents. + */ +public sealed interface EnumValue : StringValue { + /** The string expression representing this enum value. */ + public val literal: StringLiteral +} + +/** Represents an [ExpressionValue] that resolves to a [Color] value. See [const]. */ +public sealed interface ColorValue : ExpressionValue, InterpolateableValue + +/** + * Represents an [ExpressionValue] that resolves to a map value (corresponds to a JSON object). See + * [const]. + */ +public sealed interface MapValue<@Suppress("unused") out T : ExpressionValue> : ExpressionValue + +/** + * Represents an [ExpressionValue] that resolves to a list value (corresponds to a JSON array). See + * [const]. + */ +public sealed interface ListValue : ExpressionValue + +/** + * Represents an [ExpressionValue] that resolves to a list value (corresponds to a JSON array) of + * alternating types. + */ +public sealed interface AlternatingListValue< + @Suppress("unused") + out T1 : ExpressionValue, + @Suppress("unused") + out T2 : ExpressionValue, +> : ListValue + +/** + * Represents an [ExpressionValue] that resolves to an alternating list of [SymbolAnchor] and + * [FloatOffsetValue]. + * + * See [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]. + */ +public typealias TextVariableAnchorOffsetValue = + AlternatingListValue + +/** + * Represents an [ExpressionValue] that resolves to a list of numbers. + * + * @param U the unit type of the number. For dimensionless quantities, use [Number]. + */ +public sealed interface VectorValue : + ListValue>, InterpolateableValue> + +/** + * Represents an [ExpressionValue] that reoslves to a 2D vector in some unit. + * + * @param U the unit type of the offset. For dimensionless quantities, use [Number]. + */ +public sealed interface OffsetValue : VectorValue + +/** + * Represents an [ExpressionValue] that resolves to a 2D floating point offset without a particular + * unit. ([Offset]). See [offset]. + */ +public typealias FloatOffsetValue = OffsetValue + +/** + * Represents an [ExpressionValue] that resolves to a 2D floating point offset in device-independent + * pixels ([DpOffset]). See [offset]. + */ +public typealias DpOffsetValue = OffsetValue + +/** + * Represents an [ExpressionValue] that resolves to a 2D floating point offset in scalable pixels or + * em ([TextUnit]). See [offset]. + */ +public typealias TextUnitOffsetValue = OffsetValue + +/** + * Represents an [ExpressionValue] that resolves to an absolute (layout direction unaware) padding + * applied along the edges inside a box ([PaddingValues.Absolute]). See [const]. + */ +public sealed interface DpPaddingValue : VectorValue + +/** + * Represents an [ExpressionValue] that resolves to a collator object for use in locale-dependent + * comparison operations. See [collator]. + */ +public sealed interface CollatorValue : ExpressionValue + +/** Represents an [ExpressionValue] that resolves to a formatted string. See [format]. */ +public sealed interface FormattedValue : ExpressionValue + +/** Represents an [ExpressionValue] that resolves to a geometry object. */ +public sealed interface GeoJsonValue : ExpressionValue + +/** Represents an [ExpressionValue] that resolves to an image. See [image] */ +public sealed interface ImageValue : ExpressionValue, FormattableValue + +/** + * Represents an [ExpressionValue] that resolves to an interpolation type. See [linear], + * [exponential], and [cubicBezier]. + */ +public sealed interface InterpolationValue : ExpressionValue diff --git a/lib/maplibre-compose-material3/MODULE.md b/lib/maplibre-compose-material3/MODULE.md index 0b782a43..6b4bae27 100644 --- a/lib/maplibre-compose-material3/MODULE.md +++ b/lib/maplibre-compose-material3/MODULE.md @@ -4,5 +4,5 @@ Material 3 extensions for MapLibre Compose. # Package dev.sargunv.maplibrecompose.material3.controls -Contains Material 3 based controls for the map, as an alternative to the default -map ornaments. +Material 3 based controls for the map as an alternative to the default map +ornaments. diff --git a/lib/maplibre-compose/MODULE.md b/lib/maplibre-compose/MODULE.md index 60291c8c..b380199c 100644 --- a/lib/maplibre-compose/MODULE.md +++ b/lib/maplibre-compose/MODULE.md @@ -1,6 +1,6 @@ # Module maplibre-compose -Multiplatform library to manipulate maps in Compose apps. +The primary entry point for MapLibre Compose. # Package dev.sargunv.maplibrecompose.compose diff --git a/lib/maplibre-compose/build.gradle.kts b/lib/maplibre-compose/build.gradle.kts index d2dff2d0..d7fda553 100644 --- a/lib/maplibre-compose/build.gradle.kts +++ b/lib/maplibre-compose/build.gradle.kts @@ -53,6 +53,7 @@ kotlin { implementation(compose.foundation) api(libs.kermit) api(libs.spatialk.geojson) + api(project(":lib:maplibre-compose-expressions")) } androidMain.dependencies { @@ -60,9 +61,7 @@ kotlin { implementation(libs.maplibre.android.scalebar) } - desktopMain.dependencies { - implementation("io.github.kevinnzou:compose-webview-multiplatform:1.9.40") - } + desktopMain.dependencies { implementation(libs.webview) } commonTest.dependencies { implementation(kotlin("test")) diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt index 80a21891..94dac8ac 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMap.kt @@ -10,8 +10,6 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.dp import co.touchlab.kermit.Logger -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression import dev.sargunv.maplibrecompose.core.util.correctedAndroidUri import dev.sargunv.maplibrecompose.core.util.toBoundingBox import dev.sargunv.maplibrecompose.core.util.toGravity @@ -21,6 +19,8 @@ import dev.sargunv.maplibrecompose.core.util.toOffset import dev.sargunv.maplibrecompose.core.util.toPointF import dev.sargunv.maplibrecompose.core.util.toPosition import dev.sargunv.maplibrecompose.core.util.toRectF +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position @@ -297,7 +297,7 @@ internal class AndroidMap( override fun queryRenderedFeatures( offset: DpOffset, layerIds: Set?, - predicate: Expression?, + predicate: CompiledExpression?, ): List { // Kotlin hack to pass null to a java nullable varargs val query: (PointF, MLNExpression?, Array?) -> List = @@ -309,7 +309,7 @@ internal class AndroidMap( override fun queryRenderedFeatures( rect: DpRect, layerIds: Set?, - predicate: Expression?, + predicate: CompiledExpression?, ): List { // Kotlin hack to pass null to a java nullable varargs val query: (RectF, MLNExpression?, Array?) -> List = diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt index 3f160f11..66334e52 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt @@ -1,11 +1,10 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue import org.maplibre.android.style.layers.BackgroundLayer as MLNBackgroundLayer import org.maplibre.android.style.layers.PropertyFactory @@ -13,15 +12,15 @@ internal actual class BackgroundLayer actual constructor(id: String) : Layer() { override val impl: MLNBackgroundLayer = MLNBackgroundLayer(id) - actual fun setBackgroundColor(color: Expression) { + actual fun setBackgroundColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.backgroundColor(color.toMLNExpression())) } - actual fun setBackgroundPattern(pattern: Expression>) { + actual fun setBackgroundPattern(pattern: CompiledExpression) { impl.setProperties(PropertyFactory.backgroundPattern(pattern.toMLNExpression())) } - actual fun setBackgroundOpacity(opacity: Expression) { + actual fun setBackgroundOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.backgroundOpacity(opacity.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt index 5f6bf72f..fe62f74a 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt @@ -1,17 +1,16 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.CirclePitchAlignment -import dev.sargunv.maplibrecompose.core.expression.CirclePitchScale -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchScale +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor import org.maplibre.android.style.expressions.Expression as MLNExpression import org.maplibre.android.style.layers.CircleLayer as MLNCircleLayer import org.maplibre.android.style.layers.PropertyFactory @@ -22,55 +21,55 @@ internal actual class CircleLayer actual constructor(id: String, source: Source) actual override var sourceLayer: String by impl::sourceLayer - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.setFilter(filter.toMLNExpression() ?: MLNExpression.literal(true)) } - actual fun setCircleSortKey(sortKey: Expression) { + actual fun setCircleSortKey(sortKey: CompiledExpression) { impl.setProperties(PropertyFactory.circleSortKey(sortKey.toMLNExpression())) } - actual fun setCircleRadius(radius: Expression) { + actual fun setCircleRadius(radius: CompiledExpression) { impl.setProperties(PropertyFactory.circleRadius(radius.toMLNExpression())) } - actual fun setCircleColor(color: Expression) { + actual fun setCircleColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.circleColor(color.toMLNExpression())) } - actual fun setCircleBlur(blur: Expression) { + actual fun setCircleBlur(blur: CompiledExpression) { impl.setProperties(PropertyFactory.circleBlur(blur.toMLNExpression())) } - actual fun setCircleOpacity(opacity: Expression) { + actual fun setCircleOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.circleOpacity(opacity.toMLNExpression())) } - actual fun setCircleTranslate(translate: Expression) { + actual fun setCircleTranslate(translate: CompiledExpression) { impl.setProperties(PropertyFactory.circleTranslate(translate.toMLNExpression())) } - actual fun setCircleTranslateAnchor(translateAnchor: Expression>) { + actual fun setCircleTranslateAnchor(translateAnchor: CompiledExpression) { impl.setProperties(PropertyFactory.circleTranslateAnchor(translateAnchor.toMLNExpression())) } - actual fun setCirclePitchScale(pitchScale: Expression>) { + actual fun setCirclePitchScale(pitchScale: CompiledExpression) { impl.setProperties(PropertyFactory.circlePitchScale(pitchScale.toMLNExpression())) } - actual fun setCirclePitchAlignment(pitchAlignment: Expression>) { + actual fun setCirclePitchAlignment(pitchAlignment: CompiledExpression) { impl.setProperties(PropertyFactory.circlePitchAlignment(pitchAlignment.toMLNExpression())) } - actual fun setCircleStrokeWidth(strokeWidth: Expression) { + actual fun setCircleStrokeWidth(strokeWidth: CompiledExpression) { impl.setProperties(PropertyFactory.circleStrokeWidth(strokeWidth.toMLNExpression())) } - actual fun setCircleStrokeColor(strokeColor: Expression) { + actual fun setCircleStrokeColor(strokeColor: CompiledExpression) { impl.setProperties(PropertyFactory.circleStrokeColor(strokeColor.toMLNExpression())) } - actual fun setCircleStrokeOpacity(strokeOpacity: Expression) { + actual fun setCircleStrokeOpacity(strokeOpacity: CompiledExpression) { impl.setProperties(PropertyFactory.circleStrokeOpacity(strokeOpacity.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt index 253acbfe..0e08ea0e 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt @@ -1,11 +1,11 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue internal actual sealed class FeatureLayer actual constructor(actual val source: Source) : Layer() { actual abstract var sourceLayer: String - actual abstract fun setFilter(filter: Expression) + actual abstract fun setFilter(filter: CompiledExpression) } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt index c842cce1..5a03aea4 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt @@ -1,16 +1,14 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor import org.maplibre.android.style.expressions.Expression as MLNExpression import org.maplibre.android.style.layers.FillExtrusionLayer as MLNFillExtrusionLayer import org.maplibre.android.style.layers.PropertyFactory @@ -21,39 +19,39 @@ internal actual class FillExtrusionLayer actual constructor(id: String, source: actual override var sourceLayer: String by impl::sourceLayer - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.setFilter(filter.toMLNExpression() ?: MLNExpression.literal(true)) } - actual fun setFillExtrusionOpacity(opacity: Expression) { + actual fun setFillExtrusionOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionOpacity(opacity.toMLNExpression())) } - actual fun setFillExtrusionColor(color: Expression) { + actual fun setFillExtrusionColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionColor(color.toMLNExpression())) } - actual fun setFillExtrusionTranslate(translate: Expression) { + actual fun setFillExtrusionTranslate(translate: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionTranslate(translate.toMLNExpression())) } - actual fun setFillExtrusionTranslateAnchor(anchor: Expression>) { + actual fun setFillExtrusionTranslateAnchor(anchor: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionTranslateAnchor(anchor.toMLNExpression())) } - actual fun setFillExtrusionPattern(pattern: Expression>) { + actual fun setFillExtrusionPattern(pattern: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionPattern(pattern.toMLNExpression())) } - actual fun setFillExtrusionHeight(height: Expression) { + actual fun setFillExtrusionHeight(height: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionHeight(height.toMLNExpression())) } - actual fun setFillExtrusionBase(base: Expression) { + actual fun setFillExtrusionBase(base: CompiledExpression) { impl.setProperties(PropertyFactory.fillExtrusionBase(base.toMLNExpression())) } - actual fun setFillExtrusionVerticalGradient(verticalGradient: Expression) { + actual fun setFillExtrusionVerticalGradient(verticalGradient: CompiledExpression) { impl.setProperties( PropertyFactory.fillExtrusionVerticalGradient(verticalGradient.toMLNExpression()) ) diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt index 2baf39b0..40bdca4d 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt @@ -1,16 +1,14 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor import org.maplibre.android.style.expressions.Expression as MLNExpression import org.maplibre.android.style.layers.FillLayer as MLNFillLayer import org.maplibre.android.style.layers.PropertyFactory @@ -21,39 +19,39 @@ internal actual class FillLayer actual constructor(id: String, source: Source) : actual override var sourceLayer: String by impl::sourceLayer - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.setFilter(filter.toMLNExpression() ?: MLNExpression.literal(true)) } - actual fun setFillSortKey(sortKey: Expression) { + actual fun setFillSortKey(sortKey: CompiledExpression) { impl.setProperties(PropertyFactory.fillSortKey(sortKey.toMLNExpression())) } - actual fun setFillAntialias(antialias: Expression) { + actual fun setFillAntialias(antialias: CompiledExpression) { impl.setProperties(PropertyFactory.fillAntialias(antialias.toMLNExpression())) } - actual fun setFillOpacity(opacity: Expression) { + actual fun setFillOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.fillOpacity(opacity.toMLNExpression())) } - actual fun setFillColor(color: Expression) { + actual fun setFillColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.fillColor(color.toMLNExpression())) } - actual fun setFillOutlineColor(outlineColor: Expression) { + actual fun setFillOutlineColor(outlineColor: CompiledExpression) { impl.setProperties(PropertyFactory.fillOutlineColor(outlineColor.toMLNExpression())) } - actual fun setFillTranslate(translate: Expression) { + actual fun setFillTranslate(translate: CompiledExpression) { impl.setProperties(PropertyFactory.fillTranslate(translate.toMLNExpression())) } - actual fun setFillTranslateAnchor(translateAnchor: Expression>) { + actual fun setFillTranslateAnchor(translateAnchor: CompiledExpression) { impl.setProperties(PropertyFactory.fillTranslateAnchor(translateAnchor.toMLNExpression())) } - actual fun setFillPattern(pattern: Expression>) { + actual fun setFillPattern(pattern: CompiledExpression) { impl.setProperties(PropertyFactory.fillPattern(pattern.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt index bb8449bb..22edd111 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt @@ -1,12 +1,12 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue import org.maplibre.android.style.expressions.Expression as MLNExpression import org.maplibre.android.style.layers.HeatmapLayer as MLNHeatmapLayer import org.maplibre.android.style.layers.PropertyFactory @@ -17,27 +17,27 @@ internal actual class HeatmapLayer actual constructor(id: String, source: Source actual override var sourceLayer: String by impl::sourceLayer - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.setFilter(filter.toMLNExpression() ?: MLNExpression.literal(true)) } - actual fun setHeatmapRadius(radius: Expression) { + actual fun setHeatmapRadius(radius: CompiledExpression) { impl.setProperties(PropertyFactory.heatmapRadius(radius.toMLNExpression())) } - actual fun setHeatmapWeight(weight: Expression) { + actual fun setHeatmapWeight(weight: CompiledExpression) { impl.setProperties(PropertyFactory.heatmapWeight(weight.toMLNExpression())) } - actual fun setHeatmapIntensity(intensity: Expression) { + actual fun setHeatmapIntensity(intensity: CompiledExpression) { impl.setProperties(PropertyFactory.heatmapIntensity(intensity.toMLNExpression())) } - actual fun setHeatmapColor(color: Expression) { + actual fun setHeatmapColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.heatmapColor(color.toMLNExpression())) } - actual fun setHeatmapOpacity(opacity: Expression) { + actual fun setHeatmapOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.heatmapOpacity(opacity.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt index b999ad49..5aa0a42d 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt @@ -1,12 +1,11 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.IlluminationAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor import org.maplibre.android.style.layers.HillshadeLayer as MLNHillshadeLayer import org.maplibre.android.style.layers.PropertyFactory @@ -14,27 +13,27 @@ internal actual class HillshadeLayer actual constructor(id: String, actual val s Layer() { override val impl = MLNHillshadeLayer(id, source.id) - actual fun setHillshadeIlluminationDirection(direction: Expression) { + actual fun setHillshadeIlluminationDirection(direction: CompiledExpression) { impl.setProperties(PropertyFactory.hillshadeIlluminationDirection(direction.toMLNExpression())) } - actual fun setHillshadeIlluminationAnchor(anchor: Expression>) { + actual fun setHillshadeIlluminationAnchor(anchor: CompiledExpression) { impl.setProperties(PropertyFactory.hillshadeIlluminationAnchor(anchor.toMLNExpression())) } - actual fun setHillshadeExaggeration(exaggeration: Expression) { + actual fun setHillshadeExaggeration(exaggeration: CompiledExpression) { impl.setProperties(PropertyFactory.hillshadeExaggeration(exaggeration.toMLNExpression())) } - actual fun setHillshadeShadowColor(shadowColor: Expression) { + actual fun setHillshadeShadowColor(shadowColor: CompiledExpression) { impl.setProperties(PropertyFactory.hillshadeShadowColor(shadowColor.toMLNExpression())) } - actual fun setHillshadeHighlightColor(highlightColor: Expression) { + actual fun setHillshadeHighlightColor(highlightColor: CompiledExpression) { impl.setProperties(PropertyFactory.hillshadeHighlightColor(highlightColor.toMLNExpression())) } - actual fun setHillshadeAccentColor(accentColor: Expression) { + actual fun setHillshadeAccentColor(accentColor: CompiledExpression) { impl.setProperties(PropertyFactory.hillshadeAccentColor(accentColor.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt index d678cb89..eac8caf9 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt @@ -1,20 +1,18 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor -import dev.sargunv.maplibrecompose.core.expression.VectorValue import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor +import dev.sargunv.maplibrecompose.expressions.value.VectorValue import org.maplibre.android.style.expressions.Expression as MLNExpression import org.maplibre.android.style.layers.LineLayer as MLNLineLayer import org.maplibre.android.style.layers.PropertyFactory @@ -26,71 +24,71 @@ internal actual class LineLayer actual constructor(id: String, source: Source) : actual override var sourceLayer: String by impl::sourceLayer - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.setFilter(filter.toMLNExpression() ?: MLNExpression.literal(true)) } - actual fun setLineCap(cap: Expression>) { + actual fun setLineCap(cap: CompiledExpression) { impl.setProperties(PropertyFactory.lineCap(cap.toMLNExpression())) } - actual fun setLineJoin(join: Expression>) { + actual fun setLineJoin(join: CompiledExpression) { impl.setProperties(PropertyFactory.lineJoin(join.toMLNExpression())) } - actual fun setLineMiterLimit(miterLimit: Expression) { + actual fun setLineMiterLimit(miterLimit: CompiledExpression) { impl.setProperties(PropertyFactory.lineMiterLimit(miterLimit.toMLNExpression())) } - actual fun setLineRoundLimit(roundLimit: Expression) { + actual fun setLineRoundLimit(roundLimit: CompiledExpression) { impl.setProperties(PropertyFactory.lineRoundLimit(roundLimit.toMLNExpression())) } - actual fun setLineSortKey(sortKey: Expression) { + actual fun setLineSortKey(sortKey: CompiledExpression) { impl.setProperties(PropertyFactory.lineSortKey(sortKey.toMLNExpression())) } - actual fun setLineOpacity(opacity: Expression) { + actual fun setLineOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.lineOpacity(opacity.toMLNExpression())) } - actual fun setLineColor(color: Expression) { + actual fun setLineColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.lineColor(color.toMLNExpression())) } - actual fun setLineTranslate(translate: Expression) { + actual fun setLineTranslate(translate: CompiledExpression) { impl.setProperties(PropertyFactory.lineTranslate(translate.toMLNExpression())) } - actual fun setLineTranslateAnchor(translateAnchor: Expression>) { + actual fun setLineTranslateAnchor(translateAnchor: CompiledExpression) { impl.setProperties(PropertyFactory.lineTranslateAnchor(translateAnchor.toMLNExpression())) } - actual fun setLineWidth(width: Expression) { + actual fun setLineWidth(width: CompiledExpression) { impl.setProperties(PropertyFactory.lineWidth(width.toMLNExpression())) } - actual fun setLineGapWidth(gapWidth: Expression) { + actual fun setLineGapWidth(gapWidth: CompiledExpression) { impl.setProperties(PropertyFactory.lineGapWidth(gapWidth.toMLNExpression())) } - actual fun setLineOffset(offset: Expression) { + actual fun setLineOffset(offset: CompiledExpression) { impl.setProperties(PropertyFactory.lineOffset(offset.toMLNExpression())) } - actual fun setLineBlur(blur: Expression) { + actual fun setLineBlur(blur: CompiledExpression) { impl.setProperties(PropertyFactory.lineBlur(blur.toMLNExpression())) } - actual fun setLineDasharray(dasharray: Expression>) { + actual fun setLineDasharray(dasharray: CompiledExpression>) { impl.setProperties(PropertyFactory.lineDasharray(dasharray.toMLNExpression())) } - actual fun setLinePattern(pattern: Expression>) { + actual fun setLinePattern(pattern: CompiledExpression) { impl.setProperties(PropertyFactory.linePattern(pattern.toMLNExpression())) } - actual fun setLineGradient(gradient: Expression) { + actual fun setLineGradient(gradient: CompiledExpression) { impl.setProperties(PropertyFactory.lineGradient(gradient.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt index c2301718..2ff80aeb 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt @@ -1,12 +1,11 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.MillisecondsValue -import dev.sargunv.maplibrecompose.core.expression.RasterResampling import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.RasterResampling import org.maplibre.android.style.layers.PropertyFactory import org.maplibre.android.style.layers.RasterLayer as MLNRasterLayer @@ -14,35 +13,35 @@ internal actual class RasterLayer actual constructor(id: String, actual val sour Layer() { override val impl = MLNRasterLayer(id, source.id) - actual fun setRasterOpacity(opacity: Expression) { + actual fun setRasterOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.rasterOpacity(opacity.toMLNExpression())) } - actual fun setRasterHueRotate(hueRotate: Expression) { + actual fun setRasterHueRotate(hueRotate: CompiledExpression) { impl.setProperties(PropertyFactory.rasterHueRotate(hueRotate.toMLNExpression())) } - actual fun setRasterBrightnessMin(brightnessMin: Expression) { + actual fun setRasterBrightnessMin(brightnessMin: CompiledExpression) { impl.setProperties(PropertyFactory.rasterBrightnessMin(brightnessMin.toMLNExpression())) } - actual fun setRasterBrightnessMax(brightnessMax: Expression) { + actual fun setRasterBrightnessMax(brightnessMax: CompiledExpression) { impl.setProperties(PropertyFactory.rasterBrightnessMax(brightnessMax.toMLNExpression())) } - actual fun setRasterSaturation(saturation: Expression) { + actual fun setRasterSaturation(saturation: CompiledExpression) { impl.setProperties(PropertyFactory.rasterSaturation(saturation.toMLNExpression())) } - actual fun setRasterContrast(contrast: Expression) { + actual fun setRasterContrast(contrast: CompiledExpression) { impl.setProperties(PropertyFactory.rasterContrast(contrast.toMLNExpression())) } - actual fun setRasterResampling(resampling: Expression>) { + actual fun setRasterResampling(resampling: CompiledExpression) { impl.setProperties(PropertyFactory.rasterResampling(resampling.toMLNExpression())) } - actual fun setRasterFadeDuration(fadeDuration: Expression) { + actual fun setRasterFadeDuration(fadeDuration: CompiledExpression) { impl.setProperties(PropertyFactory.rasterFadeDuration(fadeDuration.toMLNExpression())) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt index ef8cdf5f..2b8b81b4 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt @@ -1,34 +1,32 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpPaddingValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatOffsetValue -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.FormattedValue -import dev.sargunv.maplibrecompose.core.expression.IconPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.IconRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.IconTextFit -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ListValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.StringValue -import dev.sargunv.maplibrecompose.core.expression.SymbolAnchor -import dev.sargunv.maplibrecompose.core.expression.SymbolPlacement -import dev.sargunv.maplibrecompose.core.expression.SymbolZOrder -import dev.sargunv.maplibrecompose.core.expression.TextJustify -import dev.sargunv.maplibrecompose.core.expression.TextPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.TextRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.TextTransform -import dev.sargunv.maplibrecompose.core.expression.TextVariableAnchorOffsetValue -import dev.sargunv.maplibrecompose.core.expression.TextWritingMode -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toMLNExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.IconPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconTextFit +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.SymbolPlacement +import dev.sargunv.maplibrecompose.expressions.value.SymbolZOrder +import dev.sargunv.maplibrecompose.expressions.value.TextJustify +import dev.sargunv.maplibrecompose.expressions.value.TextPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextTransform +import dev.sargunv.maplibrecompose.expressions.value.TextVariableAnchorOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextWritingMode +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor import org.maplibre.android.style.expressions.Expression as MLNExpression import org.maplibre.android.style.layers.PropertyFactory import org.maplibre.android.style.layers.SymbolLayer as MLNSymbolLayer @@ -39,249 +37,249 @@ internal actual class SymbolLayer actual constructor(id: String, source: Source) actual override var sourceLayer: String by impl::sourceLayer - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.setFilter(filter.toMLNExpression() ?: MLNExpression.literal(true)) } - actual fun setSymbolPlacement(placement: Expression>) { + actual fun setSymbolPlacement(placement: CompiledExpression) { impl.setProperties(PropertyFactory.symbolPlacement(placement.toMLNExpression())) } - actual fun setSymbolSpacing(spacing: Expression) { + actual fun setSymbolSpacing(spacing: CompiledExpression) { impl.setProperties(PropertyFactory.symbolSpacing(spacing.toMLNExpression())) } - actual fun setSymbolAvoidEdges(avoidEdges: Expression) { + actual fun setSymbolAvoidEdges(avoidEdges: CompiledExpression) { impl.setProperties(PropertyFactory.symbolAvoidEdges(avoidEdges.toMLNExpression())) } - actual fun setSymbolSortKey(sortKey: Expression) { + actual fun setSymbolSortKey(sortKey: CompiledExpression) { impl.setProperties(PropertyFactory.symbolSortKey(sortKey.toMLNExpression())) } - actual fun setSymbolZOrder(zOrder: Expression>) { + actual fun setSymbolZOrder(zOrder: CompiledExpression) { impl.setProperties(PropertyFactory.symbolZOrder(zOrder.toMLNExpression())) } - actual fun setIconAllowOverlap(allowOverlap: Expression) { + actual fun setIconAllowOverlap(allowOverlap: CompiledExpression) { impl.setProperties(PropertyFactory.iconAllowOverlap(allowOverlap.toMLNExpression())) } - actual fun setIconOverlap(overlap: Expression) { + actual fun setIconOverlap(overlap: CompiledExpression) { // TODO: warn not implemented by MapLibre-native Android yet // impl.setProperties(PropertyFactory.iconOverlap(overlap.toMLNExpression())) } - actual fun setIconIgnorePlacement(ignorePlacement: Expression) { + actual fun setIconIgnorePlacement(ignorePlacement: CompiledExpression) { impl.setProperties(PropertyFactory.iconIgnorePlacement(ignorePlacement.toMLNExpression())) } - actual fun setIconOptional(optional: Expression) { + actual fun setIconOptional(optional: CompiledExpression) { impl.setProperties(PropertyFactory.iconOptional(optional.toMLNExpression())) } actual fun setIconRotationAlignment( - rotationAlignment: Expression> + rotationAlignment: CompiledExpression ) { impl.setProperties(PropertyFactory.iconRotationAlignment(rotationAlignment.toMLNExpression())) } - actual fun setIconSize(size: Expression) { + actual fun setIconSize(size: CompiledExpression) { impl.setProperties(PropertyFactory.iconSize(size.toMLNExpression())) } - actual fun setIconTextFit(textFit: Expression>) { + actual fun setIconTextFit(textFit: CompiledExpression) { impl.setProperties(PropertyFactory.iconTextFit(textFit.toMLNExpression())) } - actual fun setIconTextFitPadding(textFitPadding: Expression) { + actual fun setIconTextFitPadding(textFitPadding: CompiledExpression) { impl.setProperties(PropertyFactory.iconTextFitPadding(textFitPadding.toMLNExpression())) } - actual fun setIconImage(image: Expression>) { + actual fun setIconImage(image: CompiledExpression) { impl.setProperties(PropertyFactory.iconImage(image.toMLNExpression())) } - actual fun setIconRotate(rotate: Expression) { + actual fun setIconRotate(rotate: CompiledExpression) { impl.setProperties(PropertyFactory.iconRotate(rotate.toMLNExpression())) } - actual fun setIconPadding(padding: Expression) { + actual fun setIconPadding(padding: CompiledExpression) { impl.setProperties(PropertyFactory.iconPadding(padding.toMLNExpression())) } - actual fun setIconKeepUpright(keepUpright: Expression) { + actual fun setIconKeepUpright(keepUpright: CompiledExpression) { impl.setProperties(PropertyFactory.iconKeepUpright(keepUpright.toMLNExpression())) } - actual fun setIconOffset(offset: Expression) { + actual fun setIconOffset(offset: CompiledExpression) { impl.setProperties(PropertyFactory.iconOffset(offset.toMLNExpression())) } - actual fun setIconAnchor(anchor: Expression>) { + actual fun setIconAnchor(anchor: CompiledExpression) { impl.setProperties(PropertyFactory.iconAnchor(anchor.toMLNExpression())) } - actual fun setIconPitchAlignment(pitchAlignment: Expression>) { + actual fun setIconPitchAlignment(pitchAlignment: CompiledExpression) { impl.setProperties(PropertyFactory.iconPitchAlignment(pitchAlignment.toMLNExpression())) } - actual fun setIconOpacity(opacity: Expression) { + actual fun setIconOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.iconOpacity(opacity.toMLNExpression())) } - actual fun setIconColor(color: Expression) { + actual fun setIconColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.iconColor(color.toMLNExpression())) } - actual fun setIconHaloColor(haloColor: Expression) { + actual fun setIconHaloColor(haloColor: CompiledExpression) { impl.setProperties(PropertyFactory.iconHaloColor(haloColor.toMLNExpression())) } - actual fun setIconHaloWidth(haloWidth: Expression) { + actual fun setIconHaloWidth(haloWidth: CompiledExpression) { impl.setProperties(PropertyFactory.iconHaloWidth(haloWidth.toMLNExpression())) } - actual fun setIconHaloBlur(haloBlur: Expression) { + actual fun setIconHaloBlur(haloBlur: CompiledExpression) { impl.setProperties(PropertyFactory.iconHaloBlur(haloBlur.toMLNExpression())) } - actual fun setIconTranslate(translate: Expression) { + actual fun setIconTranslate(translate: CompiledExpression) { impl.setProperties(PropertyFactory.iconTranslate(translate.toMLNExpression())) } - actual fun setIconTranslateAnchor(translateAnchor: Expression>) { + actual fun setIconTranslateAnchor(translateAnchor: CompiledExpression) { impl.setProperties(PropertyFactory.iconTranslateAnchor(translateAnchor.toMLNExpression())) } - actual fun setTextPitchAlignment(pitchAlignment: Expression>) { + actual fun setTextPitchAlignment(pitchAlignment: CompiledExpression) { impl.setProperties(PropertyFactory.textPitchAlignment(pitchAlignment.toMLNExpression())) } actual fun setTextRotationAlignment( - rotationAlignment: Expression> + rotationAlignment: CompiledExpression ) { impl.setProperties(PropertyFactory.textRotationAlignment(rotationAlignment.toMLNExpression())) } - actual fun setTextField(field: Expression>) { + actual fun setTextField(field: CompiledExpression) { impl.setProperties(PropertyFactory.textField(field.toMLNExpression())) } - actual fun setTextFont(font: Expression>) { + actual fun setTextFont(font: CompiledExpression>) { impl.setProperties(PropertyFactory.textFont(font.toMLNExpression())) } - actual fun setTextSize(size: Expression) { + actual fun setTextSize(size: CompiledExpression) { impl.setProperties(PropertyFactory.textSize(size.toMLNExpression())) } - actual fun setTextMaxWidth(maxWidth: Expression) { + actual fun setTextMaxWidth(maxWidth: CompiledExpression) { impl.setProperties(PropertyFactory.textMaxWidth(maxWidth.toMLNExpression())) } - actual fun setTextLineHeight(lineHeight: Expression) { + actual fun setTextLineHeight(lineHeight: CompiledExpression) { impl.setProperties(PropertyFactory.textLineHeight(lineHeight.toMLNExpression())) } - actual fun setTextLetterSpacing(letterSpacing: Expression) { + actual fun setTextLetterSpacing(letterSpacing: CompiledExpression) { impl.setProperties(PropertyFactory.textLetterSpacing(letterSpacing.toMLNExpression())) } - actual fun setTextJustify(justify: Expression>) { + actual fun setTextJustify(justify: CompiledExpression) { impl.setProperties(PropertyFactory.textJustify(justify.toMLNExpression())) } - actual fun setTextRadialOffset(radialOffset: Expression) { + actual fun setTextRadialOffset(radialOffset: CompiledExpression) { impl.setProperties(PropertyFactory.textRadialOffset(radialOffset.toMLNExpression())) } - actual fun setTextVariableAnchor(variableAnchor: Expression>>) { + actual fun setTextVariableAnchor(variableAnchor: CompiledExpression>) { impl.setProperties(PropertyFactory.textVariableAnchor(variableAnchor.toMLNExpression())) } actual fun setTextVariableAnchorOffset( - variableAnchorOffset: Expression + variableAnchorOffset: CompiledExpression ) { impl.setProperties( PropertyFactory.textVariableAnchorOffset(variableAnchorOffset.toMLNExpression()) ) } - actual fun setTextAnchor(anchor: Expression>) { + actual fun setTextAnchor(anchor: CompiledExpression) { impl.setProperties(PropertyFactory.textAnchor(anchor.toMLNExpression())) } - actual fun setTextMaxAngle(maxAngle: Expression) { + actual fun setTextMaxAngle(maxAngle: CompiledExpression) { impl.setProperties(PropertyFactory.textMaxAngle(maxAngle.toMLNExpression())) } - actual fun setTextWritingMode(writingMode: Expression>>) { + actual fun setTextWritingMode(writingMode: CompiledExpression>) { impl.setProperties(PropertyFactory.textWritingMode(writingMode.toMLNExpression())) } - actual fun setTextRotate(rotate: Expression) { + actual fun setTextRotate(rotate: CompiledExpression) { impl.setProperties(PropertyFactory.textRotate(rotate.toMLNExpression())) } - actual fun setTextPadding(padding: Expression) { + actual fun setTextPadding(padding: CompiledExpression) { impl.setProperties(PropertyFactory.textPadding(padding.toMLNExpression())) } - actual fun setTextKeepUpright(keepUpright: Expression) { + actual fun setTextKeepUpright(keepUpright: CompiledExpression) { impl.setProperties(PropertyFactory.textKeepUpright(keepUpright.toMLNExpression())) } - actual fun setTextTransform(transform: Expression>) { + actual fun setTextTransform(transform: CompiledExpression) { impl.setProperties(PropertyFactory.textTransform(transform.toMLNExpression())) } - actual fun setTextOffset(offset: Expression) { + actual fun setTextOffset(offset: CompiledExpression) { impl.setProperties(PropertyFactory.textOffset(offset.toMLNExpression())) } - actual fun setTextAllowOverlap(allowOverlap: Expression) { + actual fun setTextAllowOverlap(allowOverlap: CompiledExpression) { impl.setProperties(PropertyFactory.textAllowOverlap(allowOverlap.toMLNExpression())) } - actual fun setTextOverlap(overlap: Expression) { + actual fun setTextOverlap(overlap: CompiledExpression) { // not implemented by MapLibre-native Android yet // impl.setProperties(PropertyFactory.textOverlap(overlap.toMLNExpression())) } - actual fun setTextIgnorePlacement(ignorePlacement: Expression) { + actual fun setTextIgnorePlacement(ignorePlacement: CompiledExpression) { impl.setProperties(PropertyFactory.textIgnorePlacement(ignorePlacement.toMLNExpression())) } - actual fun setTextOptional(optional: Expression) { + actual fun setTextOptional(optional: CompiledExpression) { impl.setProperties(PropertyFactory.textOptional(optional.toMLNExpression())) } - actual fun setTextOpacity(opacity: Expression) { + actual fun setTextOpacity(opacity: CompiledExpression) { impl.setProperties(PropertyFactory.textOpacity(opacity.toMLNExpression())) } - actual fun setTextColor(color: Expression) { + actual fun setTextColor(color: CompiledExpression) { impl.setProperties(PropertyFactory.textColor(color.toMLNExpression())) } - actual fun setTextHaloColor(haloColor: Expression) { + actual fun setTextHaloColor(haloColor: CompiledExpression) { impl.setProperties(PropertyFactory.textHaloColor(haloColor.toMLNExpression())) } - actual fun setTextHaloWidth(haloWidth: Expression) { + actual fun setTextHaloWidth(haloWidth: CompiledExpression) { impl.setProperties(PropertyFactory.textHaloWidth(haloWidth.toMLNExpression())) } - actual fun setTextHaloBlur(haloBlur: Expression) { + actual fun setTextHaloBlur(haloBlur: CompiledExpression) { impl.setProperties(PropertyFactory.textHaloBlur(haloBlur.toMLNExpression())) } - actual fun setTextTranslate(translate: Expression) { + actual fun setTextTranslate(translate: CompiledExpression) { impl.setProperties(PropertyFactory.textTranslate(translate.toMLNExpression())) } - actual fun setTextTranslateAnchor(translateAnchor: Expression>) { + actual fun setTextTranslateAnchor(translateAnchor: CompiledExpression) { impl.setProperties(PropertyFactory.textTranslateAnchor(translateAnchor.toMLNExpression())) } } 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 6ea6a681..2785ea3b 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 @@ -1,8 +1,9 @@ package dev.sargunv.maplibrecompose.core.source -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const 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.const import io.github.dellisd.spatialk.geojson.GeoJson import org.maplibre.android.style.sources.GeoJsonOptions as MLNGeoJsonOptions import org.maplibre.android.style.sources.GeoJsonSource as MLNGeoJsonSource @@ -32,7 +33,7 @@ public actual class GeoJsonSource : Source { withClusterProperty( key, const(value.operator).toMLNExpression()!!, - value.mapper.toMLNExpression()!!, + value.mapper.compile(ExpressionContext.None).toMLNExpression()!!, ) } } diff --git a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt index 1aed5887..702c0ff6 100644 --- a/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt +++ b/lib/maplibre-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt @@ -3,10 +3,7 @@ package dev.sargunv.maplibrecompose.core.util import android.graphics.PointF import android.graphics.RectF import android.view.Gravity -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.Alignment -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.DpOffset @@ -18,7 +15,18 @@ import com.google.gson.JsonElement import com.google.gson.JsonNull import com.google.gson.JsonObject import com.google.gson.JsonPrimitive -import dev.sargunv.maplibrecompose.core.expression.Expression +import dev.sargunv.maplibrecompose.expressions.ast.BooleanLiteral +import dev.sargunv.maplibrecompose.expressions.ast.ColorLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledFunctionCall +import dev.sargunv.maplibrecompose.expressions.ast.CompiledListLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledMapLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledOptions +import dev.sargunv.maplibrecompose.expressions.ast.DpPaddingLiteral +import dev.sargunv.maplibrecompose.expressions.ast.FloatLiteral +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.ast.OffsetLiteral +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Position import java.net.URI @@ -51,54 +59,71 @@ internal fun Position.toLatLng(): LatLng = LatLng(latitude = latitude, longitude internal fun LatLngBounds.toBoundingBox(): BoundingBox = BoundingBox(northeast = northEast.toPosition(), southwest = southWest.toPosition()) -internal fun Expression<*>.toMLNExpression(): MLNExpression? = - when (value) { - null -> null - else -> MLNExpression.Converter.convert(normalizeJsonLike(value)) +internal fun CompiledExpression<*>.toMLNExpression(): MLNExpression? = + if (this == NullLiteral) null else MLNExpression.Converter.convert(normalizeJsonLike(false)) + +private fun buildLiteralArray(inLiteral: Boolean, block: JsonArray.() -> Unit): JsonArray { + return if (inLiteral) { + JsonArray().apply(block) + } else { + JsonArray(2).apply { + add("literal") + add(JsonArray().apply(block)) + } } +} -private fun normalizeJsonLike(value: Any?): JsonElement = - when (value) { - null -> JsonNull.INSTANCE - is Boolean -> JsonPrimitive(value) - is Number -> JsonPrimitive(value) - is String -> JsonPrimitive(value) - is List<*> -> JsonArray().apply { value.forEach { add(normalizeJsonLike(it)) } } - is Map<*, *> -> - JsonObject().apply { value.forEach { add(it.key as String, normalizeJsonLike(it.value)) } } - - is Offset -> - JsonArray(2).apply { - add("literal") - add( - JsonArray(2).apply { - add(value.x) - add(value.y) - } - ) - } +private fun buildLiteralObject(inLiteral: Boolean, block: JsonObject.() -> Unit): JsonObject { + return if (inLiteral) { + JsonObject().apply(block) + } else { + JsonObject().apply { add("literal", JsonObject().apply(block)) } + } +} - is PaddingValues.Absolute -> - JsonArray(2).apply { - add("literal") - add( - JsonArray(4).apply { - add(value.calculateTopPadding().value) - add(value.calculateRightPadding(LayoutDirection.Ltr).value) - add(value.calculateBottomPadding().value) - add(value.calculateLeftPadding(LayoutDirection.Ltr).value) - } - ) +private fun CompiledExpression<*>.normalizeJsonLike(inLiteral: Boolean): JsonElement = + when (this) { + NullLiteral -> JsonNull.INSTANCE + is BooleanLiteral -> JsonPrimitive(value) + is FloatLiteral -> JsonPrimitive(value) + is StringLiteral -> JsonPrimitive(value) + is OffsetLiteral -> + buildLiteralArray(inLiteral) { + add(value.x) + add(value.y) } - is Color -> + is ColorLiteral -> JsonPrimitive( value.toArgb().let { "rgba(${(it shr 16) and 0xFF}, ${(it shr 8) and 0xFF}, ${it and 0xFF}, ${value.alpha})" } ) - else -> throw IllegalArgumentException("Unsupported type: ${value::class}") + is DpPaddingLiteral -> + buildLiteralArray(inLiteral) { + add(value.calculateTopPadding().value) + add(value.calculateRightPadding(LayoutDirection.Ltr).value) + add(value.calculateBottomPadding().value) + add(value.calculateLeftPadding(LayoutDirection.Ltr).value) + } + + is CompiledFunctionCall -> + JsonArray(args.size + 1).apply { + add(name) + args.forEachIndexed { i, v -> add(v.normalizeJsonLike(inLiteral || isLiteralArg(i))) } + } + + is CompiledListLiteral<*> -> + buildLiteralArray(inLiteral) { value.forEach { add(it.normalizeJsonLike(true)) } } + + is CompiledMapLiteral<*> -> + buildLiteralObject(inLiteral) { + value.forEach { (k, v) -> add(k, v.normalizeJsonLike(true)) } + } + + is CompiledOptions<*> -> + JsonObject().apply { value.forEach { (k, v) -> add(k, v.normalizeJsonLike(inLiteral)) } } } internal fun Alignment.toGravity(layoutDir: LayoutDirection): Int { diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt index 2ced7d8c..f2790fc5 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/CameraState.kt @@ -9,9 +9,10 @@ import dev.sargunv.maplibrecompose.core.CameraMoveReason import dev.sargunv.maplibrecompose.core.CameraPosition import dev.sargunv.maplibrecompose.core.MaplibreMap import dev.sargunv.maplibrecompose.core.VisibleRegion -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position @@ -117,7 +118,8 @@ public class CameraState internal constructor(firstPosition: CameraPosition) { layerIds: Set? = null, predicate: Expression = const(true), ): List { - val predicateOrNull = predicate.takeUnless { it == const(true) } + val predicateOrNull = + predicate.takeUnless { it == const(true) }?.compile(ExpressionContext.None) return map?.queryRenderedFeatures(offset, layerIds, predicateOrNull) ?: emptyList() } @@ -137,7 +139,8 @@ public class CameraState internal constructor(firstPosition: CameraPosition) { layerIds: Set? = null, predicate: Expression = const(true), ): List { - val predicateOrNull = predicate.takeUnless { it == const(true) } + val predicateOrNull = + predicate.takeUnless { it == const(true) }?.compile(ExpressionContext.None) return map?.queryRenderedFeatures(rect, layerIds, predicateOrNull) ?: emptyList() } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/ImageManager.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/ImageManager.kt index 0da89521..01a501eb 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/ImageManager.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/engine/ImageManager.kt @@ -1,22 +1,13 @@ package dev.sargunv.maplibrecompose.compose.engine -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.remember import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.takeOrElse import androidx.compose.ui.graphics.Canvas import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.drawscope.CanvasDrawScope import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionValue -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.cast -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue internal class ImageManager(private val node: StyleNode) { private val bitmapIds = IncrementingIdMap("bitmap") @@ -26,7 +17,7 @@ internal class ImageManager(private val node: StyleNode) { private val painterCounter = ReferenceCounter() private val painterBitmaps = mutableMapOf() - private fun resolveBitmap(bitmap: ImageBitmap): String { + internal fun acquireBitmap(bitmap: ImageBitmap): String { bitmapCounter.increment(bitmap) { val id = bitmapIds.addId(bitmap) node.logger?.i { "Adding bitmap $id" } @@ -35,7 +26,7 @@ internal class ImageManager(private val node: StyleNode) { return bitmapIds.getId(bitmap) } - private fun disposeBitmap(bitmap: ImageBitmap) { + internal fun releaseBitmap(bitmap: ImageBitmap) { bitmapCounter.decrement(bitmap) { val id = bitmapIds.removeId(bitmap) node.logger?.i { "Removing bitmap $id" } @@ -53,7 +44,7 @@ internal class ImageManager(private val node: StyleNode) { } } - private fun resolvePainter( + internal fun acquirePainter( painter: Painter, density: Density, layoutDirection: LayoutDirection, @@ -69,7 +60,7 @@ internal class ImageManager(private val node: StyleNode) { return painterIds.getId(painter) } - private fun disposePainter(painter: Painter) { + internal fun releasePainter(painter: Painter) { painterCounter.decrement(painter) { val id = painterIds.removeId(painter) node.logger?.i { "Removing painter $id" } @@ -77,36 +68,4 @@ internal class ImageManager(private val node: StyleNode) { node.style.removeImage(id) } } - - @Composable - internal fun resolveImages( - expr: Expression - ): Expression> { - val density = LocalDensity.current - val layoutDirection = LocalLayoutDirection.current - - DisposableEffect(expr) { - onDispose { - expr.visitLeaves { value -> - when (value) { - is ImageBitmap -> disposeBitmap(value) - is Painter -> disposePainter(value) - else -> {} - } - } - } - } - - return remember(expr) { - expr - .mapLeaves { value -> - when (value) { - is ImageBitmap -> resolveBitmap(value) - is Painter -> resolvePainter(value, density, layoutDirection) - else -> value - } - } - .cast() - } - } } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/BackgroundLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/BackgroundLayer.kt index 99b77778..e0f6d626 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/BackgroundLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/BackgroundLayer.kt @@ -3,14 +3,13 @@ package dev.sargunv.maplibrecompose.compose.layer import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.compose.engine.LocalStyleNode -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue import dev.sargunv.maplibrecompose.core.layer.BackgroundLayer +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue /** * The background layer just draws the map background, by default, plain black. @@ -41,8 +40,11 @@ public fun BackgroundLayer( color: Expression = const(Color.Black), pattern: Expression = nil(), ) { - val node = LocalStyleNode.current - val resolvedPattern = node.imageManager.resolveImages(pattern) + val compile = rememberPropertyCompiler() + + val compiledOpacity = compile(opacity) + val compiledColor = compile(color) + val compiledPattern = compile(pattern) LayerNode( factory = { BackgroundLayer(id = id) }, @@ -50,9 +52,9 @@ public fun BackgroundLayer( set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } set(visible) { layer.visible = it } - set(color) { layer.setBackgroundColor(it) } - set(resolvedPattern) { layer.setBackgroundPattern(it) } - set(opacity) { layer.setBackgroundOpacity(it) } + set(compiledColor) { layer.setBackgroundColor(it) } + set(compiledPattern) { layer.setBackgroundPattern(it) } + set(compiledOpacity) { layer.setBackgroundOpacity(it) } }, onClick = null, onLongClick = null, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/CircleLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/CircleLayer.kt index 040a3749..dfad0fc4 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/CircleLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/CircleLayer.kt @@ -6,20 +6,19 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import dev.sargunv.maplibrecompose.compose.FeaturesClickHandler import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.CirclePitchAlignment -import dev.sargunv.maplibrecompose.core.expression.CirclePitchScale -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.layer.CircleLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchScale +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor /** * A circle layer draws points from the [sourceLayer] in the given [source] in the given style as a @@ -34,8 +33,7 @@ import dev.sargunv.maplibrecompose.core.source.Source * this, the layer will be hidden. A value in the range of `[0..24]`. * @param filter An expression specifying conditions on source features. Only features that match * the filter are displayed. Zoom expressions in filters are only evaluated at integer zoom - * levels. The - * [featureState][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.featureState] + * levels. The [featureState][dev.sargunv.maplibrecompose.expressions.dsl.Feature.state] * expression is not supported in filter expressions. * @param visible Whether the layer should be displayed. * @param sortKey Sorts features within this layer in ascending order based on this value. Features @@ -71,7 +69,7 @@ public fun CircleLayer( visible: Boolean = true, sortKey: Expression = nil(), translate: Expression = const(DpOffset.Zero), - translateAnchor: Expression> = const(TranslateAnchor.Map), + translateAnchor: Expression = const(TranslateAnchor.Map), opacity: Expression = const(1f), color: Expression = const(Color.Black), blur: Expression = const(0f), @@ -79,32 +77,47 @@ public fun CircleLayer( strokeOpacity: Expression = const(1f), strokeColor: Expression = const(Color.Black), strokeWidth: Expression = const(0.dp), - pitchScale: Expression> = const(CirclePitchScale.Map), - pitchAlignment: Expression> = - const(CirclePitchAlignment.Viewport), + pitchScale: Expression = const(CirclePitchScale.Map), + pitchAlignment: Expression = const(CirclePitchAlignment.Viewport), onClick: FeaturesClickHandler? = null, onLongClick: FeaturesClickHandler? = null, ) { + val compile = rememberPropertyCompiler() + + val compiledFilter = compile(filter) + val compiledSortKey = compile(sortKey) + val compiledTranslate = compile(translate) + val compiledTranslateAnchor = compile(translateAnchor) + val compiledOpacity = compile(opacity) + val compiledColor = compile(color) + val compiledBlur = compile(blur) + val compiledRadius = compile(radius) + val compiledStrokeOpacity = compile(strokeOpacity) + val compiledStrokeColor = compile(strokeColor) + val compiledStrokeWidth = compile(strokeWidth) + val compiledPitchScale = compile(pitchScale) + val compiledPitchAlignment = compile(pitchAlignment) + LayerNode( factory = { CircleLayer(id = id, source = source) }, update = { set(sourceLayer) { layer.sourceLayer = it } set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } - set(filter) { layer.setFilter(it) } + set(compiledFilter) { layer.setFilter(it) } set(visible) { layer.visible = it } - set(sortKey) { layer.setCircleSortKey(it) } - set(radius) { layer.setCircleRadius(it) } - set(color) { layer.setCircleColor(it) } - set(blur) { layer.setCircleBlur(it) } - set(opacity) { layer.setCircleOpacity(it) } - set(translate) { layer.setCircleTranslate(it) } - set(translateAnchor) { layer.setCircleTranslateAnchor(it) } - set(pitchScale) { layer.setCirclePitchScale(it) } - set(pitchAlignment) { layer.setCirclePitchAlignment(it) } - set(strokeWidth) { layer.setCircleStrokeWidth(it) } - set(strokeColor) { layer.setCircleStrokeColor(it) } - set(strokeOpacity) { layer.setCircleStrokeOpacity(it) } + set(compiledSortKey) { layer.setCircleSortKey(it) } + set(compiledRadius) { layer.setCircleRadius(it) } + set(compiledColor) { layer.setCircleColor(it) } + set(compiledBlur) { layer.setCircleBlur(it) } + set(compiledOpacity) { layer.setCircleOpacity(it) } + set(compiledTranslate) { layer.setCircleTranslate(it) } + set(compiledTranslateAnchor) { layer.setCircleTranslateAnchor(it) } + set(compiledPitchScale) { layer.setCirclePitchScale(it) } + set(compiledPitchAlignment) { layer.setCirclePitchAlignment(it) } + set(compiledStrokeWidth) { layer.setCircleStrokeWidth(it) } + set(compiledStrokeColor) { layer.setCircleStrokeColor(it) } + set(compiledStrokeOpacity) { layer.setCircleStrokeOpacity(it) } }, onClick = onClick, onLongClick = onLongClick, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillExtrusionLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillExtrusionLayer.kt index 5a9ef879..633a53d5 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillExtrusionLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillExtrusionLayer.kt @@ -5,19 +5,17 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.DpOffset import dev.sargunv.maplibrecompose.compose.FeaturesClickHandler import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.compose.engine.LocalStyleNode -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.layer.FillExtrusionLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor /** * A fill extrusion layer draws polygons from the [sourceLayer] in the given [source] in the given @@ -33,8 +31,7 @@ import dev.sargunv.maplibrecompose.core.source.Source * this, the layer will be hidden. A value in the range of `[0..24]`. * @param filter An expression specifying conditions on source features. Only features that match * the filter are displayed. Zoom expressions in filters are only evaluated at integer zoom - * levels. The - * [featureState][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.featureState] + * levels. The [featureState][dev.sargunv.maplibrecompose.expressions.dsl.Feature.state] * expression is not supported in filter expressions. * @param visible Whether the layer should be displayed. * @param translate The geometry's offset relative to the [translateAnchor]. Negative numbers @@ -72,7 +69,7 @@ public fun FillExtrusionLayer( filter: Expression = nil(), visible: Boolean = true, translate: Expression = const(DpOffset.Zero), - translateAnchor: Expression> = const(TranslateAnchor.Map), + translateAnchor: Expression = const(TranslateAnchor.Map), opacity: Expression = const(1f), color: Expression = const(Color.Black), pattern: Expression = nil(), @@ -82,8 +79,17 @@ public fun FillExtrusionLayer( onClick: FeaturesClickHandler? = null, onLongClick: FeaturesClickHandler? = null, ) { - val node = LocalStyleNode.current - val resolvedPattern = node.imageManager.resolveImages(pattern) + val compile = rememberPropertyCompiler() + + val compiledFilter = compile(filter) + val compiledOpacity = compile(opacity) + val compiledColor = compile(color) + val compiledTranslate = compile(translate) + val compiledTranslateAnchor = compile(translateAnchor) + val compiledPattern = compile(pattern) + val compiledHeight = compile(height) + val compiledBase = compile(base) + val compiledVerticalGradient = compile(verticalGradient) LayerNode( factory = { FillExtrusionLayer(id = id, source = source) }, @@ -91,16 +97,16 @@ public fun FillExtrusionLayer( set(sourceLayer) { layer.sourceLayer = it } set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } - set(filter) { layer.setFilter(it) } + set(compiledFilter) { layer.setFilter(it) } set(visible) { layer.visible = it } - set(opacity) { layer.setFillExtrusionOpacity(it) } - set(color) { layer.setFillExtrusionColor(it) } - set(translate) { layer.setFillExtrusionTranslate(it) } - set(translateAnchor) { layer.setFillExtrusionTranslateAnchor(it) } - set(resolvedPattern) { layer.setFillExtrusionPattern(it) } - set(height) { layer.setFillExtrusionHeight(it) } - set(base) { layer.setFillExtrusionBase(it) } - set(verticalGradient) { layer.setFillExtrusionVerticalGradient(it) } + set(compiledOpacity) { layer.setFillExtrusionOpacity(it) } + set(compiledColor) { layer.setFillExtrusionColor(it) } + set(compiledTranslate) { layer.setFillExtrusionTranslate(it) } + set(compiledTranslateAnchor) { layer.setFillExtrusionTranslateAnchor(it) } + set(compiledPattern) { layer.setFillExtrusionPattern(it) } + set(compiledHeight) { layer.setFillExtrusionHeight(it) } + set(compiledBase) { layer.setFillExtrusionBase(it) } + set(compiledVerticalGradient) { layer.setFillExtrusionVerticalGradient(it) } }, onClick = onClick, onLongClick = onLongClick, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillLayer.kt index 9665e93a..90124116 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/FillLayer.kt @@ -5,19 +5,17 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.DpOffset import dev.sargunv.maplibrecompose.compose.FeaturesClickHandler import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.compose.engine.LocalStyleNode -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.layer.FillLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor /** * A fill layer draws polygons from the [sourceLayer] in the given [source] in the given style as a @@ -32,8 +30,7 @@ import dev.sargunv.maplibrecompose.core.source.Source * this, the layer will be hidden. A value in the range of `[0..24]`. * @param filter An expression specifying conditions on source features. Only features that match * the filter are displayed. Zoom expressions in filters are only evaluated at integer zoom - * levels. The - * [featureState][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.featureState] + * levels. The [featureState][dev.sargunv.maplibrecompose.expressions.dsl.Feature.state] * expression is not supported in filter expressions. * @param visible Whether the layer should be displayed. * @param sortKey Sorts features within this layer in ascending order based on this value. Features @@ -72,7 +69,7 @@ public fun FillLayer( visible: Boolean = true, sortKey: Expression = nil(), translate: Expression = const(DpOffset.Zero), - translateAnchor: Expression> = const(TranslateAnchor.Map), + translateAnchor: Expression = const(TranslateAnchor.Map), opacity: Expression = const(1f), color: Expression = const(Color.Black), pattern: Expression = nil(), @@ -81,8 +78,17 @@ public fun FillLayer( onClick: FeaturesClickHandler? = null, onLongClick: FeaturesClickHandler? = null, ) { - val node = LocalStyleNode.current - val resolvedPattern = node.imageManager.resolveImages(pattern) + val compile = rememberPropertyCompiler() + + val compiledFilter = compile(filter) + val compiledSortKey = compile(sortKey) + val compiledTranslate = compile(translate) + val compiledAntialias = compile(antialias) + val compiledOpacity = compile(opacity) + val compiledColor = compile(color) + val compiledPattern = compile(pattern) + val compiledTranslateAnchor = compile(translateAnchor) + val compiledOutlineColor = compile(outlineColor) LayerNode( factory = { FillLayer(id = id, source = source) }, @@ -90,16 +96,16 @@ public fun FillLayer( set(sourceLayer) { layer.sourceLayer = it } set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } - set(filter) { layer.setFilter(it) } + set(compiledFilter) { layer.setFilter(it) } set(visible) { layer.visible = it } - set(sortKey) { layer.setFillSortKey(it) } - set(antialias) { layer.setFillAntialias(it) } - set(opacity) { layer.setFillOpacity(it) } - set(color) { layer.setFillColor(it) } - set(outlineColor) { layer.setFillOutlineColor(it) } - set(translate) { layer.setFillTranslate(it) } - set(translateAnchor) { layer.setFillTranslateAnchor(it) } - set(resolvedPattern) { layer.setFillPattern(it) } + set(compiledSortKey) { layer.setFillSortKey(it) } + set(compiledAntialias) { layer.setFillAntialias(it) } + set(compiledOpacity) { layer.setFillOpacity(it) } + set(compiledColor) { layer.setFillColor(it) } + set(compiledOutlineColor) { layer.setFillOutlineColor(it) } + set(compiledTranslate) { layer.setFillTranslate(it) } + set(compiledTranslateAnchor) { layer.setFillTranslateAnchor(it) } + set(compiledPattern) { layer.setFillPattern(it) } }, onClick = onClick, onLongClick = onLongClick, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HeatmapLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HeatmapLayer.kt index 3157fa2a..1d8d399e 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HeatmapLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/HeatmapLayer.kt @@ -4,17 +4,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp import dev.sargunv.maplibrecompose.compose.FeaturesClickHandler import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Defaults -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.heatmapDensity -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.FloatValue import dev.sargunv.maplibrecompose.core.layer.HeatmapLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.Defaults +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.heatmapDensity +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue /** * A heatmap layer draws points from the [sourceLayer] in the given [source] as a heatmap. @@ -28,8 +28,7 @@ import dev.sargunv.maplibrecompose.core.source.Source * this, the layer will be hidden. A value in the range of `[0..24]`. * @param filter An expression specifying conditions on source features. Only features that match * the filter are displayed. Zoom expressions in filters are only evaluated at integer zoom - * levels. The - * [featureState][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.featureState] + * levels. The [featureState][dev.sargunv.maplibrecompose.expressions.dsl.Feature.state] * expression is not supported in filter expressions. * @param visible Whether the layer should be displayed. * @param color Defines the color of each pixel based on its density value in a heatmap. Should be @@ -63,19 +62,28 @@ public fun HeatmapLayer( onClick: FeaturesClickHandler? = null, onLongClick: FeaturesClickHandler? = null, ) { + val compile = rememberPropertyCompiler() + + val compiledFilter = compile(filter) + val compiledColor = compile(color) + val compiledOpacity = compile(opacity) + val compiledRadius = compile(radius) + val compiledWeight = compile(weight) + val compiledIntensity = compile(intensity) + LayerNode( factory = { HeatmapLayer(id = id, source = source) }, update = { set(sourceLayer) { layer.sourceLayer = it } set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } - set(filter) { layer.setFilter(it) } + set(compiledFilter) { layer.setFilter(it) } set(visible) { layer.visible = it } - set(radius) { layer.setHeatmapRadius(it) } - set(weight) { layer.setHeatmapWeight(it) } - set(intensity) { layer.setHeatmapIntensity(it) } - set(color) { layer.setHeatmapColor(it) } - set(opacity) { layer.setHeatmapOpacity(it) } + set(compiledRadius) { layer.setHeatmapRadius(it) } + set(compiledWeight) { layer.setHeatmapWeight(it) } + set(compiledIntensity) { layer.setHeatmapIntensity(it) } + set(compiledColor) { layer.setHeatmapColor(it) } + set(compiledOpacity) { layer.setHeatmapOpacity(it) } }, onClick = onClick, onLongClick = onLongClick, 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 c663149a..e5d1ae13 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 @@ -3,14 +3,13 @@ package dev.sargunv.maplibrecompose.compose.layer import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.IlluminationAnchor import dev.sargunv.maplibrecompose.core.layer.HillshadeLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor /** * Client-side hillshading visualization based on DEM data. The implementation supports Mapbox @@ -48,22 +47,30 @@ public fun HillshadeLayer( highlightColor: Expression = const(Color.White), accentColor: Expression = const(Color.Black), illuminationDirection: Expression = const(355f), - illuminationAnchor: Expression> = - const(IlluminationAnchor.Viewport), + illuminationAnchor: Expression = const(IlluminationAnchor.Viewport), exaggeration: Expression = const(0.5f), ) { + val compile = rememberPropertyCompiler() + + val compiledShadowColor = compile(shadowColor) + val compiledHighlightColor = compile(highlightColor) + val compiledAccentColor = compile(accentColor) + val compiledIlluminationDirection = compile(illuminationDirection) + val compiledIlluminationAnchor = compile(illuminationAnchor) + val compiledExaggeration = compile(exaggeration) + LayerNode( factory = { HillshadeLayer(id = id, source = source) }, update = { set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } set(visible) { layer.visible = it } - set(illuminationDirection) { layer.setHillshadeIlluminationDirection(it) } - set(illuminationAnchor) { layer.setHillshadeIlluminationAnchor(it) } - set(exaggeration) { layer.setHillshadeExaggeration(it) } - set(shadowColor) { layer.setHillshadeShadowColor(it) } - set(highlightColor) { layer.setHillshadeHighlightColor(it) } - set(accentColor) { layer.setHillshadeAccentColor(it) } + set(compiledIlluminationDirection) { layer.setHillshadeIlluminationDirection(it) } + set(compiledIlluminationAnchor) { layer.setHillshadeIlluminationAnchor(it) } + set(compiledExaggeration) { layer.setHillshadeExaggeration(it) } + set(compiledShadowColor) { layer.setHillshadeShadowColor(it) } + set(compiledHighlightColor) { layer.setHillshadeHighlightColor(it) } + set(compiledAccentColor) { layer.setHillshadeAccentColor(it) } }, onClick = null, onLongClick = null, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LayerPropertyCompiler.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LayerPropertyCompiler.kt new file mode 100644 index 00000000..1b84ad70 --- /dev/null +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LayerPropertyCompiler.kt @@ -0,0 +1,107 @@ +package dev.sargunv.maplibrecompose.compose.layer + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.TextUnitType +import dev.sargunv.maplibrecompose.compose.engine.LocalStyleNode +import dev.sargunv.maplibrecompose.compose.engine.StyleNode +import dev.sargunv.maplibrecompose.expressions.ExpressionContext +import dev.sargunv.maplibrecompose.expressions.ast.BitmapLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.ast.PainterLiteral +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.value.ExpressionValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue + +internal class LayerPropertyCompiler( + private val styleNode: StyleNode, + private val density: Density, + private val layoutDirection: LayoutDirection, + private val emScale: Expression? = null, + private val spScale: Expression? = null, +) { + private val context = + object : ExpressionContext { + private var seenTextUnitType: TextUnitType? = null + + override val emScale: Expression + get() { + return this@LayerPropertyCompiler.emScale + ?: when (seenTextUnitType) { + null -> { + seenTextUnitType = TextUnitType.Em + const(1f) + } + + TextUnitType.Em -> const(1f) + else -> error("mixing EM and SP units is not supported in most expressions") + } + } + + override val spScale: Expression + get() { + return this@LayerPropertyCompiler.spScale + ?: when (seenTextUnitType) { + null -> { + seenTextUnitType = TextUnitType.Sp + const(1f) + } + + TextUnitType.Sp -> const(1f) + else -> error("mixing SP and EM units is not supported in most expressions") + } + } + + override fun resolveBitmap(bitmap: ImageBitmap): String { + return styleNode.imageManager.acquireBitmap(bitmap) + } + + override fun resolvePainter(painter: Painter): String { + return styleNode.imageManager.acquirePainter(painter, density, layoutDirection) + } + + fun reset() { + seenTextUnitType = null + } + } + + @Composable + operator fun invoke(expression: Expression): CompiledExpression { + DisposableEffect(this, expression) { + onDispose { + expression.visit { + when (it) { + is BitmapLiteral -> styleNode.imageManager.releaseBitmap(it.value) + is PainterLiteral -> styleNode.imageManager.releasePainter(it.value) + else -> {} + } + } + } + } + return remember(this, expression) { + context.reset() + expression.compile(context) + } + } +} + +@Composable +internal fun rememberPropertyCompiler( + emScale: Expression? = null, + spScale: Expression? = null, +): LayerPropertyCompiler { + val styleNode = LocalStyleNode.current + val density = LocalDensity.current + val layoutDirection = LocalLayoutDirection.current + return remember(styleNode, density, layoutDirection, emScale, spScale) { + LayerPropertyCompiler(styleNode, density, layoutDirection, emScale, spScale) + } +} diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LineLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LineLayer.kt index 7c32f41d..a17afe2f 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LineLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/LineLayer.kt @@ -6,23 +6,21 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import dev.sargunv.maplibrecompose.compose.FeaturesClickHandler import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.compose.engine.LocalStyleNode -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor -import dev.sargunv.maplibrecompose.core.expression.VectorValue import dev.sargunv.maplibrecompose.core.layer.LineLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor +import dev.sargunv.maplibrecompose.expressions.value.VectorValue /** * A line layer draws polylines and polygons from the [sourceLayer] in the given [source] in the @@ -38,8 +36,7 @@ import dev.sargunv.maplibrecompose.core.source.Source * this, the layer will be hidden. A value in the range of `[0..24]`. * @param filter An expression specifying conditions on source features. Only features that match * the filter are displayed. Zoom expressions in filters are only evaluated at integer zoom - * levels. The - * [featureState][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.featureState] + * levels. The [featureState][dev.sargunv.maplibrecompose.expressions.dsl.Feature.state] * expression is not supported in filter expressions. * @param visible Whether the layer should be displayed. * @param sortKey Sorts features within this layer in ascending order based on this value. Features @@ -96,7 +93,7 @@ public fun LineLayer( visible: Boolean = true, sortKey: Expression = nil(), translate: Expression = const(DpOffset.Zero), - translateAnchor: Expression> = const(TranslateAnchor.Map), + translateAnchor: Expression = const(TranslateAnchor.Map), opacity: Expression = const(1f), color: Expression = const(Color.Black), dasharray: Expression> = nil(), @@ -106,15 +103,32 @@ public fun LineLayer( width: Expression = const(1.dp), gapWidth: Expression = const(0.dp), offset: Expression = const(0.dp), - cap: Expression> = const(LineCap.Butt), - join: Expression> = const(LineJoin.Miter), + cap: Expression = const(LineCap.Butt), + join: Expression = const(LineJoin.Miter), miterLimit: Expression = const(2f), roundLimit: Expression = const(1.05f), onClick: FeaturesClickHandler? = null, onLongClick: FeaturesClickHandler? = null, ) { - val styleNode = LocalStyleNode.current - val resolvedPattern = styleNode.imageManager.resolveImages(pattern) + val compile = rememberPropertyCompiler() + + val compiledFilter = compile(filter) + val compiledSortKey = compile(sortKey) + val compiledTranslate = compile(translate) + val compiledTranslateAnchor = compile(translateAnchor) + val compiledOpacity = compile(opacity) + val compiledColor = compile(color) + val compiledDasharray = compile(dasharray) + val compiledPattern = compile(pattern) + val compiledGradient = compile(gradient) + val compiledBlur = compile(blur) + val compiledWidth = compile(width) + val compiledGapWidth = compile(gapWidth) + val compiledOffset = compile(offset) + val compiledCap = compile(cap) + val compiledJoin = compile(join) + val compiledMiterLimit = compile(miterLimit) + val compiledRoundLimit = compile(roundLimit) LayerNode( factory = { LineLayer(id = id, source = source) }, @@ -122,24 +136,24 @@ public fun LineLayer( set(sourceLayer) { layer.sourceLayer = it } set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } - set(filter) { layer.setFilter(it) } + set(compiledFilter) { layer.setFilter(it) } set(visible) { layer.visible = it } - set(cap) { layer.setLineCap(it) } - set(join) { layer.setLineJoin(it) } - set(miterLimit) { layer.setLineMiterLimit(it) } - set(roundLimit) { layer.setLineRoundLimit(it) } - set(sortKey) { layer.setLineSortKey(it) } - set(opacity) { layer.setLineOpacity(it) } - set(color) { layer.setLineColor(it) } - set(translate) { layer.setLineTranslate(it) } - set(translateAnchor) { layer.setLineTranslateAnchor(it) } - set(width) { layer.setLineWidth(it) } - set(gapWidth) { layer.setLineGapWidth(it) } - set(offset) { layer.setLineOffset(it) } - set(blur) { layer.setLineBlur(it) } - set(dasharray) { layer.setLineDasharray(it) } - set(resolvedPattern) { layer.setLinePattern(it) } - set(gradient) { layer.setLineGradient(it) } + set(compiledCap) { layer.setLineCap(it) } + set(compiledJoin) { layer.setLineJoin(it) } + set(compiledMiterLimit) { layer.setLineMiterLimit(it) } + set(compiledRoundLimit) { layer.setLineRoundLimit(it) } + set(compiledSortKey) { layer.setLineSortKey(it) } + set(compiledOpacity) { layer.setLineOpacity(it) } + set(compiledColor) { layer.setLineColor(it) } + set(compiledTranslate) { layer.setLineTranslate(it) } + set(compiledTranslateAnchor) { layer.setLineTranslateAnchor(it) } + set(compiledWidth) { layer.setLineWidth(it) } + set(compiledGapWidth) { layer.setLineGapWidth(it) } + set(compiledOffset) { layer.setLineOffset(it) } + set(compiledBlur) { layer.setLineBlur(it) } + set(compiledDasharray) { layer.setLineDasharray(it) } + set(compiledPattern) { layer.setLinePattern(it) } + set(compiledGradient) { layer.setLineGradient(it) } }, onClick = onClick, onLongClick = onLongClick, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/RasterLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/RasterLayer.kt index 49259b9b..4e704b57 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/RasterLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/RasterLayer.kt @@ -2,14 +2,13 @@ package dev.sargunv.maplibrecompose.compose.layer import androidx.compose.runtime.Composable import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.MillisecondsValue -import dev.sargunv.maplibrecompose.core.expression.RasterResampling import dev.sargunv.maplibrecompose.core.layer.RasterLayer import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.RasterResampling import kotlin.time.Duration.Companion.milliseconds /** @@ -50,23 +49,34 @@ public fun RasterLayer( brightnessMax: Expression = const(1f), saturation: Expression = const(0f), contrast: Expression = const(0f), - resampling: Expression> = const(RasterResampling.Linear), + resampling: Expression = const(RasterResampling.Linear), fadeDuration: Expression = const(300.milliseconds), ) { + val compile = rememberPropertyCompiler() + + val compiledOpacity = compile(opacity) + val compiledHueRotate = compile(hueRotate) + val compiledBrightnessMin = compile(brightnessMin) + val compiledBrightnessMax = compile(brightnessMax) + val compiledSaturation = compile(saturation) + val compiledContrast = compile(contrast) + val compiledResampling = compile(resampling) + val compiledFadeDuration = compile(fadeDuration) + LayerNode( factory = { RasterLayer(id = id, source = source) }, update = { set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } set(visible) { layer.visible = it } - set(opacity) { layer.setRasterOpacity(it) } - set(hueRotate) { layer.setRasterHueRotate(it) } - set(brightnessMin) { layer.setRasterBrightnessMin(it) } - set(brightnessMax) { layer.setRasterBrightnessMax(it) } - set(saturation) { layer.setRasterSaturation(it) } - set(contrast) { layer.setRasterContrast(it) } - set(resampling) { layer.setRasterResampling(it) } - set(fadeDuration) { layer.setRasterFadeDuration(it) } + set(compiledOpacity) { layer.setRasterOpacity(it) } + set(compiledHueRotate) { layer.setRasterHueRotate(it) } + set(compiledBrightnessMin) { layer.setRasterBrightnessMin(it) } + set(compiledBrightnessMax) { layer.setRasterBrightnessMax(it) } + set(compiledSaturation) { layer.setRasterSaturation(it) } + set(compiledContrast) { layer.setRasterContrast(it) } + set(compiledResampling) { layer.setRasterResampling(it) } + set(compiledFadeDuration) { layer.setRasterFadeDuration(it) } }, onClick = null, onLongClick = null, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/SymbolLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/SymbolLayer.kt index 6757024f..de8f06b8 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/SymbolLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/SymbolLayer.kt @@ -9,45 +9,41 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import dev.sargunv.maplibrecompose.compose.FeaturesClickHandler import dev.sargunv.maplibrecompose.compose.MaplibreComposable -import dev.sargunv.maplibrecompose.compose.engine.LocalStyleNode -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Defaults -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpPaddingValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.cast -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.dp -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.nil -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.offset -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.times -import dev.sargunv.maplibrecompose.core.expression.FloatOffsetValue -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.FormattedValue -import dev.sargunv.maplibrecompose.core.expression.IconPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.IconRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.IconTextFit -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ListValue -import dev.sargunv.maplibrecompose.core.expression.StringValue -import dev.sargunv.maplibrecompose.core.expression.SymbolAnchor -import dev.sargunv.maplibrecompose.core.expression.SymbolPlacement -import dev.sargunv.maplibrecompose.core.expression.SymbolZOrder -import dev.sargunv.maplibrecompose.core.expression.TextJustify -import dev.sargunv.maplibrecompose.core.expression.TextPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.TextRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.TextTransform -import dev.sargunv.maplibrecompose.core.expression.TextUnitOffsetValue -import dev.sargunv.maplibrecompose.core.expression.TextUnitValue -import dev.sargunv.maplibrecompose.core.expression.TextWritingMode -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor -import dev.sargunv.maplibrecompose.core.expression.ZeroPadding import dev.sargunv.maplibrecompose.core.layer.SymbolLayer import dev.sargunv.maplibrecompose.core.source.Source -import dev.sargunv.maplibrecompose.core.util.JsOnlyApi +import dev.sargunv.maplibrecompose.expressions.Defaults +import dev.sargunv.maplibrecompose.expressions.ZeroPadding +import dev.sargunv.maplibrecompose.expressions.ast.Expression +import dev.sargunv.maplibrecompose.expressions.dsl.const +import dev.sargunv.maplibrecompose.expressions.dsl.div +import dev.sargunv.maplibrecompose.expressions.dsl.nil +import dev.sargunv.maplibrecompose.expressions.dsl.offset +import dev.sargunv.maplibrecompose.expressions.dsl.times +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.IconPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconTextFit +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.SymbolPlacement +import dev.sargunv.maplibrecompose.expressions.value.SymbolZOrder +import dev.sargunv.maplibrecompose.expressions.value.TextJustify +import dev.sargunv.maplibrecompose.expressions.value.TextPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextTransform +import dev.sargunv.maplibrecompose.expressions.value.TextUnitOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextUnitValue +import dev.sargunv.maplibrecompose.expressions.value.TextWritingMode +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor /** * A symbol layer draws data from the [sourceLayer] in the given [source] as icons and/or text @@ -62,8 +58,7 @@ import dev.sargunv.maplibrecompose.core.util.JsOnlyApi * this, the layer will be hidden. A value in the range of `[0..24]`. * @param filter An expression specifying conditions on source features. Only features that match * the filter are displayed. Zoom expressions in filters are only evaluated at integer zoom - * levels. The - * [featureState][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.Feature.state] + * levels. The [featureState][dev.sargunv.maplibrecompose.expressions.dsl.Feature.state] * expression is not supported in filter expressions. * @param visible Whether the layer should be displayed. * @param sortKey Sorts features within this layer in ascending order based on this value. Features @@ -190,7 +185,7 @@ import dev.sargunv.maplibrecompose.core.util.JsOnlyApi * the plain string "My label". * * The text can also be formatted, employing different colors, fonts, etc., see - * [format][dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.format]. + * [format][dev.sargunv.maplibrecompose.expressions.dsl.format]. * * @param textOpacity The opacity at which the text will be drawn. * @@ -349,7 +344,7 @@ import dev.sargunv.maplibrecompose.core.util.JsOnlyApi * Ignored if [textField] is not specified. * * @param textOverlap Controls whether to show an icon/text when it overlaps other symbols on the - * map. See [SymbolOverlap][dev.sargunv.maplibrecompose.core.expression.SymbolOverlap]. Overrides + * map. See [SymbolOverlap][dev.sargunv.maplibrecompose.expressions.SymbolOverlap]. Overrides * [textAllowOverlap]. * * Ignored if [textField] is not specified. @@ -379,7 +374,6 @@ import dev.sargunv.maplibrecompose.core.util.JsOnlyApi * @param onClick Function to call when any feature in this layer has been clicked. * @param onLongClick Function to call when any feature in this layer has been long-clicked. */ -@OptIn(JsOnlyApi::class) @Composable @MaplibreComposable public fun SymbolLayer( @@ -391,10 +385,10 @@ public fun SymbolLayer( filter: Expression = nil(), visible: Boolean = true, sortKey: Expression = nil(), - placement: Expression> = const(SymbolPlacement.Point), + placement: Expression = const(SymbolPlacement.Point), spacing: Expression = const(250.dp), avoidEdges: Expression = const(false), - zOrder: Expression> = const(SymbolZOrder.Auto), + zOrder: Expression = const(SymbolZOrder.Auto), // icon image iconImage: Expression = nil(), @@ -408,16 +402,15 @@ public fun SymbolLayer( // icon layout iconSize: Expression = const(1f), - iconRotationAlignment: Expression> = - const(IconRotationAlignment.Auto), - iconPitchAlignment: Expression> = const(IconPitchAlignment.Auto), - iconTextFit: Expression> = const(IconTextFit.None), + iconRotationAlignment: Expression = const(IconRotationAlignment.Auto), + iconPitchAlignment: Expression = const(IconPitchAlignment.Auto), + iconTextFit: Expression = const(IconTextFit.None), iconTextFitPadding: Expression = const(ZeroPadding), iconKeepUpright: Expression = const(false), iconRotate: Expression = const(0f), // icon anchoring - iconAnchor: Expression> = const(SymbolAnchor.Center), + iconAnchor: Expression = const(SymbolAnchor.Center), iconOffset: Expression = const(DpOffset.Zero), // icon collision @@ -429,7 +422,7 @@ public fun SymbolLayer( // icon translate iconTranslate: Expression = const(DpOffset.Zero), - iconTranslateAnchor: Expression> = const(TranslateAnchor.Map), + iconTranslateAnchor: Expression = const(TranslateAnchor.Map), // text content textField: Expression = const("").cast(), @@ -444,25 +437,24 @@ public fun SymbolLayer( // text glyph properties textFont: Expression> = Defaults.FontNames, textSize: Expression = const(1.em), - textTransform: Expression> = const(TextTransform.None), + textTransform: Expression = const(TextTransform.None), textLetterSpacing: Expression = const(0f.em), - textRotationAlignment: Expression> = - const(TextRotationAlignment.Auto), - textPitchAlignment: Expression> = const(TextPitchAlignment.Auto), + textRotationAlignment: Expression = const(TextRotationAlignment.Auto), + textPitchAlignment: Expression = const(TextPitchAlignment.Auto), textMaxAngle: Expression = const(45f), // text paragraph layout textMaxWidth: Expression = const(10f.em), textLineHeight: Expression = const(1.2f.em), - textJustify: Expression> = const(TextJustify.Center), - textWritingMode: Expression>> = nil(), + textJustify: Expression = const(TextJustify.Center), + textWritingMode: Expression> = nil(), textKeepUpright: Expression = const(true), textRotate: Expression = const(0f), // text anchoring - textAnchor: Expression> = const(SymbolAnchor.Center), + textAnchor: Expression = const(SymbolAnchor.Center), textOffset: Expression = offset(0f.em, 0f.em), - textVariableAnchor: Expression>> = nil(), + textVariableAnchor: Expression> = nil(), textRadialOffset: Expression = const(0f.em), // textVariableAnchorOffset: Expression = nil(), textVariableAnchorOffset: Expression = nil(), @@ -476,29 +468,81 @@ public fun SymbolLayer( // text translate textTranslate: Expression = const(DpOffset.Zero), - textTranslateAnchor: Expression> = const(TranslateAnchor.Map), + textTranslateAnchor: Expression = const(TranslateAnchor.Map), onClick: FeaturesClickHandler? = null, onLongClick: FeaturesClickHandler? = null, ) { - val textSizeSp = textSize.rememberTextUnitsAsSp(const(16f), 1f.em).cast() - val textLetterSpacingEm = - textLetterSpacing.rememberTextUnitsAsEm(textSizeSp, 0f.em).cast() - val textMaxWidthEm = textMaxWidth.rememberTextUnitsAsEm(textSizeSp, 10f.em).cast() - val textLineHeightEm = - textLineHeight.rememberTextUnitsAsEm(textSizeSp, 1.2f.em).cast() - val textRadialOffsetEm = - textRadialOffset.rememberTextUnitsAsEm(textSizeSp, 0f.em).cast() - val textOffsetEm = textOffset.rememberTextUnitsAsEm(textSizeSp, 0f.em).cast() - val textFieldEm = textField.rememberTextUnitsAsEm(textSizeSp, 1f.em).cast() - // used for scaling textSize from sp (api) to dp (core) - // TODO needs changes after https://github.com/maplibre/maplibre-native/issues/3057 - val fontScale = LocalDensity.current.fontScale - val textSizeDp = remember(textSizeSp, fontScale) { (textSizeSp * const(fontScale)).dp } + // needs changes after https://github.com/maplibre/maplibre-native/issues/3057 + val dpPerSp = LocalDensity.current.fontScale.dp + val compileTextSize = rememberPropertyCompiler(emScale = const(16f), spScale = const(1f)) + val textSizeSp = compileTextSize(textSize.cast()) + val textSizeDp = remember(textSizeSp, dpPerSp) { textSizeSp * const(dpPerSp) } + + // compiles TextUnit as EMs + val compile = rememberPropertyCompiler(emScale = const(1f), spScale = const(1f) / textSizeSp) + + val compiledFilter = compile(filter) + val compiledSortKey = compile(sortKey) + val compiledSpacing = compile(spacing) + val compiledAvoidEdges = compile(avoidEdges) + val compiledZOrder = compile(zOrder) + val compiledPlacement = compile(placement) + + val compiledIconImage = compile(iconImage) + val compiledIconOpacity = compile(iconOpacity) + val compiledIconColor = compile(iconColor) + val compiledIconHaloColor = compile(iconHaloColor) + val compiledIconHaloWidth = compile(iconHaloWidth) + val compiledIconHaloBlur = compile(iconHaloBlur) + val compiledIconSize = compile(iconSize) + val compiledIconRotationAlignment = compile(iconRotationAlignment) + val compiledIconPitchAlignment = compile(iconPitchAlignment) + val compiledIconTextFit = compile(iconTextFit) + val compiledIconTextFitPadding = compile(iconTextFitPadding) + val compiledIconKeepUpright = compile(iconKeepUpright) + val compiledIconRotate = compile(iconRotate) + val compiledIconAnchor = compile(iconAnchor) + val compiledIconOffset = compile(iconOffset) + val compiledIconPadding = compile(iconPadding) + val compiledIconAllowOverlap = compile(iconAllowOverlap) + val compiledIconOverlap = compile(iconOverlap) + val compiledIconIgnorePlacement = compile(iconIgnorePlacement) + val compiledIconOptional = compile(iconOptional) + val compiledIconTranslate = compile(iconTranslate) + val compiledIconTranslateAnchor = compile(iconTranslateAnchor) - val node = LocalStyleNode.current - val resolvedIconImage = node.imageManager.resolveImages(iconImage) - val resolvedTextField = node.imageManager.resolveImages(textFieldEm) + val compiledTextField = compile(textField) + val compiledTextOpacity = compile(textOpacity) + val compiledTextColor = compile(textColor) + val compiledTextHaloColor = compile(textHaloColor) + val compiledTextHaloWidth = compile(textHaloWidth) + val compiledTextHaloBlur = compile(textHaloBlur) + val compiledTextFont = compile(textFont) + val compiledTextSizeDp = compile(textSizeDp) + val compiledTextTransform = compile(textTransform) + val compiledTextLetterSpacing = compile(textLetterSpacing) + val compiledTextRotationAlignment = compile(textRotationAlignment) + val compiledTextPitchAlignment = compile(textPitchAlignment) + val compiledTextMaxAngle = compile(textMaxAngle) + val compiledTextMaxWidth = compile(textMaxWidth) + val compiledTextLineHeight = compile(textLineHeight) + val compiledTextJustify = compile(textJustify) + val compiledTextWritingMode = compile(textWritingMode) + val compiledTextKeepUpright = compile(textKeepUpright) + val compiledTextRotate = compile(textRotate) + val compiledTextAnchor = compile(textAnchor) + val compiledTextOffset = compile(textOffset) + val compiledTextVariableAnchor = compile(textVariableAnchor) + val compiledTextRadialOffset = compile(textRadialOffset) + val compiledTextVariableAnchorOffset = compile(textVariableAnchorOffset) + val compiledTextPadding = compile(textPadding) + val compiledTextAllowOverlap = compile(textAllowOverlap) + val compiledTextOverlap = compile(textOverlap) + val compiledTextIgnorePlacement = compile(textIgnorePlacement) + val compiledTextOptional = compile(textOptional) + val compiledTextTranslate = compile(textTranslate) + val compiledTextTranslateAnchor = compile(textTranslateAnchor) LayerNode( factory = { SymbolLayer(id = id, source = source) }, @@ -506,68 +550,68 @@ public fun SymbolLayer( set(sourceLayer) { layer.sourceLayer = it } set(minZoom) { layer.minZoom = it } set(maxZoom) { layer.maxZoom = it } - set(filter) { layer.setFilter(it) } + set(compiledFilter) { layer.setFilter(it) } set(visible) { layer.visible = it } - set(placement) { layer.setSymbolPlacement(it) } - set(spacing) { layer.setSymbolSpacing(it) } - set(avoidEdges) { layer.setSymbolAvoidEdges(it) } - set(sortKey) { layer.setSymbolSortKey(it) } - set(zOrder) { layer.setSymbolZOrder(it) } + set(compiledPlacement) { layer.setSymbolPlacement(it) } + set(compiledSpacing) { layer.setSymbolSpacing(it) } + set(compiledAvoidEdges) { layer.setSymbolAvoidEdges(it) } + set(compiledSortKey) { layer.setSymbolSortKey(it) } + set(compiledZOrder) { layer.setSymbolZOrder(it) } - set(iconAllowOverlap) { layer.setIconAllowOverlap(it) } - set(iconOverlap) { layer.setIconOverlap(it) } - set(iconIgnorePlacement) { layer.setIconIgnorePlacement(it) } - set(iconOptional) { layer.setIconOptional(it) } - set(iconRotationAlignment) { layer.setIconRotationAlignment(it) } - set(iconSize) { layer.setIconSize(it) } - set(iconTextFit) { layer.setIconTextFit(it) } - set(iconTextFitPadding) { layer.setIconTextFitPadding(it) } - set(resolvedIconImage) { layer.setIconImage(it) } - set(iconRotate) { layer.setIconRotate(it) } - set(iconPadding) { layer.setIconPadding(it) } - set(iconKeepUpright) { layer.setIconKeepUpright(it) } - set(iconOffset) { layer.setIconOffset(it) } - set(iconAnchor) { layer.setIconAnchor(it) } - set(iconPitchAlignment) { layer.setIconPitchAlignment(it) } - set(iconOpacity) { layer.setIconOpacity(it) } - set(iconColor) { layer.setIconColor(it) } - set(iconHaloColor) { layer.setIconHaloColor(it) } - set(iconHaloWidth) { layer.setIconHaloWidth(it) } - set(iconHaloBlur) { layer.setIconHaloBlur(it) } - set(iconTranslate) { layer.setIconTranslate(it) } - set(iconTranslateAnchor) { layer.setIconTranslateAnchor(it) } + set(compiledIconAllowOverlap) { layer.setIconAllowOverlap(it) } + set(compiledIconOverlap) { layer.setIconOverlap(it) } + set(compiledIconIgnorePlacement) { layer.setIconIgnorePlacement(it) } + set(compiledIconOptional) { layer.setIconOptional(it) } + set(compiledIconRotationAlignment) { layer.setIconRotationAlignment(it) } + set(compiledIconSize) { layer.setIconSize(it) } + set(compiledIconTextFit) { layer.setIconTextFit(it) } + set(compiledIconTextFitPadding) { layer.setIconTextFitPadding(it) } + set(compiledIconImage) { layer.setIconImage(it) } + set(compiledIconRotate) { layer.setIconRotate(it) } + set(compiledIconPadding) { layer.setIconPadding(it) } + set(compiledIconKeepUpright) { layer.setIconKeepUpright(it) } + set(compiledIconOffset) { layer.setIconOffset(it) } + set(compiledIconAnchor) { layer.setIconAnchor(it) } + set(compiledIconPitchAlignment) { layer.setIconPitchAlignment(it) } + set(compiledIconOpacity) { layer.setIconOpacity(it) } + set(compiledIconColor) { layer.setIconColor(it) } + set(compiledIconHaloColor) { layer.setIconHaloColor(it) } + set(compiledIconHaloWidth) { layer.setIconHaloWidth(it) } + set(compiledIconHaloBlur) { layer.setIconHaloBlur(it) } + set(compiledIconTranslate) { layer.setIconTranslate(it) } + set(compiledIconTranslateAnchor) { layer.setIconTranslateAnchor(it) } - set(textPitchAlignment) { layer.setTextPitchAlignment(it) } - set(textRotationAlignment) { layer.setTextRotationAlignment(it) } - set(resolvedTextField) { layer.setTextField(it) } - set(textFont) { layer.setTextFont(it) } - set(textSizeDp) { layer.setTextSize(it) } - set(textMaxWidthEm) { layer.setTextMaxWidth(it) } - set(textLineHeightEm) { layer.setTextLineHeight(it) } - set(textLetterSpacingEm) { layer.setTextLetterSpacing(it) } - set(textJustify) { layer.setTextJustify(it) } - set(textRadialOffsetEm) { layer.setTextRadialOffset(it) } - set(textVariableAnchor) { layer.setTextVariableAnchor(it) } - set(textVariableAnchorOffset) { layer.setTextVariableAnchorOffset(it) } - set(textAnchor) { layer.setTextAnchor(it) } - set(textMaxAngle) { layer.setTextMaxAngle(it) } - set(textWritingMode) { layer.setTextWritingMode(it) } - set(textRotate) { layer.setTextRotate(it) } - set(textPadding) { layer.setTextPadding(it) } - set(textKeepUpright) { layer.setTextKeepUpright(it) } - set(textTransform) { layer.setTextTransform(it) } - set(textOffsetEm) { layer.setTextOffset(it) } - set(textAllowOverlap) { layer.setTextAllowOverlap(it) } - set(textOverlap) { layer.setTextOverlap(it) } - set(textIgnorePlacement) { layer.setTextIgnorePlacement(it) } - set(textOptional) { layer.setTextOptional(it) } - set(textOpacity) { layer.setTextOpacity(it) } - set(textColor) { layer.setTextColor(it) } - set(textHaloColor) { layer.setTextHaloColor(it) } - set(textHaloWidth) { layer.setTextHaloWidth(it) } - set(textHaloBlur) { layer.setTextHaloBlur(it) } - set(textTranslate) { layer.setTextTranslate(it) } - set(textTranslateAnchor) { layer.setTextTranslateAnchor(it) } + set(compiledTextPitchAlignment) { layer.setTextPitchAlignment(it) } + set(compiledTextRotationAlignment) { layer.setTextRotationAlignment(it) } + set(compiledTextField) { layer.setTextField(it) } + set(compiledTextFont) { layer.setTextFont(it) } + set(compiledTextSizeDp) { layer.setTextSize(it) } + set(compiledTextMaxWidth.cast()) { layer.setTextMaxWidth(it) } + set(compiledTextLineHeight.cast()) { layer.setTextLineHeight(it) } + set(compiledTextLetterSpacing.cast()) { layer.setTextLetterSpacing(it) } + set(compiledTextJustify) { layer.setTextJustify(it) } + set(compiledTextRadialOffset.cast()) { layer.setTextRadialOffset(it) } + set(compiledTextVariableAnchor) { layer.setTextVariableAnchor(it) } + set(compiledTextVariableAnchorOffset) { layer.setTextVariableAnchorOffset(it) } + set(compiledTextAnchor) { layer.setTextAnchor(it) } + set(compiledTextMaxAngle) { layer.setTextMaxAngle(it) } + set(compiledTextWritingMode) { layer.setTextWritingMode(it) } + set(compiledTextRotate) { layer.setTextRotate(it) } + set(compiledTextPadding) { layer.setTextPadding(it) } + set(compiledTextKeepUpright) { layer.setTextKeepUpright(it) } + set(compiledTextTransform) { layer.setTextTransform(it) } + set(compiledTextOffset.cast()) { layer.setTextOffset(it) } + set(compiledTextAllowOverlap) { layer.setTextAllowOverlap(it) } + set(compiledTextOverlap) { layer.setTextOverlap(it) } + set(compiledTextIgnorePlacement) { layer.setTextIgnorePlacement(it) } + set(compiledTextOptional) { layer.setTextOptional(it) } + set(compiledTextOpacity) { layer.setTextOpacity(it) } + set(compiledTextColor) { layer.setTextColor(it) } + set(compiledTextHaloColor) { layer.setTextHaloColor(it) } + set(compiledTextHaloWidth) { layer.setTextHaloWidth(it) } + set(compiledTextHaloBlur) { layer.setTextHaloBlur(it) } + set(compiledTextTranslate) { layer.setTextTranslate(it) } + set(compiledTextTranslateAnchor) { layer.setTextTranslateAnchor(it) } }, onClick = onClick, onLongClick = onLongClick, diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/util.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/util.kt deleted file mode 100644 index ae9be4e8..00000000 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/compose/layer/util.kt +++ /dev/null @@ -1,36 +0,0 @@ -package dev.sargunv.maplibrecompose.compose.layer - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.unit.TextUnit -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.const -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.div -import dev.sargunv.maplibrecompose.core.expression.ExpressionsDsl.resolveTextUnits -import dev.sargunv.maplibrecompose.core.expression.FloatValue - -@Composable -internal fun Expression<*>.rememberTextUnitsAsSp( - spPerEm: Expression, - unspecified: TextUnit, -) = - remember(this, spPerEm, unspecified) { - resolveTextUnits( - spMultiplier = const(1f), - emMultiplier = spPerEm, - unspecifiedValue = const(unspecified), - ) - } - -@Composable -internal fun Expression<*>.rememberTextUnitsAsEm( - spPerEm: Expression, - unspecified: TextUnit, -) = - remember(this, spPerEm, unspecified) { - resolveTextUnits( - spMultiplier = const(1f) / spPerEm, - emMultiplier = const(1f), - unspecifiedValue = const(unspecified), - ) - } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt index c9ff6602..c173b96b 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/MaplibreMap.kt @@ -2,8 +2,8 @@ package dev.sargunv.maplibrecompose.core import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpRect -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position @@ -34,13 +34,13 @@ internal interface MaplibreMap { fun queryRenderedFeatures( offset: DpOffset, layerIds: Set? = null, - predicate: Expression? = null, + predicate: CompiledExpression? = null, ): List fun queryRenderedFeatures( rect: DpRect, layerIds: Set? = null, - predicate: Expression? = null, + predicate: CompiledExpression? = null, ): List fun metersPerDpAtLatitude(latitude: Double): Double diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/Expression.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/Expression.kt deleted file mode 100644 index d173d445..00000000 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/Expression.kt +++ /dev/null @@ -1,98 +0,0 @@ -package dev.sargunv.maplibrecompose.core.expression - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.painter.Painter - -/** Wraps a JSON-like value that represents an expression, typically used for styling map layers. */ -public data class Expression<@Suppress("unused") out T : ExpressionValue> -private constructor( - /** The JSON-like value that backs this expression. */ - public val value: Any? -) { - internal fun mapLeaves(transform: (Any?) -> Any?): Expression<*> { - fun mapLeavesImpl(value: Any?, transform: (Any?) -> Any?): Any? { - return when (value) { - is Map<*, *> -> value.mapValues { (_, v) -> mapLeavesImpl(v, transform) } - is List<*> -> value.map { v -> mapLeavesImpl(v, transform) } - else -> transform(value) - } - } - - return Expression(mapLeavesImpl(value, transform)) - } - - internal fun visitLeaves(action: (Any?) -> Unit) { - fun visitLeavesImpl(value: Any?) { - when (value) { - is Map<*, *> -> value.values.forEach { visitLeavesImpl(it) } - is List<*> -> value.forEach { visitLeavesImpl(it) } - else -> action(value) - } - } - visitLeavesImpl(value) - } - - internal companion object { - private const val NUM_SMALL_NUMBERS = 512 - private const val SMALL_FLOAT_RESOLUTION = 0.05f - - private val constSmallInts = Array(NUM_SMALL_NUMBERS) { Expression(it) } - private val constSmallFloats = - Array(NUM_SMALL_NUMBERS) { Expression(it.toFloat() / 20f) } - private val ofEmptyString = Expression("") - private val ofTransparent = Expression(Color.Transparent) - private val ofBlack = Expression(Color.Black) - private val ofWhite = Expression(Color.White) - private val ofEmptyMap = Expression>(emptyMap()) - private val ofEmptyList = Expression>(emptyList()) - private val ofZeroOffset = Expression(Offset.Zero) - private val ofZeroPadding = Expression(ZeroPadding) - - val ofNull = Expression(null) - val ofTrue = Expression(true) - val ofFalse = Expression(false) - - private fun Float.isSmallInt() = toInt().toFloat() == this && toInt().isSmallInt() - - private fun Int.isSmallInt() = this in 0.. constSmallInts[float.toInt()] - (float / SMALL_FLOAT_RESOLUTION).isSmallInt() -> - constSmallFloats[(float / SMALL_FLOAT_RESOLUTION).toInt()] - - else -> Expression(float) - } - - fun ofInt(int: Int) = if (int.isSmallInt()) constSmallInts[int] else Expression(int) - - fun ofString(string: String) = if (string.isEmpty()) ofEmptyString else Expression(string) - - fun ofColor(color: Color) = - when (color) { - Color.Transparent -> ofTransparent - Color.Black -> ofBlack - Color.White -> ofWhite - else -> Expression(color) - } - - fun ofMap(map: Map>): Expression> = - if (map.isEmpty()) ofEmptyMap else Expression(map.mapValues { it.value.value }) - - fun ofList(list: List<*>): Expression> = - if (list.isEmpty()) ofEmptyList else Expression(list) - - fun ofOffset(offset: Offset) = if (offset == Offset.Zero) ofZeroOffset else Expression(offset) - - fun ofPadding(padding: PaddingValues.Absolute) = - if (padding == ZeroPadding) ofZeroPadding else Expression(padding) - - fun ofBitmap(bitmap: ImageBitmap): Expression = Expression(listOf("image", bitmap)) - - fun ofPainter(painter: Painter): Expression = Expression(listOf("image", painter)) - } -} diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/ExpressionValue.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/ExpressionValue.kt deleted file mode 100644 index b60e3951..00000000 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/ExpressionValue.kt +++ /dev/null @@ -1,577 +0,0 @@ -package dev.sargunv.maplibrecompose.core.expression - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.TextUnit -import kotlin.time.Duration - -/** - * Represents a value that an [Expression] can resolve to. These types are never actually - * instantiated at runtime; they're only used as type parameters to hint at the type of an - * [Expression] - */ -public sealed interface ExpressionValue - -/** - * Represents an [Expression] that resolves to a true or false value. See [ExpressionsDsl.const]. - */ -public sealed interface BooleanValue : ExpressionValue, EquatableValue - -/** - * Represents an [Expression] that resolves to a numeric quantity. Corresponds to numbers in the - * JSON style spec. Use [ExpressionsDsl.const] to create a literal [NumberValue]. - * - * @param U the unit type of the number. For dimensionless quantities, use [Number]. - */ -public sealed interface NumberValue : - ExpressionValue, - MatchableValue, - InterpolateableValue, - ComparableValue>, - EquatableValue - -/** - * Represents an [Expression] that resolves to a dimensionless quantity. See [ExpressionsDsl.const]. - */ -public typealias FloatValue = NumberValue - -/** - * Represents an [Expression] that resolves to an integer dimensionless quantity. See - * [ExpressionsDsl.const]. - */ -public sealed interface IntValue : NumberValue - -/** - * Represents an [Expression] that resolves to device-independent pixels ([Dp]). See - * [ExpressionsDsl.const]. - */ -public typealias DpValue = NumberValue - -/** - * Represents an [Expression] that resolves to scalable pixels or em ([TextUnit]). See - * [ExpressionsDsl.const]. - * - * Which unit it resolves to is determined by the style property it's used in. - */ -public typealias TextUnitValue = NumberValue - -/** - * Represents an [Expression] that resolves to an amount of time with millisecond precision - * ([Duration]). See [ExpressionsDsl.const]. - */ -public typealias MillisecondsValue = NumberValue - -/** Represents an [Expression] that resolves to a string value. See [ExpressionsDsl.const]. */ -public sealed interface StringValue : - ExpressionValue, - MatchableValue, - ComparableValue, - EquatableValue, - FormattableValue, - FormattedValue - -/** - * Represents an [Expression] that resolves to an enum string. See [ExpressionsDsl.const]. - * - * @param T The [EnumValue] descendent type that this value represents. - */ -public sealed interface EnumValue : StringValue { - /** The string expression representing this enum value. You probably don't need this. */ - public val stringConst: Expression -} - -/** Represents an [Expression] that resolves to a [Color] value. See [ExpressionsDsl.const]. */ -public sealed interface ColorValue : ExpressionValue, InterpolateableValue - -/** - * Represents an [Expression] that resolves to a map value (corresponds to a JSON object). See - * [ExpressionsDsl.const]. - */ -public sealed interface MapValue : ExpressionValue - -/** - * Represents an [Expression] that resolves to a list value (corresponds to a JSON array). See - * [ExpressionsDsl.const]. - */ -public sealed interface ListValue : ExpressionValue - -/** - * Represents an [Expression] that resolves to a list value (corresponds to a JSON array) of - * alternating types. - */ -public sealed interface AlternatingListValue : - ListValue - -/** - * Represents an [Expression] that resolves to an alternating list of [SymbolAnchor] and - * [FloatOffsetValue]. - * - * See [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]. - */ -public typealias TextVariableAnchorOffsetValue = - AlternatingListValue - -/** - * Represents an [Expression] that resolves to a list of numbers. - * - * @param U the unit type of the number. For dimensionless quantities, use [Number]. - */ -public sealed interface VectorValue : - ListValue>, InterpolateableValue> - -/** - * Represents an [Expression] that reoslves to a 2D vector in some unit. - * - * @param U the unit type of the offset. For dimensionless quantities, use [Number]. - */ -public sealed interface OffsetValue : VectorValue - -/** - * Represents an [Expression] that resolves to a 2D floating point offset without a particular unit. - * ([Offset]). See [ExpressionsDsl.offset]. - */ -public typealias FloatOffsetValue = OffsetValue - -/** - * Represents an [Expression] that resolves to a 2D floating point offset in device-independent - * pixels ([DpOffset]). See [ExpressionsDsl.offset]. - */ -public typealias DpOffsetValue = OffsetValue - -/** - * Represents an [Expression] that resolves to a 2D floating point offset in scalable pixels or em - * ([TextUnit]). See [ExpressionsDsl.offset]. - */ -public typealias TextUnitOffsetValue = OffsetValue - -/** - * Represents an [Expression] that resolves to an absolute (layout direction unaware) padding - * applied along the edges inside a box ([PaddingValues.Absolute]). See [ExpressionsDsl.const]. - */ -public sealed interface DpPaddingValue : VectorValue - -/** - * Represents an [Expression] that resolves to a collator object for use in locale-dependent - * comparison operations. See [ExpressionsDsl.collator]. - */ -public sealed interface CollatorValue : ExpressionValue - -/** Represents an [Expression] that resolves to a formatted string. See [ExpressionsDsl.format]. */ -public sealed interface FormattedValue : ExpressionValue - -/** Represents an [Expression] that resolves to a geometry object. */ -public sealed interface GeoJsonValue : ExpressionValue - -/** Represents an [Expression] that resolves to an image. See [ExpressionsDsl.image] */ -public sealed interface ImageValue : ExpressionValue, FormattableValue - -/** - * Represents an [Expression] that resolves to an interpolation type. See [ExpressionsDsl.linear], - * [ExpressionsDsl.exponential], and [ExpressionsDsl.cubicBezier]. - */ -public sealed interface InterpolationValue : ExpressionValue - -/** - * Represents and [Expression] that resolves to a value that can be an input to - * [ExpressionsDsl.format]. - */ -public sealed interface FormattableValue : ExpressionValue - -/** - * Represents an [Expression] that resolves to a value that can be compared for equality. See - * [ExpressionsDsl.eq] and [ExpressionsDsl.neq]. - */ -public sealed interface EquatableValue : ExpressionValue - -/** - * Union type for an [Expression] that resolves to a value that can be matched. See - * [ExpressionsDsl.switch]. - */ -public sealed interface MatchableValue : ExpressionValue - -/** - * Union type for an [Expression] that resolves to a value that can be ordered with other values of - * its type. See [ExpressionsDsl.gt], [ExpressionsDsl.lt], [ExpressionsDsl.gte], and - * [ExpressionsDsl.lte]. - * - * @param T the type of the value that can be compared against for ordering. - */ -public sealed interface ComparableValue : ExpressionValue - -/** - * Union type for an [Expression] that resolves to a value that can be interpolated. See - * [ExpressionsDsl.interpolate]. - * - * @param T the type of values that can be interpolated between. - */ -public sealed interface InterpolateableValue : ExpressionValue - -internal sealed interface ResolvedValue : ExpressionValue - -/** The type of value resolved from an expression, as returned by [ExpressionsDsl.type]. */ -public enum class ExpressionType(override val stringConst: Expression) : - EnumValue { - Number(Expression.ofString("number")), - String(Expression.ofString("string")), - Object(Expression.ofString("object")), - Boolean(Expression.ofString("boolean")), - Color(Expression.ofString("color")), - Array(Expression.ofString("array")), -} - -/** Type of a GeoJson feature, as returned by [ExpressionsDsl.geometryType]. */ -public enum class GeometryType(override val stringConst: Expression) : - EnumValue { - Point(Expression.ofString("Point")), - LineString(Expression.ofString("LineString")), - Polygon(Expression.ofString("Polygon")), - MultiPoint(Expression.ofString("MultiPoint")), - MultiLineString(Expression.ofString("MultiLineString")), - MultiPolygon(Expression.ofString("MultiPolygon")), -} - -/** Frame of reference for offsetting geometry. */ -public enum class TranslateAnchor(override val stringConst: Expression) : - EnumValue { - /** Offset is relative to the map */ - Map(Expression.ofString("map")), - - /** Offset is relative to the viewport */ - Viewport(Expression.ofString("viewport")), -} - -/** Scaling behavior of circles when the map is pitched. */ -public enum class CirclePitchScale(override val stringConst: Expression) : - EnumValue { - /** - * Circles are scaled according to their apparent distance to the camera, i.e. as if they are on - * the map. - */ - Map(Expression.ofString("map")), - - /** Circles are not scaled, i.e. as if glued to the viewport. */ - Viewport(Expression.ofString("viewport")), -} - -/** Orientation of circles when the map is pitched. */ -public enum class CirclePitchAlignment(override val stringConst: Expression) : - EnumValue { - /** Circles are aligned to the plane of the map, i.e. flat on top of the map. */ - Map(Expression.ofString("map")), - - /** Circles are aligned to the plane of the viewport, i.e. facing the camera. */ - Viewport(Expression.ofString("viewport")), -} - -/** Direction of light source when map is rotated. */ -public enum class IlluminationAnchor(override val stringConst: Expression) : - EnumValue { - - /** The hillshade illumination is relative to the north direction. */ - Map(Expression.ofString("map")), - - /** The hillshade illumination is relative to the top of the viewport. */ - Viewport(Expression.ofString("viewport")), -} - -/** Display of joined lines */ -public enum class LineJoin(override val stringConst: Expression) : - EnumValue { - /** - * A join with a squared-off end which is drawn beyond the endpoint of the line at a distance of - * one-half of the line's width. - */ - Bevel(Expression.ofString("bevel")), - - /** - * A join with a rounded end which is drawn beyond the endpoint of the line at a radius of - * one-half of the line's width and centered on the endpoint of the line. - */ - Round(Expression.ofString("round")), - - /** - * A join with a sharp, angled corner which is drawn with the outer sides beyond the endpoint of - * the path until they meet. - */ - Miter(Expression.ofString("miter")), -} - -/** Display of line endings */ -public enum class LineCap(override val stringConst: Expression) : EnumValue { - /** A cap with a squared-off end which is drawn to the exact endpoint of the line. */ - Butt(Expression.ofString("butt")), - - /** - * A cap with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half - * of the line's width and centered on the endpoint of the line. - */ - Round(Expression.ofString("round")), - - /** - * A cap with a squared-off end which is drawn beyond the endpoint of the line at a distance of - * one-half of the line's width. - */ - Square(Expression.ofString("square")), -} - -/** - * The resampling/interpolation method to use for overscaling, also known as texture magnification - * filter - */ -public enum class RasterResampling(override val stringConst: Expression) : - EnumValue { - /** - * (Bi)linear filtering interpolates pixel values using the weighted average of the four closest - * original source pixels creating a smooth but blurry look when overscaled - */ - Linear(Expression.ofString("linear")), - - /** - * Nearest neighbor filtering interpolates pixel values using the nearest original source pixel - * creating a sharp but pixelated look when overscaled - */ - Nearest(Expression.ofString("nearest")), -} - -/** Symbol placement relative to its geometry. */ -public enum class SymbolPlacement(override val stringConst: Expression) : - EnumValue { - /** The label is placed at the point where the geometry is located. */ - Point(Expression.ofString("point")), - - /** - * The label is placed along the line of the geometry. Can only be used on LineString and Polygon - * geometries. - */ - Line(Expression.ofString("line")), - - /** - * The label is placed at the center of the line of the geometry. Can only be used on LineString - * and Polygon geometries. Note that a single feature in a vector tile may contain multiple line - * geometries. - */ - LineCenter(Expression.ofString("line-center")), -} - -/** - * Determines whether overlapping symbols in the same layer are rendered in the order that they - * appear in the data source or by their y-position relative to the viewport. To control the order - * and prioritization of symbols otherwise, use `sortKey`. - */ -public enum class SymbolZOrder(override val stringConst: Expression) : - EnumValue { - /** - * Sorts symbols by `sortKey` if set. Otherwise, sorts symbols by their y-position relative to the - * viewport if `iconAllowOverlap` or `textAllowOverlap` is set to `true` or `iconIgnorePlacement` - * or `textIgnorePlacement` is `false`. - */ - Auto(Expression.ofString("auto")), - - /** - * Sorts symbols by their y-position relative to the viewport if `iconAllowOverlap` or - * `textAllowOverlap` is set to `true` or `iconIgnorePlacement` or `textIgnorePlacement` is - * `false`. - */ - ViewportY(Expression.ofString("viewport-y")), - - /** - * Sorts symbols by `sortKey` if set. Otherwise, no sorting is applied; symbols are rendered in - * the same order as the source data. - */ - Source(Expression.ofString("source")), -} - -/** Part of the icon/text placed closest to the anchor. */ -public enum class SymbolAnchor(override val stringConst: Expression) : - EnumValue { - /** The center of the icon is placed closest to the anchor. */ - Center(Expression.ofString("center")), - - /** The left side of the icon is placed closest to the anchor. */ - Left(Expression.ofString("left")), - - /** The right side of the icon is placed closest to the anchor. */ - Right(Expression.ofString("right")), - - /** The top of the icon is placed closest to the anchor. */ - Top(Expression.ofString("top")), - - /** The bottom of the icon is placed closest to the anchor. */ - Bottom(Expression.ofString("bottom")), - - /** The top left corner of the icon is placed closest to the anchor. */ - TopLeft(Expression.ofString("top-left")), - - /** The top right corner of the icon is placed closest to the anchor. */ - TopRight(Expression.ofString("top-right")), - - /** The bottom left corner of the icon is placed closest to the anchor. */ - BottomLeft(Expression.ofString("bottom-left")), - - /** The bottom right corner of the icon is placed closest to the anchor. */ - BottomRight(Expression.ofString("bottom-right")), -} - -/** Controls whether to show an icon/text when it overlaps other symbols on the map. */ -public enum class SymbolOverlap(override val stringConst: Expression) : - EnumValue { - /** The icon/text will be hidden if it collides with any other previously drawn symbol. */ - Never(Expression.ofString("never")), - - /** The icon/text will be visible even if it collides with any other previously drawn symbol. */ - Always(Expression.ofString("always")), - - /** - * If the icon/text collides with another previously drawn symbol, the overlap mode for that - * symbol is checked. If the previous symbol was placed using never overlap mode, the new - * icon/text is hidden. If the previous symbol was placed using always or cooperative overlap - * mode, the new icon/text is visible. - */ - Cooperative(Expression.ofString("cooperative")), -} - -/** In combination with [SymbolPlacement], determines the rotation behavior of icons. */ -public enum class IconRotationAlignment(override val stringConst: Expression) : - EnumValue { - /** - * For [SymbolPlacement.Point], aligns icons east-west. Otherwise, aligns icon x-axes with the - * line. - */ - Map(Expression.ofString("map")), - - /** - * Produces icons whose x-axes are aligned with the x-axis of the viewport, regardless of the - * [SymbolPlacement]. - */ - Viewport(Expression.ofString("viewport")), - - /** - * For [SymbolPlacement.Point], this is equivalent to [IconRotationAlignment.Viewport]. Otherwise, - * this is equivalent to [IconRotationAlignment.Map]. - */ - Auto(Expression.ofString("auto")), -} - -/** Scales the icon to fit around the associated text. */ -public enum class IconTextFit(override val stringConst: Expression) : - EnumValue { - /** The icon is displayed at its intrinsic aspect ratio. */ - None(Expression.ofString("none")), - - /** The icon is scaled in the x-dimension to fit the width of the text. */ - Width(Expression.ofString("width")), - - /** The icon is scaled in the y-dimension to fit the height of the text. */ - Height(Expression.ofString("height")), - - /** The icon is scaled in both x- and y-dimensions. */ - Both(Expression.ofString("both")), -} - -/** Orientation of icon when map is pitched. */ -public enum class IconPitchAlignment(override val stringConst: Expression) : - EnumValue { - /** The icon is aligned to the plane of the map. */ - Map(Expression.ofString("map")), - - /** The icon is aligned to the plane of the viewport, i.e. as if glued to the screen */ - Viewport(Expression.ofString("viewport")), - - /** Automatically matches the value of [IconRotationAlignment] */ - Auto(Expression.ofString("auto")), -} - -/** Orientation of text when map is pitched. */ -public enum class TextPitchAlignment(override val stringConst: Expression) : - EnumValue { - /** The text is aligned to the plane of the map. */ - Map(Expression.ofString("map")), - - /** The text is aligned to the plane of the viewport, i.e. as if glued to the screen */ - Viewport(Expression.ofString("viewport")), - - /** Automatically matches the value of [TextRotationAlignment] */ - Auto(Expression.ofString("auto")), -} - -/** - * In combination with [SymbolPlacement], determines the rotation behavior of the individual glyphs - * forming the text. - */ -public enum class TextRotationAlignment(override val stringConst: Expression) : - EnumValue { - /** - * For [SymbolPlacement.Point], aligns text east-west. Otherwise, aligns text x-axes with the - * line. - */ - Map(Expression.ofString("map")), - - /** - * Produces glyphs whose x-axes are aligned with the x-axis of the viewport, regardless of the - * [SymbolPlacement]. - */ - Viewport(Expression.ofString("viewport")), - - /** - * For [SymbolPlacement.Point], this is equivalent to [TextRotationAlignment.Viewport]. Otherwise, - * aligns glyphs to the x-axis of the viewport and places them along the line. - * - * **Note**: This value not supported on native platforms yet, see - * [maplibre-native#250](https://github.com/maplibre/maplibre-native/issues/250)** - */ - ViewportGlyph(Expression.ofString("viewport-glyph")), - - /** - * For [SymbolPlacement.Point], this is equivalent to [TextRotationAlignment.Viewport]. Otherwise, - * this is equivalent to [TextRotationAlignment.Map]. - */ - Auto(Expression.ofString("auto")), -} - -/** How the text will be laid out. */ -public enum class TextWritingMode(override val stringConst: Expression) : - EnumValue { - /** - * If a text's language supports horizontal writing mode, symbols with point placement would be - * laid out horizontally. - */ - Horizontal(Expression.ofString("horizontal")), - - /** - * If a text's language supports vertical writing mode, symbols with point placement would be laid - * out vertically. - */ - Vertical(Expression.ofString("vertical")), -} - -/** Text justification options. */ -public enum class TextJustify(override val stringConst: Expression) : - EnumValue { - /** The text is aligned towards the anchor position. */ - Auto(Expression.ofString("auto")), - - /** The text is aligned to the left. */ - Left(Expression.ofString("left")), - - /** The text is centered. */ - Center(Expression.ofString("center")), - - /** The text is aligned to the right. */ - Right(Expression.ofString("right")), -} - -/** Specifies how to capitalize text, similar to the CSS text-transform property. */ -public enum class TextTransform(override val stringConst: Expression) : - EnumValue { - /** The text is not altered. */ - None(Expression.ofString("none")), - - /** Forces all letters to be displayed in uppercase. */ - Uppercase(Expression.ofString("uppercase")), - - /** Forces all letters to be displayed in lowercase. */ - Lowercase(Expression.ofString("lowercase")), -} diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/ExpressionsDsl.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/ExpressionsDsl.kt deleted file mode 100644 index 4abc509e..00000000 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/expression/ExpressionsDsl.kt +++ /dev/null @@ -1,1428 +0,0 @@ -package dev.sargunv.maplibrecompose.core.expression - -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ImageBitmap -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.TextUnitType -import androidx.compose.ui.unit.isSpecified -import dev.sargunv.maplibrecompose.core.util.JsOnlyApi -import kotlin.enums.enumEntries -import kotlin.jvm.JvmInline -import kotlin.jvm.JvmName -import kotlin.time.Duration - -public object ExpressionsDsl { - // region Literals - - /** Creates a literal expression for a [String] value. */ - public fun const(string: String): Expression = Expression.ofString(string) - - /** Creates a literal expression for an enum value implementing [EnumValue]. */ - public fun > const(value: T): Expression = value.stringConst.cast() - - /** Creates a literal expression for a dimensionless [Float] value. */ - public fun const(float: Float): Expression = Expression.ofFloat(float) - - /** Creates a literal expression for an dimensionless [Int] value. */ - public fun const(int: Int): Expression = Expression.ofInt(int) - - /** Creates a literal expression for a [Dp] value. */ - public fun const(dp: Dp): Expression = Expression.ofFloat(dp.value).cast() - - /** - * Creates a literal expression for a specified [TextUnit] value in SP or EM. It can be provided - * in either unit, and will resolve to one at runtime depending on the property it is used in. - */ - public fun const(textUnit: TextUnit): Expression = - when (textUnit.type) { - TextUnitType.Sp -> const(textUnit.value).sp - TextUnitType.Em -> const(textUnit.value).em - TextUnitType.Unspecified -> unspecifiedValueVar.use() - else -> error("Unrecognized TextUnitType: ${textUnit.type}") - } - - /** - * Creates a literal expression for a [Duration] value. - * - * The duration will be rounded to the nearest whole milliseconds. - */ - public fun const(duration: Duration): Expression = - Expression.ofInt(duration.inWholeMilliseconds.toInt()).cast() - - /** Creates a literal expression for a [Boolean] value. */ - public fun const(bool: Boolean): Expression = - if (bool) Expression.ofTrue else Expression.ofFalse - - /** Creates a literal expression for a [Color] value. */ - public fun const(color: Color): Expression = Expression.ofColor(color) - - /** Creates a literal expression for an [Offset] value. */ - public fun const(offset: Offset): Expression = Expression.ofOffset(offset) - - /** Creates a literal expression for a [DpOffset] value. */ - public fun const(dpOffset: DpOffset): Expression = - Expression.ofOffset(Offset(dpOffset.x.value, dpOffset.y.value)).cast() - - /** Creates a literal expression for a [PaddingValues.Absolute] value. */ - public fun const(padding: PaddingValues.Absolute): Expression = - Expression.ofPadding(padding) - - internal fun literal(list: List): Expression> = - Expression.ofList(listOf("literal", list)).cast() - - /** Creates a literal expression for a list of strings. */ - @JvmName("constStringList") - public fun const(list: List): Expression> = literal(list).cast() - - /** Creates a literal expression for a list of numbers. */ - @JvmName("constNumberList") - public fun const(list: List): Expression> = literal(list).cast() - - /** - * Creates a literal expression for [TextVariableAnchorOffsetValue], used by - * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]'s - * `textVariableAnchorOffset` parameter. - * - * The offset is measured in a multipler of the text size (EM). It's in [Offset] instead of - * [offset] because of technical limitations in MapLibre. - */ - public fun textVariableAnchorOffset( - vararg pairs: Pair - ): Expression { - return literal( - buildList { - pairs.forEach { (anchor, offset) -> - add(anchor.stringConst) - add(offset) - } - } - ) - .cast() - } - - /** Creates a literal expression for a 2D [Offset]. */ - public fun offset(x: Float, y: Float): Expression = const(Offset(x, y)) - - /** Creates a literal expression for a 2D [DpOffset]. */ - public fun offset(x: Dp, y: Dp): Expression = const(DpOffset(x, y)) - - /** - * Creates a literal expression for a 2D [TextUnit] offset. - * - * Both [x] and [y] must have the same [TextUnitType]. - */ - public fun offset(x: TextUnit, y: TextUnit): Expression { - require(x.type == y.type) { "x and y must have the same TextUnitType" } - - val reasonablyLargeMultiplier = 1000f - val type = x.type - val xVal = if (x.isSpecified) x.value else 1f - val yVal = if (y.isSpecified) y.value else 1f - - return interpolate( - linear(), - when (type) { - TextUnitType.Sp -> spMultiplierVar - TextUnitType.Em -> emMultiplierVar - TextUnitType.Unspecified -> unspecifiedValueVar - else -> error("Unrecognized TextUnitType: $type") - } - .use() - .cast(), - 0f to const(listOf(0, 0)), - 1f to const(listOf(xVal, yVal)), - reasonablyLargeMultiplier to - const(listOf(xVal * reasonablyLargeMultiplier, yVal * reasonablyLargeMultiplier)), - ) - .cast() - } - - /** - * Creates a literal expression for a `null` value. - * - * For simplicity, the expression type system does not encode nullability, so the return value of - * this function is assignable to any kind of expression. - */ - public fun nil(): Expression = Expression.ofNull.cast() - - // endregion - - // region Conversion - - /** Converts a numeric [Expression] to a [DpValue] expression. */ - public val Expression.dp: Expression - get() = this.cast() - - /** Converts a numeric [Expression] in milliseconds to a [MillisecondsValue] expression. */ - public val Expression.milliseconds: Expression - get() = this.cast() - - /** Converts a numeric [Expression] in seconds to a [MillisecondsValue] expression. */ - public val Expression.seconds: Expression - get() = (this * const(1000f)).cast() - - /** Converts a numeric [Expression] to an [TextUnitValue] expression in SP. */ - public val Expression.sp: Expression - get() = this.cast() * spMultiplierVar.use() - - /** Converts a numeric [Expression] to an [TextUnitValue] expression in EM */ - public val Expression.em: Expression - get() = this.cast() * emMultiplierVar.use() - - internal val spMultiplierVar = Variable("__sp_multiplier") - internal val emMultiplierVar = Variable("__em_multiplier") - internal val unspecifiedValueVar = Variable("__unspecified_value") - - internal fun Expression<*>.resolveTextUnits( - spMultiplier: Expression, - emMultiplier: Expression, - unspecifiedValue: Expression, - ): Expression<*> = - spMultiplierVar.bind( - spMultiplier, - emMultiplierVar.bind(emMultiplier, unspecifiedValueVar.bind(unspecifiedValue, this)), - ) - - // endregion - - // region Variable binding - - /** - * Binds expression [value] to a [Variable] with the given [name], which can then be referenced - * inside the block using [use]. For example: - * ```kt - * val result = withVariable("x", const(5)) { x -> - * x.use() + const(3) - * } - * ``` - * - * Variable names starting with `__` are reserved for internal use by the library. - */ - public inline fun withVariable( - name: String, - value: Expression, - block: (Variable) -> Expression, - ): Expression { - require(!name.startsWith("__")) { "Variable names starting with '__' are reserved." } - return Variable(name).let { it.bind(value, block(it)) } - } - - /** References a [Variable] bound in [withVariable]. */ - public fun Variable.use(): Expression = - callFn("var", const(name)).cast() - - /** Represents a variable bound with [withVariable]. Reference the bound expression with [use]. */ - @JvmInline - public value class Variable<@Suppress("unused") T : ExpressionValue> - @PublishedApi - internal constructor(public val name: String) - - @PublishedApi - internal fun Variable.bind( - value: Expression, - expression: Expression, - ): Expression = callFn("let", const(name), value, expression).cast() - - // endregion - - // region Types - - /** Returns a string describing the type of this expression. */ - public fun Expression<*>.type(): Expression = callFn("typeof", this).cast() - - /** - * Asserts that this is a list (optionally with a specific item [type] and [length]). - * - * If, when the input expression is evaluated, it is not of the asserted type, then this assertion - * will cause the whole expression to be aborted. - */ - public fun Expression<*>.asList( - type: Expression = nil(), - length: Expression = nil(), - ): Expression> = callFn("array", this, type, length).cast() - - /** - * Asserts that this is a list of numbers, optionally with a specific [length]. - * - * If, when the input expression is evaluated, it is not of the asserted type, then this assertion - * will cause the whole expression to be aborted. - */ - public fun > Expression<*>.asVector( - length: Expression = nil() - ): Expression = asList(const(ExpressionType.Number), length).cast() - - /** - * Asserts that this is a list of numbers of length 2. - * - * If, when the input expression is evaluated, it is not of the asserted type, then this assertion - * will cause the whole expression to be aborted. - */ - public fun Expression<*>.asOffset(): Expression = - asList(const(ExpressionType.Number), const(2)).cast() - - /** - * Asserts that this is a list of numbers of length 2. - * - * If, when the input expression is evaluated, it is not of the asserted type, then this assertion - * will cause the whole expression to be aborted. - */ - public fun Expression<*>.asDpOffset(): Expression = - asList(const(ExpressionType.Number), const(2)).cast() - - /** - * Asserts that this is a list of numbers of length 4. - * - * If, when the input expression is evaluated, it is not of the asserted type, then this assertion - * will cause the whole expression to be aborted. - */ - public fun Expression<*>.asPadding(): Expression = - asList(const(ExpressionType.Number), const(2)).cast() - - /** - * Asserts that this value is a string. - * - * In case this expression is not a string, each of the [fallbacks] is evaluated in order until a - * string is obtained. If none of the inputs are strings, the expression is an error. - */ - public fun Expression<*>.asString(vararg fallbacks: Expression<*>): Expression = - callFn("string", this, *fallbacks).cast() - - /** - * Asserts that this value is an entry of the enum specified by [T]. - * - * In case this expression is not an entry of the enum, each of the [fallbacks] is evaluated in - * order until a match is obtained. If none of the inputs match, the expression is an error. - */ - public inline fun Expression<*>.asEnum( - vararg fallbacks: Expression<*> - ): Expression where T : Enum, T : EnumValue { - val entries = const(enumEntries().map { it.name }) - return switch( - condition(entries.contains(this), this), - *fallbacks.map { condition(entries.contains(it), it) }.toTypedArray(), - fallback = nil(), // should always error .asString(), which is what we want as per kdoc - ) - .asString() - .cast() - } - - /** - * Asserts that this value is a number. - * - * In case this expression is not a number, each of the [fallbacks] is evaluated in order until a - * number is obtained. If none of the inputs are numbers, the expression is an error. - */ - public fun Expression<*>.asNumber(vararg fallbacks: Expression<*>): Expression = - callFn("number", this, *fallbacks).cast() - - /** - * Asserts that this value is a boolean. - * - * In case this expression is not a boolean, each of the [fallbacks] is evaluated in order until a - * boolean is obtained. If none of the inputs are booleans, the expression is an error. - */ - public fun Expression<*>.asBoolean(vararg fallbacks: Expression<*>): Expression = - callFn("boolean", this, *fallbacks).cast() - - /** - * Asserts that this value is a map. - * - * In case this expression is not a map, each of the [fallbacks] is evaluated in order until a map - * is obtained. If none of the inputs are maps, the expression is an error. - */ - public fun Expression<*>.asMap(vararg fallbacks: Expression<*>): Expression> = - callFn("object", this, *fallbacks).cast() - - /** - * Returns a collator for use in locale-dependent comparison operations. The [caseSensitive] and - * [diacriticSensitive] options default to `false`. The [locale] argument specifies the IETF - * language tag of the locale to use. If none is provided, the default locale is used. If the - * requested locale is not available, the collator will use a system-defined fallback locale. Use - * [resolvedLocale] to test the results of locale fallback behavior. - */ - public fun collator( - caseSensitive: Expression? = null, - diacriticSensitive: Expression? = null, - locale: Expression? = null, - ): Expression = - callFn( - "collator", - buildOptions { - caseSensitive?.let { put("case-sensitive", it) } - diacriticSensitive?.let { put("diacritic-sensitive", it) } - locale?.let { put("locale", it) } - }, - ) - .cast() - - /** - * Returns a formatted string for displaying mixed-format text in the `textField` property (see - * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]). The input may contain a - * string literal or expression, including an [image] expression. - * - * Example: - * ``` - * format( - * span( - * feature.get("name").asString().substring(const(0), const(1)).uppercase(), - * textScale = const(1.5f), - * ), - * span(feature.get("name").asString().substring(const(1))) - * ) - * ``` - * - * Capitalizes the first letter of the features' property "name" and formats it to be extra-large, - * the rest of the name is written normally. - */ - public fun format(vararg spans: FormatSpan): Expression = - callFn( - "format", - *spans.foldToArgs { span -> - add(span.value) - add(span.options) - }, - ) - .cast() - - /** Configures a span of text in a [format] expression. */ - public fun span( - value: Expression, - textFont: Expression? = null, - textColor: Expression? = null, - textSize: Expression? = null, - ): FormatSpan = - FormatSpan(value = value, textFont = textFont, textColor = textColor, textSize = textSize) - - /** Configures an image in a [format] expression. */ - public fun span(value: Expression): FormatSpan = FormatSpan(value = value) - - /** Represents a component of a [format] expression. See [span]. */ - public data class FormatSpan - internal constructor( - val value: Expression, - val textFont: Expression? = null, - val textColor: Expression? = null, - val textSize: Expression? = null, - ) { - internal val options - get() = buildOptions { - textFont?.let { put("text-font", it) } - textColor?.let { put("text-color", it) } - textSize?.let { put("font-scale", it) } - } - } - - /** - * Returns an image type for use in `iconImage` (see - * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]), `pattern` entries (see - * [BackgroundLayer][dev.sargunv.maplibrecompose.compose.layer.BackgroundLayer], - * [FillLayer][dev.sargunv.maplibrecompose.compose.layer.FillLayer], - * [FillExtrusionLayer][dev.sargunv.maplibrecompose.compose.layer.FillExtrusionLayer], - * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]) and as a section in the - * [format] expression. - * - * If set, the image argument will check that the requested image exists in the style and will - * return either the resolved image name or `null`, depending on whether or not the image is - * currently in the style. This validation process is synchronous and requires the image to have - * been added to the style before requesting it in the image argument. - */ - public fun image(value: Expression): Expression = - callFn("image", value).cast() - - /** - * Returns an image type for use in `iconImage` (see - * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]), `pattern` entries (see - * [BackgroundLayer][dev.sargunv.maplibrecompose.compose.layer.BackgroundLayer], - * [FillLayer][dev.sargunv.maplibrecompose.compose.layer.FillLayer], - * [FillExtrusionLayer][dev.sargunv.maplibrecompose.compose.layer.FillExtrusionLayer], - * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]) and as a section in the - * [format] expression. - * - * The [ImageBitmap] will be registered with the style when it's referenced by a layer, and - * unregistered from the style if it's no longer referenced by any layer. An ID referencing the - * bitmap will be generated automatically and inserted into the expression. - */ - public fun image(value: ImageBitmap): Expression = Expression.ofBitmap(value) - - /** - * Returns an image type for use in `iconImage` (see - * [SymbolLayer][dev.sargunv.maplibrecompose.compose.layer.SymbolLayer]), `pattern` entries (see - * [BackgroundLayer][dev.sargunv.maplibrecompose.compose.layer.BackgroundLayer], - * [FillLayer][dev.sargunv.maplibrecompose.compose.layer.FillLayer], - * [FillExtrusionLayer][dev.sargunv.maplibrecompose.compose.layer.FillExtrusionLayer], - * [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]) and as a section in the - * [format] expression. - * - * The [Painter] will be drawn to an [ImageBitmap] and registered with the style when it's - * referenced by a layer, and unregistered from the style if it's no longer referenced by any - * layer. An ID referencing the bitmap will be generated automatically and inserted into the - * expression. - * - * The bitmap will be created with the intrinsic size of the painter, or 16x16 DP if the painter - * does not have an intrinsic size. - */ - public fun image(value: Painter): Expression = Expression.ofPainter(value) - - /** - * Converts this number into a string representation using the provided formatting rules. - * - * @param locale BCP 47 language tag for which locale to use - * @param currency an ISO 4217 code to use for currency-style formatting - * @param minFractionDigits minimum fractional digits to include - * @param maxFractionDigits maximum fractional digits to include - */ - public fun Expression>.formatToString( - locale: Expression? = null, - currency: Expression? = null, - minFractionDigits: Expression? = null, - maxFractionDigits: Expression? = null, - ): Expression = - callFn( - "number-format", - this, - buildOptions { - locale?.let { put("locale", it) } - currency?.let { put("currency", it) } - minFractionDigits?.let { put("min-fraction-digits", it) } - maxFractionDigits?.let { put("max-fraction-digits", it) } - }, - ) - .cast() - - /** - * Converts this expression to a string. - * - * If this is ... - * - `null`, the result is `""` - * - a boolean, the result is `"true"` or `"false"` - * - a number, it is converted to a string as specified by the "NumberToString" algorithm of the - * ECMAScript Language Specification. - * - a color, it is converted to a string of the form `"rgba(r,g,b,a)"`, where `r`, `g`, and `b` - * are numerals ranging from 0 to 255, and `a` ranges from 0 to 1. - * - * Otherwise, the input is converted to a string in the format specified by the JSON.stringify - * function of the ECMAScript Language Specification. - */ - public fun Expression<*>.convertToString(): Expression = - callFn("to-string", this).cast() - - /** - * Converts this expression to a number. - * - * If this expression is `null` or `false`, the result is `0`. If this is `true`, the result is - * `1`. If the input is a string, it is converted to a number as specified by the "ToNumber - * Applied to the String Type" algorithm of the ECMAScript Language Specification. - * - * In case this expression cannot be converted to a number, each of the [fallbacks] is evaluated - * in order until the first successful conversion is obtained. If none of the inputs can be - * converted, the expression is an error. - */ - public fun Expression<*>.convertToNumber( - vararg fallbacks: Expression<*> - ): Expression = callFn("to-number", this, *fallbacks).cast() - - /** - * Converts this expression to a boolean expression. - * - * The result is `false` when then this is an empty string, `0`, `false`,`null` or `NaN`; - * otherwise it is `true`. - */ - public fun Expression<*>.convertToBoolean(): Expression = - callFn("to-boolean", this).cast() - - /** - * Converts this expression to a color expression. - * - * In case this expression cannot be converted to a color, each of the [fallbacks] is evaluated in - * order until the first successful conversion is obtained. If none of the inputs can be - * converted, the expression is an error. - */ - public fun Expression<*>.convertToColor(vararg fallbacks: Expression<*>): Expression = - callFn("to-color", this, *fallbacks).cast() - - // endregion - - // region List - - /** Returns the item at [index]. */ - @JvmName("getAt") - public operator fun Expression>.get( - index: Expression - ): Expression = callFn("at", index, this).cast() - - /** Returns whether this list contains the [item]. */ - @JvmName("containsList") - public fun Expression>.contains( - item: Expression - ): Expression = callFn("in", item, this).cast() - - /** - * Returns the first index at which the [item] is located in this list, or `-1` if it cannot be - * found. Accepts an optional [startIndex] from where to begin the search. - */ - @JvmName("indexOfList") - public fun Expression>.indexOf( - item: Expression, - startIndex: Expression? = null, - ): Expression { - val args = buildList { - add(item) - add(this@indexOf) - startIndex?.let { add(it) } - } - return callFn("index-of", *args.toTypedArray()).cast() - } - - /** - * Returns the items in this list from the [startIndex] (inclusive) to the end of this list if - * [endIndex] is not specified or `null`, otherwise to [endIndex] (exclusive). - */ - public fun Expression>.slice( - startIndex: Expression, - endIndex: Expression? = null, - ): Expression> { - val args = buildList { - add(this@slice) - add(startIndex) - endIndex?.let { add(it) } - } - return callFn("slice", *args.toTypedArray()).cast() - } - - /** Gets the length of a this list. */ - @JvmName("lengthOfList") - public fun Expression>.length(): Expression = callFn("length", this).cast() - - // endregion - - // region Map - - /** Returns the value corresponding the given [key] or `null` if it is not present in this map. */ - public operator fun Expression>.get( - key: Expression - ): Expression = callFn("get", key, this).cast() - - /** Returns whether the given [key] is in this map. */ - public fun Expression>.has(key: Expression): Expression = - callFn("has", key, this).cast() - - // endregion - - // region String - - /** Returns whether this string contains the [substring]. */ - @JvmName("containsString") - public fun Expression.contains( - substring: Expression - ): Expression = callFn("in", substring, this).cast() - - /** - * Returns the first index at which the [substring] is located in this string, or `-1` if it - * cannot be found. Accepts an optional [startIndex] from where to begin the search. - */ - @JvmName("indexOfString") - public fun Expression.indexOf( - substring: Expression, - startIndex: Expression? = null, - ): Expression { - val args = buildList { - add(substring) - add(this@indexOf) - startIndex?.let { add(it) } - } - return callFn("index-of", *args.toTypedArray>()).cast() - } - - /** - * Returns a substring from this string from the [startIndex] (inclusive) to the end of the string - * if [endIndex] is not specified or `null`, otherwise to [endIndex] (exclusive). - * - * A UTF-16 surrogate pair counts as a single position. - */ - public fun Expression.substring( - startIndex: Expression, - endIndex: Expression? = null, - ): Expression { - val args = buildList { - add(this@substring) - add(startIndex) - endIndex?.let { add(it) } - } - return callFn("slice", *args.toTypedArray>()).cast() - } - - /** - * Gets the length of this string. - * - * A UTF-16 surrogate pair counts as a single position. - */ - @JvmName("lengthOfString") - public fun Expression.length(): Expression = callFn("length", this).cast() - - /** - * Returns `true` if this string is expected to render legibly. Returns `false` if this string - * contains sections that cannot be rendered without potential loss of meaning (e.g. Indic scripts - * that require complex text shaping). - */ - public fun Expression.isScriptSupported(): Expression = - callFn("is-supported-script", this).cast() - - /** - * Returns this string converted to uppercase. Follows the Unicode Default Case Conversion - * algorithm and the locale-insensitive case mappings in the Unicode Character Database. - */ - public fun Expression.uppercase(): Expression = - callFn("upcase", this).cast() - - /** - * Returns this string converted to lowercase. Follows the Unicode Default Case Conversion - * algorithm and the locale-insensitive case mappings in the Unicode Character Database. - */ - public fun Expression.lowercase(): Expression = - callFn("downcase", this).cast() - - /** Concatenates this string expression with [other]. */ - @JvmName("concat") - public operator fun Expression.plus( - other: Expression - ): Expression = callFn("concat", this, other).cast() - - /** - * Returns the IETF language tag of the locale being used by the provided [collator]. This can be - * used to determine the default system locale, or to determine if a requested locale was - * successfully loaded. - */ - public fun resolvedLocale(collator: Expression): Expression = - callFn("resolved-locale", collator).cast() - - // endregion - - // region Decision - - /** - * Selects the first output from the given [conditions] whose corresponding test condition - * evaluates to `true`, or the [fallback] value otherwise. - * - * Example: - * ``` - * switch( - * condition( - * test = feature.has(const("color1")) and feature.has(const("color2")), - * output = interpolate( - * linear(), - * zoom(), - * 1 to feature.get(const("color1")).convertToColor(), - * 20 to feature.get(const("color2")).convertToColor() - * ), - * ), - * condition( - * test = feature.has(const("color")), - * output = feature.get(const("color")).convertToColor(), - * ), - * fallback = const(Color.Red), - * ) - * ``` - * - * If the feature has both a "color1" and "color2" property, the result is an interpolation - * between these two colors based on the zoom level. Otherwise, if the feature has a "color" - * property, that color is returned. If the feature has none of the three, the color red is - * returned. - */ - public fun switch( - vararg conditions: Condition, - fallback: Expression, - ): Expression = - callFn( - "case", - *conditions.foldToArgs { (test, output) -> - add(test) - add(output) - }, - fallback, - ) - .cast() - - public data class Condition - internal constructor( - internal val test: Expression, - internal val output: Expression, - ) - - /** Create a [Condition], see [case] */ - public fun condition( - test: Expression, - output: Expression, - ): Condition = Condition(test, output) - - /** - * Selects the output from the given [cases] whose label value matches the [input], or the - * [fallback] value if no match is found. - * - * Each label must be unique. If the input type does not match the type of the labels, the result - * will be the [fallback] value. - * - * Example: - * ``` - * switch( - * input = feature.get(const("building_type")).asString(), - * case( - * label = "residential", - * output = const(Color.Cyan), - * ), - * case( - * label = listOf("commercial", "industrial"), - * output = const(Color.Yellow), - * ), - * fallback = const(Color.Red), - * ) - * ``` - * - * If the feature has a property "building_type" with the value "residential", cyan is returned. - * Otherwise, if the value of that property is either "commercial" or "industrial", yellow is - * returned. If none of that is true, the fallback is returned, i.e. red. - */ - public fun switch( - input: Expression, - vararg cases: Case, - fallback: Expression, - ): Expression = - callFn( - "match", - input, - *cases.foldToArgs { (label, output) -> - add(label) - add(output) - }, - fallback, - ) - .cast() - - public data class Case<@Suppress("unused") I : MatchableValue, O : ExpressionValue> - internal constructor(internal val label: Expression<*>, internal val output: Expression) - - /** Create a [Case], see [switch] */ - public fun case( - label: String, - output: Expression, - ): Case = Case(const(label), output) - - /** Create a [Case], see [switch] */ - public fun case(label: Number, output: Expression): Case = - Case(const(label.toFloat()), output) - - /** Create a [Case], see [switch] */ - @JvmName("stringsCase") - public fun case( - label: List, - output: Expression, - ): Case = Case(Expression.ofList(label), output) - - /** Create a [Case], see [switch] */ - @JvmName("numbersCase") - public fun case( - label: List, - output: Expression, - ): Case = Case(Expression.ofList(label), output) - - /** - * Evaluates each expression in [values] in turn until the first non-null value is obtained, and - * returns that value. - */ - public fun coalesce(vararg values: Expression): Expression = - callFn("coalesce", *values).cast() - - /** Returns whether this expression is equal to [other]. */ - public infix fun Expression.eq( - other: Expression - ): Expression = callFn("==", this, other).cast() - - /** - * Returns whether the [left] string expression is equal to the [right] string expression. An - * optional [collator] (see [ExpressionsDsl.collator] function) can be specified to control - * locale-dependent string comparisons. - */ - public fun eq( - left: Expression, - right: Expression, - collator: Expression, - ): Expression = callFn("==", left, right, collator).cast() - - /** Returns whether this expression is not equal to [other]. */ - public infix fun Expression.neq( - other: Expression - ): Expression = callFn("!=", this, other).cast() - - /** - * Returns whether the [left] string expression is not equal to the [right] string expression. An - * optional [collator] (see [ExpressionsDsl.collator]) can be specified to control - * locale-dependent string comparisons. - */ - public fun neq( - left: Expression, - right: Expression, - collator: Expression, - ): Expression = callFn("!=", left, right, collator).cast() - - /** - * Returns whether this expression is strictly greater than [other]. - * - * Strings are compared lexicographically (`"b" > "a"`). - */ - public infix fun Expression>.gt( - other: Expression> - ): Expression = callFn(">", this, other).cast() - - /** - * Returns whether the [left] string expression is strictly greater than the [right] string - * expression. An optional [collator] (see [ExpressionsDsl.collator]) can be specified to control - * locale-dependent string comparisons. - * - * Strings are compared lexicographically (`"b" > "a"`). - */ - public fun gt( - left: Expression, - right: Expression, - collator: Expression, - ): Expression = callFn(">", left, right, collator).cast() - - /** - * Returns whether this expression is strictly less than [other]. - * - * Strings are compared lexicographically (`"a" < "b"`). - */ - public infix fun Expression>.lt( - other: Expression> - ): Expression = callFn("<", this, other).cast() - - /** - * Returns whether the [left] string expression is strictly less than the [right] string - * expression. An optional [collator] (see [ExpressionsDsl.collator]) can be specified to control - * locale-dependent string comparisons. - * - * Strings are compared lexicographically (`"a" < "b"`). - */ - public fun lt( - left: Expression, - right: Expression, - collator: Expression, - ): Expression = callFn("<", left, right, collator).cast() - - /** - * Returns whether this expression is greater than or equal to [other]. - * - * Strings are compared lexicographically (`"b" >= "a"`). - */ - public infix fun Expression>.gte( - other: Expression> - ): Expression = callFn(">=", this, other).cast() - - /** - * Returns whether the [left] string expression is greater than or equal to the [right] string - * expression. An optional [collator] (see [ExpressionsDsl.collator]) can be specified to control - * locale-dependent string comparisons. - * - * Strings are compared lexicographically (`"b" >= "a"`). - */ - public fun gte( - left: Expression, - right: Expression, - collator: Expression, - ): Expression = callFn(">=", left, right, collator).cast() - - /** - * Returns whether this string expression is less than or equal to [other]. - * - * Strings are compared lexicographically (`"a" <= "b"`). - */ - public infix fun Expression>.lte( - other: Expression> - ): Expression = callFn("<=", this, other).cast() - - /** - * Returns whether the [left] string expression is less than or equal to the [right] string - * expression. An optional [collator] (see [ExpressionsDsl.collator]) can be specified to control - * locale-dependent string comparisons. - * - * Strings are compared lexicographically (`"a" < "b"`). - */ - public fun lte( - left: Expression, - right: Expression, - collator: Expression, - ): Expression = callFn("<=", left, right, collator).cast() - - /** Returns whether all [expressions] are `true`. */ - public fun all(vararg expressions: Expression): Expression = - callFn("all", *expressions).cast() - - /** Returns whether both this and [other] expressions are `true`. */ - public infix fun Expression.and( - other: Expression - ): Expression = all(this, other) - - /** Returns whether any [expressions] are `true`. */ - public fun any(vararg expressions: Expression): Expression = - callFn("any", *expressions).cast() - - /** Returns whether any of this or the [other] expressions are `true`. */ - public infix fun Expression.or( - other: Expression - ): Expression = any(this, other) - - /** Negates this expression. */ - @JvmName("notOperator") - public operator fun Expression.not(): Expression = - callFn("!", this).cast() - - /** - * Returns true if the evaluated feature is fully contained inside a boundary of the input - * geometry, false otherwise. The input value can be a valid GeoJSON of type Polygon, - * MultiPolygon, Feature, or FeatureCollection. Supported features for evaluation: - * - Point: Returns false if a point is on the boundary or falls outside the boundary. - * - LineString: Returns false if any part of a line falls outside the boundary, the line - * intersects the boundary, or a line's endpoint is on the boundary. - */ - public fun within(geometry: Expression): Expression = - callFn("within", geometry).cast() - - // endregion - - // region Ramps, Scales, Curves - - /** - * Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs - * of input and output values ([stops]). Returns the output value of the stop just less than the - * [input], or the [fallback] if the input is less than the first stop. - * - * Example: - * ``` - * step(zoom(), const(0), 10 to const(2.5), 20 to const(10.5)) - * ``` - * - * returns 0 if the zoom is less than 10, 2.5 if the zoom is between 10 and less than 20, 10.5 if - * the zoom is greater than or equal 20. - */ - public fun step( - input: Expression, - fallback: Expression, - vararg stops: Pair>, - ): Expression = - callFn( - "step", - input, - fallback, - *stops - .sortedBy { it.first.toFloat() } - .foldToArgs { - add(const(it.first.toFloat())) - add(it.second) - }, - ) - .cast() - - private fun > interpolateImpl( - name: String, - type: Expression, - input: Expression, - vararg stops: Pair>, - ): Expression = - callFn( - name, - type, - input, - *stops - .sortedBy { it.first.toDouble() } - .foldToArgs { - add(const(it.first.toFloat())) - add(it.second) - }, - ) - .cast() - - /** - * Produces continuous, smooth results by interpolating between pairs of input and output values - * ([stops]), given the [input] value. - * - * Example: - * ``` - * interpolate( - * exponential(2), zoom(), - * 16 to const(1), - * 24 to const(256), - * ) - * ``` - * - * interpolates exponentially from 1 to 256 in zoom levels 16 to 24. Below zoom 16, it is 1, above - * zoom 24, it is 256. Applied to for example line width, this has the visual effect that the line - * stays the same width in meters on the map (rather than on the viewport). - */ - public fun > interpolate( - type: Expression, - input: Expression, - vararg stops: Pair>, - ): Expression = interpolateImpl("interpolate", type, input, *stops) - - /** - * Produces continuous, smooth results by interpolating between pairs of input and output values - * ([stops]), given the [input] value. Works like [interpolate], but the interpolation is - * performed in the - * [Hue-Chroma-Luminance color space](https://en.wikipedia.org/wiki/HCL_color_space). - * - * Example: - * ``` - * interpolateHcl( - * linear(), - * zoom(), - * 1 to const(Color.Red), - * 5 to const(Color.Blue), - * 10 to const(Color.Green) - * ) - * ``` - * - * interpolates linearly from red to blue between in zoom levels 1 to 5, then interpolates - * linearly from blue to green in zoom levels 5 to 10, which it where it remains until maximum - * zoom. - */ - @JsOnlyApi - public fun interpolateHcl( - type: Expression, - input: Expression, - vararg stops: Pair>, - ): Expression = interpolateImpl("interpolate-hcl", type, input, *stops) - - /** - * Produces continuous, smooth results by interpolating between pairs of input and output values - * ([stops]), given the [input] value. Works like [interpolate], but the interpolation is - * performed in the [CIELAB color space](https://en.wikipedia.org/wiki/CIELAB_color_space). - */ - @JsOnlyApi - public fun interpolateLab( - type: Expression, - input: Expression, - vararg stops: Pair>, - ): Expression = interpolateImpl("interpolate-lab", type, input, *stops) - - /** Interpolates linearly between the pairs of stops. */ - public fun linear(): Expression = callFn("linear").cast() - - /** - * Interpolates exponentially between the stops. - * - * @param [base] controls the rate at which the output increases: higher values make the output - * increase more towards the high end of the range. With values close to 1 the output increases - * linearly. - */ - public fun exponential(base: Expression): Expression = - callFn("exponential", base).cast() - - /** - * Interpolates using the cubic bezier curve defined by the given control points between the pairs - * of stops. - */ - public fun cubicBezier( - x1: Expression, - y1: Expression, - x2: Expression, - y2: Expression, - ): Expression = callFn("cubic-bezier", x1, y1, x2, y2).cast() - - // endregion - - // region Math - - /** Returns mathematical constant ln(2) = natural logarithm of 2. */ - public fun ln2(): Expression = callFn("ln2").cast() - - /** Returns the mathematical constant π */ - public fun pi(): Expression = callFn("pi").cast() - - /** Returns the mathematical constant e */ - public fun e(): Expression = callFn("e").cast() - - /** Returns the sum of this number expression with [other]. */ - public operator fun > Expression.plus( - other: Expression - ): Expression = callFn("+", this, other).cast() - - /** Returns the product of this number expression with [other]. */ - @JvmName("timesUnitLeft") - public operator fun > Expression.times( - other: Expression - ): Expression = callFn("*", this, other).cast() - - /** Returns the product of this number expression with [other]. */ - @JvmName("timesUnitRight") - public operator fun > Expression.times( - other: Expression - ): Expression = callFn("*", this, other).cast() - - /** Returns the product of this number expression with [other]. */ - public operator fun Expression.times( - other: Expression - ): Expression = callFn("*", this, other).cast() - - /** Returns the result of subtracting [other] from this number expression. */ - public operator fun > Expression.minus( - other: Expression> - ): Expression = callFn("-", this, other).cast() - - /** Negates this number expression. */ - public operator fun > Expression.unaryMinus(): Expression = - callFn("-", this).cast() - - /** Returns the result of floating point division of this number expression by [divisor]. */ - @JvmName("divUnitBoth") - public operator fun > Expression.div( - divisor: Expression - ): Expression = callFn("/", this, divisor).cast() - - /** Returns the result of floating point division of this number expression by [divisor]. */ - @JvmName("divUnitLeftOnly") - public operator fun > Expression.div( - divisor: Expression - ): Expression = callFn("/", this, divisor).cast() - - /** Returns the result of floating point division of this number expression by [divisor]. */ - public operator fun Expression.div( - divisor: Expression - ): Expression = callFn("/", this, divisor).cast() - - /** Returns the remainder after integer division of this number expression by [divisor]. */ - public operator fun > Expression.rem( - divisor: Expression - ): Expression = callFn("%", this, divisor).cast() - - /** Returns the result of raising this number expression to the power of [exponent]. */ - public fun Expression.pow(exponent: Expression): Expression = - callFn("^", this, exponent).cast() - - /** Returns the square root of [value]. */ - public fun sqrt(value: Expression): Expression = - callFn("sqrt", value).cast() - - /** Returns the base-ten logarithm of [value]. */ - public fun log10(value: Expression): Expression = - callFn("log10", value).cast() - - /** Returns the natural logarithm of [value]. */ - public fun ln(value: Expression): Expression = callFn("ln", value).cast() - - /** Returns the base-two logarithm of [value]. */ - public fun log2(value: Expression): Expression = - callFn("log2", value).cast() - - /** Returns the sine of [value]. */ - public fun sin(value: Expression): Expression = - callFn("sin", value).cast() - - /** Returns the cosine of [value]. */ - public fun cos(value: Expression): Expression = - callFn("cos", value).cast() - - /** Returns the tangent of [value]. */ - public fun tan(value: Expression): Expression = - callFn("tan", value).cast() - - /** Returns the arcsine of [value]. */ - public fun asin(value: Expression): Expression = - callFn("asin", value).cast() - - /** Returns the arccosine of [value]. */ - public fun acos(value: Expression): Expression = - callFn("acos", value).cast() - - /** Returns the arctangent of [value]. */ - public fun atan(value: Expression): Expression = - callFn("atan", value).cast() - - /** Returns the smallest of all given [numbers]. */ - public fun > min(vararg numbers: Expression): Expression = - callFn("min", *numbers).cast() - - /** Returns the greatest of all given [numbers]. */ - public fun > max(vararg numbers: Expression): Expression = - callFn("max", *numbers).cast() - - /** Returns the absolute value of [value], i.e. always a positive value. */ - public fun > abs(value: Expression): Expression = - callFn("abs", value).cast() - - /** - * Rounds [value] to the nearest integer. Halfway values are rounded away from zero. - * - * For example `round(const(-1.5))` evaluates to `-2`. - */ - public fun round(value: Expression): Expression = - callFn("round", value).cast() - - /** Returns the smallest integer that is greater than or equal to [value]. */ - public fun ceil(value: Expression): Expression = - callFn("ceil", value).cast() - - /** Returns the largest integer that is less than or equal to [value]. */ - public fun floor(value: Expression): Expression = - callFn("floor", value).cast() - - /** Returns the shortest distance in meters between the evaluated feature and [geometry]. */ - public fun distance(geometry: Expression): Expression = - callFn("distance", geometry).cast() - - // endregion - - // region Color - - /** - * Returns a four-element list, containing the color's red, green, blue, and alpha components, in - * that order. - */ - public fun Expression.toRgbaComponents(): Expression> = - callFn("to-rgba", this).cast() - - /** - * Creates a color value from [red], [green], and [blue] components, which must range between 0 - * and 255, and optionally an [alpha] component which must range between 0 and 1. - * - * If any component is out of range, the expression is an error. - */ - public fun rgbColor( - red: Expression, - green: Expression, - blue: Expression, - alpha: Expression? = null, - ): Expression = - if (alpha != null) { - callFn("rgba", red, green, blue, alpha) - } else { - callFn("rgb", red, green, blue) - } - .cast() - - // endregion - - // region Feature data - - /** Object to access feature-related data, see [feature] */ - public object Feature { - /** - * Returns the value corresponding to the given [key] in the current feature's properties or - * `null` if it is not present. - */ - public fun get(key: Expression): Expression<*> = callFn("get", key) - - /** Tests for the presence of a property value [key] in the current feature's properties. */ - public fun has(key: Expression): Expression = - callFn("has", key).cast() - - /** - * Gets the feature properties object. Note that in some cases, it may be more efficient to use - * [get]`("property_name")` directly. - */ - public fun properties(): Expression> = callFn("properties").cast() - - /** - * **Note: Not supported on native platforms. See - * [maplibre-native#1698](https://github.com/maplibre/maplibre-native/issues/1698)** - * - * Retrieves a property value from the current feature's state. Returns `null` if the requested - * property is not present on the feature's state. - * - * A feature's state is not part of the GeoJSON or vector tile data, and must be set - * programmatically on each feature. - * - * When `source.promoteId` is not provided, features are identified by their `id` attribute, - * which must be an integer or a string that can be cast to an integer. When `source.promoteId` - * is provided, features are identified by their `promoteId` property, which may be a number, - * string, or any primitive data type. Note that [state] can only be used with layer properties - * that support data-driven styling. - */ - // TODO: document which layer properties support feature state expressions on which platforms - @JsOnlyApi - public fun state(key: Expression): Expression = - callFn("feature-state", key).cast() - - /** Gets the feature's geometry type. */ - public fun type(): Expression = callFn("geometry-type").cast() - - /** Gets the feature's id, if it has one. */ - public fun id(): Expression = callFn("id").cast() - - /** - * Gets the progress along a gradient line. Can only be used in the `gradient` property of a - * line layer, see [LineLayer][dev.sargunv.maplibrecompose.compose.layer.LineLayer]. - */ - public fun lineProgress(value: Expression): Expression = - callFn("line-progress", value).cast() - - /** - * Gets the value of a cluster property accumulated so far. Can only be used in the - * `clusterProperties` option of a clustered GeoJSON source, see - * [GeoJsonOptions][dev.sargunv.maplibrecompose.core.source.GeoJsonOptions]. - */ - public fun accumulated(key: Expression): Expression = - callFn("accumulated", key).cast() - } - - /** Accesses to feature-related data */ - public val feature: Feature = Feature - - // endregion - - // region Zoom - - /** - * Gets the current zoom level. Note that in layer style properties, [zoom] may only appear as the - * input to a top-level [step] or [interpolate] (, [interpolateHcl], [interpolateLab], ...) - * expression. - */ - public fun zoom(): Expression = callFn("zoom").cast() - - // endregion - - // region Heatmap - - /** - * Gets the kernel density estimation of a pixel in a heatmap layer, which is a relative measure - * of how many data points are crowded around a particular pixel. Can only be used in the - * expression for the `color` parameter in a - * [HeatmapLayer][dev.sargunv.maplibrecompose.compose.layer.HeatmapLayer]. - */ - public fun heatmapDensity(): Expression = callFn("heatmap-density").cast() - - // endregion - - // region Utils - - @Suppress("UNCHECKED_CAST") - /** Casts this expression to the specified type without any runtime check. Use with caution. */ - public fun Expression<*>.cast(): Expression = this as Expression - - private fun callFn(function: String, vararg args: Expression<*>): Expression<*> = - Expression.ofList( - buildList { - add(function) - args.forEach { add(it.value) } - } - ) - - private inline fun buildOptions(block: MutableMap>.() -> Unit) = - Expression.ofMap(mutableMapOf>().apply(block).mapValues { it.value }) - - private inline fun Array.foldToArgs( - block: MutableList>.(element: T) -> Unit - ) = - fold(mutableListOf>()) { acc, element -> acc.apply { block(element) } } - .toTypedArray() - - private inline fun List.foldToArgs( - block: MutableList>.(element: T) -> Unit - ) = - fold(mutableListOf>()) { acc, element -> acc.apply { block(element) } } - .toTypedArray() - - // endregion -} diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt index b36703f8..2784d7ad 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt @@ -1,15 +1,14 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue internal expect class BackgroundLayer(id: String) : Layer { - fun setBackgroundColor(color: Expression) + fun setBackgroundColor(color: CompiledExpression) - fun setBackgroundPattern(pattern: Expression>) + fun setBackgroundPattern(pattern: CompiledExpression) - fun setBackgroundOpacity(opacity: Expression) + fun setBackgroundOpacity(opacity: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt index 5232ebaa..7b07ee6d 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt @@ -1,43 +1,42 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.CirclePitchAlignment -import dev.sargunv.maplibrecompose.core.expression.CirclePitchScale -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchScale +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal expect class CircleLayer(id: String, source: Source) : FeatureLayer { override var sourceLayer: String - override fun setFilter(filter: Expression) + override fun setFilter(filter: CompiledExpression) - fun setCircleSortKey(sortKey: Expression) + fun setCircleSortKey(sortKey: CompiledExpression) - fun setCircleRadius(radius: Expression) + fun setCircleRadius(radius: CompiledExpression) - fun setCircleColor(color: Expression) + fun setCircleColor(color: CompiledExpression) - fun setCircleBlur(blur: Expression) + fun setCircleBlur(blur: CompiledExpression) - fun setCircleOpacity(opacity: Expression) + fun setCircleOpacity(opacity: CompiledExpression) - fun setCircleTranslate(translate: Expression) + fun setCircleTranslate(translate: CompiledExpression) - fun setCircleTranslateAnchor(translateAnchor: Expression>) + fun setCircleTranslateAnchor(translateAnchor: CompiledExpression) - fun setCirclePitchScale(pitchScale: Expression>) + fun setCirclePitchScale(pitchScale: CompiledExpression) - fun setCirclePitchAlignment(pitchAlignment: Expression>) + fun setCirclePitchAlignment(pitchAlignment: CompiledExpression) - fun setCircleStrokeWidth(strokeWidth: Expression) + fun setCircleStrokeWidth(strokeWidth: CompiledExpression) - fun setCircleStrokeColor(strokeColor: Expression) + fun setCircleStrokeColor(strokeColor: CompiledExpression) - fun setCircleStrokeOpacity(strokeOpacity: Expression) + fun setCircleStrokeOpacity(strokeOpacity: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt index 75bdd0dc..dd8e0b6d 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt @@ -1,12 +1,12 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue internal expect sealed class FeatureLayer(source: Source) : Layer { val source: Source abstract var sourceLayer: String - abstract fun setFilter(filter: Expression) + abstract fun setFilter(filter: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt index 232b1237..9b665aab 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt @@ -1,34 +1,32 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal expect class FillExtrusionLayer(id: String, source: Source) : FeatureLayer { override var sourceLayer: String - override fun setFilter(filter: Expression) + override fun setFilter(filter: CompiledExpression) - fun setFillExtrusionOpacity(opacity: Expression) + fun setFillExtrusionOpacity(opacity: CompiledExpression) - fun setFillExtrusionColor(color: Expression) + fun setFillExtrusionColor(color: CompiledExpression) - fun setFillExtrusionTranslate(translate: Expression) + fun setFillExtrusionTranslate(translate: CompiledExpression) - fun setFillExtrusionTranslateAnchor(anchor: Expression>) + fun setFillExtrusionTranslateAnchor(anchor: CompiledExpression) - fun setFillExtrusionPattern(pattern: Expression>) + fun setFillExtrusionPattern(pattern: CompiledExpression) - fun setFillExtrusionHeight(height: Expression) + fun setFillExtrusionHeight(height: CompiledExpression) - fun setFillExtrusionBase(base: Expression) + fun setFillExtrusionBase(base: CompiledExpression) - fun setFillExtrusionVerticalGradient(verticalGradient: Expression) + fun setFillExtrusionVerticalGradient(verticalGradient: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt index 42b34d86..04b8de3d 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt @@ -1,34 +1,32 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal expect class FillLayer(id: String, source: Source) : FeatureLayer { override var sourceLayer: String - override fun setFilter(filter: Expression) + override fun setFilter(filter: CompiledExpression) - fun setFillSortKey(sortKey: Expression) + fun setFillSortKey(sortKey: CompiledExpression) - fun setFillAntialias(antialias: Expression) + fun setFillAntialias(antialias: CompiledExpression) - fun setFillOpacity(opacity: Expression) + fun setFillOpacity(opacity: CompiledExpression) - fun setFillColor(color: Expression) + fun setFillColor(color: CompiledExpression) - fun setFillOutlineColor(outlineColor: Expression) + fun setFillOutlineColor(outlineColor: CompiledExpression) - fun setFillTranslate(translate: Expression) + fun setFillTranslate(translate: CompiledExpression) - fun setFillTranslateAnchor(translateAnchor: Expression>) + fun setFillTranslateAnchor(translateAnchor: CompiledExpression) - fun setFillPattern(pattern: Expression>) + fun setFillPattern(pattern: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt index 5c0ab827..30ca2331 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt @@ -1,24 +1,24 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue internal expect class HeatmapLayer(id: String, source: Source) : FeatureLayer { override var sourceLayer: String - override fun setFilter(filter: Expression) + override fun setFilter(filter: CompiledExpression) - fun setHeatmapRadius(radius: Expression) + fun setHeatmapRadius(radius: CompiledExpression) - fun setHeatmapWeight(weight: Expression) + fun setHeatmapWeight(weight: CompiledExpression) - fun setHeatmapIntensity(intensity: Expression) + fun setHeatmapIntensity(intensity: CompiledExpression) - fun setHeatmapColor(color: Expression) + fun setHeatmapColor(color: CompiledExpression) - fun setHeatmapOpacity(opacity: Expression) + fun setHeatmapOpacity(opacity: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt index 505668be..f089128a 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt @@ -1,24 +1,23 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.IlluminationAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor internal expect class HillshadeLayer(id: String, source: Source) : Layer { val source: Source - fun setHillshadeIlluminationDirection(direction: Expression) + fun setHillshadeIlluminationDirection(direction: CompiledExpression) - fun setHillshadeIlluminationAnchor(anchor: Expression>) + fun setHillshadeIlluminationAnchor(anchor: CompiledExpression) - fun setHillshadeExaggeration(exaggeration: Expression) + fun setHillshadeExaggeration(exaggeration: CompiledExpression) - fun setHillshadeShadowColor(shadowColor: Expression) + fun setHillshadeShadowColor(shadowColor: CompiledExpression) - fun setHillshadeHighlightColor(highlightColor: Expression) + fun setHillshadeHighlightColor(highlightColor: CompiledExpression) - fun setHillshadeAccentColor(accentColor: Expression) + fun setHillshadeAccentColor(accentColor: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt index c1c6ae58..97c08800 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt @@ -1,54 +1,52 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor -import dev.sargunv.maplibrecompose.core.expression.VectorValue import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor +import dev.sargunv.maplibrecompose.expressions.value.VectorValue internal expect class LineLayer(id: String, source: Source) : FeatureLayer { override var sourceLayer: String - override fun setFilter(filter: Expression) + override fun setFilter(filter: CompiledExpression) - fun setLineCap(cap: Expression>) + fun setLineCap(cap: CompiledExpression) - fun setLineJoin(join: Expression>) + fun setLineJoin(join: CompiledExpression) - fun setLineMiterLimit(miterLimit: Expression) + fun setLineMiterLimit(miterLimit: CompiledExpression) - fun setLineRoundLimit(roundLimit: Expression) + fun setLineRoundLimit(roundLimit: CompiledExpression) - fun setLineSortKey(sortKey: Expression) + fun setLineSortKey(sortKey: CompiledExpression) - fun setLineOpacity(opacity: Expression) + fun setLineOpacity(opacity: CompiledExpression) - fun setLineColor(color: Expression) + fun setLineColor(color: CompiledExpression) - fun setLineTranslate(translate: Expression) + fun setLineTranslate(translate: CompiledExpression) - fun setLineTranslateAnchor(translateAnchor: Expression>) + fun setLineTranslateAnchor(translateAnchor: CompiledExpression) - fun setLineWidth(width: Expression) + fun setLineWidth(width: CompiledExpression) - fun setLineGapWidth(gapWidth: Expression) + fun setLineGapWidth(gapWidth: CompiledExpression) - fun setLineOffset(offset: Expression) + fun setLineOffset(offset: CompiledExpression) - fun setLineBlur(blur: Expression) + fun setLineBlur(blur: CompiledExpression) - fun setLineDasharray(dasharray: Expression>) + fun setLineDasharray(dasharray: CompiledExpression>) - fun setLinePattern(pattern: Expression>) + fun setLinePattern(pattern: CompiledExpression) - fun setLineGradient(gradient: Expression) + fun setLineGradient(gradient: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt index 4d2b5568..f0d83fa6 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt @@ -1,28 +1,27 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.MillisecondsValue -import dev.sargunv.maplibrecompose.core.expression.RasterResampling import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.RasterResampling internal expect class RasterLayer(id: String, source: Source) : Layer { val source: Source - fun setRasterOpacity(opacity: Expression) + fun setRasterOpacity(opacity: CompiledExpression) - fun setRasterHueRotate(hueRotate: Expression) + fun setRasterHueRotate(hueRotate: CompiledExpression) - fun setRasterBrightnessMin(brightnessMin: Expression) + fun setRasterBrightnessMin(brightnessMin: CompiledExpression) - fun setRasterBrightnessMax(brightnessMax: Expression) + fun setRasterBrightnessMax(brightnessMax: CompiledExpression) - fun setRasterSaturation(saturation: Expression) + fun setRasterSaturation(saturation: CompiledExpression) - fun setRasterContrast(contrast: Expression) + fun setRasterContrast(contrast: CompiledExpression) - fun setRasterResampling(resampling: Expression>) + fun setRasterResampling(resampling: CompiledExpression) - fun setRasterFadeDuration(fadeDuration: Expression) + fun setRasterFadeDuration(fadeDuration: CompiledExpression) } diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt index 9ee373e8..ac74daf0 100644 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt +++ b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt @@ -1,153 +1,152 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpPaddingValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatOffsetValue -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.FormattedValue -import dev.sargunv.maplibrecompose.core.expression.IconPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.IconRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.IconTextFit -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ListValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.StringValue -import dev.sargunv.maplibrecompose.core.expression.SymbolAnchor -import dev.sargunv.maplibrecompose.core.expression.SymbolPlacement -import dev.sargunv.maplibrecompose.core.expression.SymbolZOrder -import dev.sargunv.maplibrecompose.core.expression.TextJustify -import dev.sargunv.maplibrecompose.core.expression.TextPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.TextRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.TextTransform -import dev.sargunv.maplibrecompose.core.expression.TextVariableAnchorOffsetValue -import dev.sargunv.maplibrecompose.core.expression.TextWritingMode -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source -import dev.sargunv.maplibrecompose.core.util.JsOnlyApi +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.IconPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconTextFit +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.SymbolPlacement +import dev.sargunv.maplibrecompose.expressions.value.SymbolZOrder +import dev.sargunv.maplibrecompose.expressions.value.TextJustify +import dev.sargunv.maplibrecompose.expressions.value.TextPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextTransform +import dev.sargunv.maplibrecompose.expressions.value.TextVariableAnchorOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextWritingMode +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal expect class SymbolLayer(id: String, source: Source) : FeatureLayer { override var sourceLayer: String - override fun setFilter(filter: Expression) + override fun setFilter(filter: CompiledExpression) - fun setSymbolPlacement(placement: Expression>) + fun setSymbolPlacement(placement: CompiledExpression) - fun setSymbolSpacing(spacing: Expression) + fun setSymbolSpacing(spacing: CompiledExpression) - fun setSymbolAvoidEdges(avoidEdges: Expression) + fun setSymbolAvoidEdges(avoidEdges: CompiledExpression) - fun setSymbolSortKey(sortKey: Expression) + fun setSymbolSortKey(sortKey: CompiledExpression) - fun setSymbolZOrder(zOrder: Expression>) + fun setSymbolZOrder(zOrder: CompiledExpression) - fun setIconAllowOverlap(allowOverlap: Expression) + fun setIconAllowOverlap(allowOverlap: CompiledExpression) - @JsOnlyApi fun setIconOverlap(overlap: Expression) + fun setIconOverlap(overlap: CompiledExpression) - fun setIconIgnorePlacement(ignorePlacement: Expression) + fun setIconIgnorePlacement(ignorePlacement: CompiledExpression) - fun setIconOptional(optional: Expression) + fun setIconOptional(optional: CompiledExpression) - fun setIconRotationAlignment(rotationAlignment: Expression>) + fun setIconRotationAlignment(rotationAlignment: CompiledExpression) - fun setIconSize(size: Expression) + fun setIconSize(size: CompiledExpression) - fun setIconTextFit(textFit: Expression>) + fun setIconTextFit(textFit: CompiledExpression) - fun setIconTextFitPadding(textFitPadding: Expression) + fun setIconTextFitPadding(textFitPadding: CompiledExpression) - fun setIconImage(image: Expression>) + fun setIconImage(image: CompiledExpression) - fun setIconRotate(rotate: Expression) + fun setIconRotate(rotate: CompiledExpression) - fun setIconPadding(padding: Expression) + fun setIconPadding(padding: CompiledExpression) - fun setIconKeepUpright(keepUpright: Expression) + fun setIconKeepUpright(keepUpright: CompiledExpression) - fun setIconOffset(offset: Expression) + fun setIconOffset(offset: CompiledExpression) - fun setIconAnchor(anchor: Expression>) + fun setIconAnchor(anchor: CompiledExpression) - fun setIconPitchAlignment(pitchAlignment: Expression>) + fun setIconPitchAlignment(pitchAlignment: CompiledExpression) - fun setIconOpacity(opacity: Expression) + fun setIconOpacity(opacity: CompiledExpression) - fun setIconColor(color: Expression) + fun setIconColor(color: CompiledExpression) - fun setIconHaloColor(haloColor: Expression) + fun setIconHaloColor(haloColor: CompiledExpression) - fun setIconHaloWidth(haloWidth: Expression) + fun setIconHaloWidth(haloWidth: CompiledExpression) - fun setIconHaloBlur(haloBlur: Expression) + fun setIconHaloBlur(haloBlur: CompiledExpression) - fun setIconTranslate(translate: Expression) + fun setIconTranslate(translate: CompiledExpression) - fun setIconTranslateAnchor(translateAnchor: Expression>) + fun setIconTranslateAnchor(translateAnchor: CompiledExpression) - fun setTextPitchAlignment(pitchAlignment: Expression>) + fun setTextPitchAlignment(pitchAlignment: CompiledExpression) - fun setTextRotationAlignment(rotationAlignment: Expression>) + fun setTextRotationAlignment(rotationAlignment: CompiledExpression) - fun setTextField(field: Expression>) + fun setTextField(field: CompiledExpression) - fun setTextFont(font: Expression>) + fun setTextFont(font: CompiledExpression>) - fun setTextSize(size: Expression) + fun setTextSize(size: CompiledExpression) - fun setTextMaxWidth(maxWidth: Expression) + fun setTextMaxWidth(maxWidth: CompiledExpression) - fun setTextLineHeight(lineHeight: Expression) + fun setTextLineHeight(lineHeight: CompiledExpression) - fun setTextLetterSpacing(letterSpacing: Expression) + fun setTextLetterSpacing(letterSpacing: CompiledExpression) - fun setTextJustify(justify: Expression>) + fun setTextJustify(justify: CompiledExpression) - fun setTextRadialOffset(radialOffset: Expression) + fun setTextRadialOffset(radialOffset: CompiledExpression) - fun setTextVariableAnchor(variableAnchor: Expression>>) + fun setTextVariableAnchor(variableAnchor: CompiledExpression>) - fun setTextVariableAnchorOffset(variableAnchorOffset: Expression) + fun setTextVariableAnchorOffset( + variableAnchorOffset: CompiledExpression + ) - fun setTextAnchor(anchor: Expression>) + fun setTextAnchor(anchor: CompiledExpression) - fun setTextMaxAngle(maxAngle: Expression) + fun setTextMaxAngle(maxAngle: CompiledExpression) - fun setTextWritingMode(writingMode: Expression>>) + fun setTextWritingMode(writingMode: CompiledExpression>) - fun setTextRotate(rotate: Expression) + fun setTextRotate(rotate: CompiledExpression) - fun setTextPadding(padding: Expression) + fun setTextPadding(padding: CompiledExpression) - fun setTextKeepUpright(keepUpright: Expression) + fun setTextKeepUpright(keepUpright: CompiledExpression) - fun setTextTransform(transform: Expression>) + fun setTextTransform(transform: CompiledExpression) - fun setTextOffset(offset: Expression) + fun setTextOffset(offset: CompiledExpression) - fun setTextAllowOverlap(allowOverlap: Expression) + fun setTextAllowOverlap(allowOverlap: CompiledExpression) - @JsOnlyApi fun setTextOverlap(overlap: Expression) + fun setTextOverlap(overlap: CompiledExpression) - fun setTextIgnorePlacement(ignorePlacement: Expression) + fun setTextIgnorePlacement(ignorePlacement: CompiledExpression) - fun setTextOptional(optional: Expression) + fun setTextOptional(optional: CompiledExpression) - fun setTextOpacity(opacity: Expression) + fun setTextOpacity(opacity: CompiledExpression) - fun setTextColor(color: Expression) + fun setTextColor(color: CompiledExpression) - fun setTextHaloColor(haloColor: Expression) + fun setTextHaloColor(haloColor: CompiledExpression) - fun setTextHaloWidth(haloWidth: Expression) + fun setTextHaloWidth(haloWidth: CompiledExpression) - fun setTextHaloBlur(haloBlur: Expression) + fun setTextHaloBlur(haloBlur: CompiledExpression) - fun setTextTranslate(translate: Expression) + fun setTextTranslate(translate: CompiledExpression) - fun setTextTranslateAnchor(translateAnchor: Expression>) + fun setTextTranslateAnchor(translateAnchor: CompiledExpression) } 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 e337abbc..7101a7c1 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,7 @@ package dev.sargunv.maplibrecompose.core.source import androidx.compose.runtime.Immutable -import dev.sargunv.maplibrecompose.core.expression.Expression +import dev.sargunv.maplibrecompose.expressions.ast.Expression /** * @param minZoom Minimum zoom level at which to create vector tiles (lower means more field of view diff --git a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt b/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt deleted file mode 100644 index 80670082..00000000 --- a/lib/maplibre-compose/src/commonMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt +++ /dev/null @@ -1,17 +0,0 @@ -package dev.sargunv.maplibrecompose.core.util - -@RequiresOptIn( - level = RequiresOptIn.Level.WARNING, - message = "This API is only available on platforms using MapLibre Native.", -) -@Retention(AnnotationRetention.BINARY) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) -public annotation class NativeOnlyApi - -@RequiresOptIn( - level = RequiresOptIn.Level.WARNING, - message = "This API is only available on platforms using MapLibre JS.", -) -@Retention(AnnotationRetention.BINARY) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) -public annotation class JsOnlyApi diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt index f5e50e3a..7024e0eb 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt @@ -1,24 +1,23 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue internal actual class BackgroundLayer actual constructor(id: String) : Layer() { override val impl: Nothing = TODO() - actual fun setBackgroundColor(color: Expression) { + actual fun setBackgroundColor(color: CompiledExpression) { TODO() } - actual fun setBackgroundPattern(pattern: Expression>) { + actual fun setBackgroundPattern(pattern: CompiledExpression) { TODO() } - actual fun setBackgroundOpacity(opacity: Expression) { + actual fun setBackgroundOpacity(opacity: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt index 35531abd..65d63238 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt @@ -1,16 +1,15 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.CirclePitchAlignment -import dev.sargunv.maplibrecompose.core.expression.CirclePitchScale -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchScale +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class CircleLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -18,55 +17,55 @@ internal actual class CircleLayer actual constructor(id: String, source: Source) actual override var sourceLayer: String = TODO() - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { TODO() } - actual fun setCircleSortKey(sortKey: Expression) { + actual fun setCircleSortKey(sortKey: CompiledExpression) { TODO() } - actual fun setCircleRadius(radius: Expression) { + actual fun setCircleRadius(radius: CompiledExpression) { TODO() } - actual fun setCircleColor(color: Expression) { + actual fun setCircleColor(color: CompiledExpression) { TODO() } - actual fun setCircleBlur(blur: Expression) { + actual fun setCircleBlur(blur: CompiledExpression) { TODO() } - actual fun setCircleOpacity(opacity: Expression) { + actual fun setCircleOpacity(opacity: CompiledExpression) { TODO() } - actual fun setCircleTranslate(translate: Expression) { + actual fun setCircleTranslate(translate: CompiledExpression) { TODO() } - actual fun setCircleTranslateAnchor(translateAnchor: Expression>) { + actual fun setCircleTranslateAnchor(translateAnchor: CompiledExpression) { TODO() } - actual fun setCirclePitchScale(pitchScale: Expression>) { + actual fun setCirclePitchScale(pitchScale: CompiledExpression) { TODO() } - actual fun setCirclePitchAlignment(pitchAlignment: Expression>) { + actual fun setCirclePitchAlignment(pitchAlignment: CompiledExpression) { TODO() } - actual fun setCircleStrokeWidth(strokeWidth: Expression) { + actual fun setCircleStrokeWidth(strokeWidth: CompiledExpression) { TODO() } - actual fun setCircleStrokeColor(strokeColor: Expression) { + actual fun setCircleStrokeColor(strokeColor: CompiledExpression) { TODO() } - actual fun setCircleStrokeOpacity(strokeOpacity: Expression) { + actual fun setCircleStrokeOpacity(strokeOpacity: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt index 253acbfe..0e08ea0e 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt @@ -1,11 +1,11 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue internal actual sealed class FeatureLayer actual constructor(actual val source: Source) : Layer() { actual abstract var sourceLayer: String - actual abstract fun setFilter(filter: Expression) + actual abstract fun setFilter(filter: CompiledExpression) } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt index 7e6948ff..a4b9273b 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt @@ -1,15 +1,13 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class FillExtrusionLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -17,39 +15,39 @@ internal actual class FillExtrusionLayer actual constructor(id: String, source: actual override var sourceLayer: String = TODO() - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { TODO() } - actual fun setFillExtrusionOpacity(opacity: Expression) { + actual fun setFillExtrusionOpacity(opacity: CompiledExpression) { TODO() } - actual fun setFillExtrusionColor(color: Expression) { + actual fun setFillExtrusionColor(color: CompiledExpression) { TODO() } - actual fun setFillExtrusionTranslate(translate: Expression) { + actual fun setFillExtrusionTranslate(translate: CompiledExpression) { TODO() } - actual fun setFillExtrusionTranslateAnchor(anchor: Expression>) { + actual fun setFillExtrusionTranslateAnchor(anchor: CompiledExpression) { TODO() } - actual fun setFillExtrusionPattern(pattern: Expression>) { + actual fun setFillExtrusionPattern(pattern: CompiledExpression) { TODO() } - actual fun setFillExtrusionHeight(height: Expression) { + actual fun setFillExtrusionHeight(height: CompiledExpression) { TODO() } - actual fun setFillExtrusionBase(base: Expression) { + actual fun setFillExtrusionBase(base: CompiledExpression) { TODO() } - actual fun setFillExtrusionVerticalGradient(verticalGradient: Expression) { + actual fun setFillExtrusionVerticalGradient(verticalGradient: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt index b122086c..6089fd52 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt @@ -1,15 +1,13 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class FillLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -18,39 +16,39 @@ internal actual class FillLayer actual constructor(id: String, source: Source) : actual override var sourceLayer: String = TODO() - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { TODO() } - actual fun setFillSortKey(sortKey: Expression) { + actual fun setFillSortKey(sortKey: CompiledExpression) { TODO() } - actual fun setFillAntialias(antialias: Expression) { + actual fun setFillAntialias(antialias: CompiledExpression) { TODO() } - actual fun setFillOpacity(opacity: Expression) { + actual fun setFillOpacity(opacity: CompiledExpression) { TODO() } - actual fun setFillColor(color: Expression) { + actual fun setFillColor(color: CompiledExpression) { TODO() } - actual fun setFillOutlineColor(outlineColor: Expression) { + actual fun setFillOutlineColor(outlineColor: CompiledExpression) { TODO() } - actual fun setFillTranslate(translate: Expression) { + actual fun setFillTranslate(translate: CompiledExpression) { TODO() } - actual fun setFillTranslateAnchor(translateAnchor: Expression>) { + actual fun setFillTranslateAnchor(translateAnchor: CompiledExpression) { TODO() } - actual fun setFillPattern(pattern: Expression>) { + actual fun setFillPattern(pattern: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt index 36584c9d..64bde753 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt @@ -1,11 +1,11 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue internal actual class HeatmapLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -13,27 +13,27 @@ internal actual class HeatmapLayer actual constructor(id: String, source: Source actual override var sourceLayer: String = TODO() - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { TODO() } - actual fun setHeatmapRadius(radius: Expression) { + actual fun setHeatmapRadius(radius: CompiledExpression) { TODO() } - actual fun setHeatmapWeight(weight: Expression) { + actual fun setHeatmapWeight(weight: CompiledExpression) { TODO() } - actual fun setHeatmapIntensity(intensity: Expression) { + actual fun setHeatmapIntensity(intensity: CompiledExpression) { TODO() } - actual fun setHeatmapColor(color: Expression) { + actual fun setHeatmapColor(color: CompiledExpression) { TODO() } - actual fun setHeatmapOpacity(opacity: Expression) { + actual fun setHeatmapOpacity(opacity: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt index 6dfa3260..709a61a9 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt @@ -1,37 +1,36 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.IlluminationAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor internal actual class HillshadeLayer actual constructor(id: String, actual val source: Source) : Layer() { override val impl = TODO() - actual fun setHillshadeIlluminationDirection(direction: Expression) { + actual fun setHillshadeIlluminationDirection(direction: CompiledExpression) { TODO() } - actual fun setHillshadeIlluminationAnchor(anchor: Expression>) { + actual fun setHillshadeIlluminationAnchor(anchor: CompiledExpression) { TODO() } - actual fun setHillshadeExaggeration(exaggeration: Expression) { + actual fun setHillshadeExaggeration(exaggeration: CompiledExpression) { TODO() } - actual fun setHillshadeShadowColor(shadowColor: Expression) { + actual fun setHillshadeShadowColor(shadowColor: CompiledExpression) { TODO() } - actual fun setHillshadeHighlightColor(highlightColor: Expression) { + actual fun setHillshadeHighlightColor(highlightColor: CompiledExpression) { TODO() } - actual fun setHillshadeAccentColor(accentColor: Expression) { + actual fun setHillshadeAccentColor(accentColor: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt index 174363b4..0737ccfa 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt @@ -1,19 +1,17 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor -import dev.sargunv.maplibrecompose.core.expression.VectorValue import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor +import dev.sargunv.maplibrecompose.expressions.value.VectorValue internal actual class LineLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -22,71 +20,71 @@ internal actual class LineLayer actual constructor(id: String, source: Source) : actual override var sourceLayer: String = TODO() - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { TODO() } - actual fun setLineCap(cap: Expression>) { + actual fun setLineCap(cap: CompiledExpression) { TODO() } - actual fun setLineJoin(join: Expression>) { + actual fun setLineJoin(join: CompiledExpression) { TODO() } - actual fun setLineMiterLimit(miterLimit: Expression) { + actual fun setLineMiterLimit(miterLimit: CompiledExpression) { TODO() } - actual fun setLineRoundLimit(roundLimit: Expression) { + actual fun setLineRoundLimit(roundLimit: CompiledExpression) { TODO() } - actual fun setLineSortKey(sortKey: Expression) { + actual fun setLineSortKey(sortKey: CompiledExpression) { TODO() } - actual fun setLineOpacity(opacity: Expression) { + actual fun setLineOpacity(opacity: CompiledExpression) { TODO() } - actual fun setLineColor(color: Expression) { + actual fun setLineColor(color: CompiledExpression) { TODO() } - actual fun setLineTranslate(translate: Expression) { + actual fun setLineTranslate(translate: CompiledExpression) { TODO() } - actual fun setLineTranslateAnchor(translateAnchor: Expression>) { + actual fun setLineTranslateAnchor(translateAnchor: CompiledExpression) { TODO() } - actual fun setLineWidth(width: Expression) { + actual fun setLineWidth(width: CompiledExpression) { TODO() } - actual fun setLineGapWidth(gapWidth: Expression) { + actual fun setLineGapWidth(gapWidth: CompiledExpression) { TODO() } - actual fun setLineOffset(offset: Expression) { + actual fun setLineOffset(offset: CompiledExpression) { TODO() } - actual fun setLineBlur(blur: Expression) { + actual fun setLineBlur(blur: CompiledExpression) { TODO() } - actual fun setLineDasharray(dasharray: Expression>) { + actual fun setLineDasharray(dasharray: CompiledExpression>) { TODO() } - actual fun setLinePattern(pattern: Expression>) { + actual fun setLinePattern(pattern: CompiledExpression) { TODO() } - actual fun setLineGradient(gradient: Expression) { + actual fun setLineGradient(gradient: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt index d9ee36ac..eef06e8f 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt @@ -1,45 +1,44 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.MillisecondsValue -import dev.sargunv.maplibrecompose.core.expression.RasterResampling import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.RasterResampling internal actual class RasterLayer actual constructor(id: String, actual val source: Source) : Layer() { override val impl = TODO() - actual fun setRasterOpacity(opacity: Expression) { + actual fun setRasterOpacity(opacity: CompiledExpression) { TODO() } - actual fun setRasterHueRotate(hueRotate: Expression) { + actual fun setRasterHueRotate(hueRotate: CompiledExpression) { TODO() } - actual fun setRasterBrightnessMin(brightnessMin: Expression) { + actual fun setRasterBrightnessMin(brightnessMin: CompiledExpression) { TODO() } - actual fun setRasterBrightnessMax(brightnessMax: Expression) { + actual fun setRasterBrightnessMax(brightnessMax: CompiledExpression) { TODO() } - actual fun setRasterSaturation(saturation: Expression) { + actual fun setRasterSaturation(saturation: CompiledExpression) { TODO() } - actual fun setRasterContrast(contrast: Expression) { + actual fun setRasterContrast(contrast: CompiledExpression) { TODO() } - actual fun setRasterResampling(resampling: Expression>) { + actual fun setRasterResampling(resampling: CompiledExpression) { TODO() } - actual fun setRasterFadeDuration(fadeDuration: Expression) { + actual fun setRasterFadeDuration(fadeDuration: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt index 8f8fd1f1..af047326 100644 --- a/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt +++ b/lib/maplibre-compose/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt @@ -1,33 +1,31 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpPaddingValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatOffsetValue -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.FormattedValue -import dev.sargunv.maplibrecompose.core.expression.IconPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.IconRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.IconTextFit -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ListValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.StringValue -import dev.sargunv.maplibrecompose.core.expression.SymbolAnchor -import dev.sargunv.maplibrecompose.core.expression.SymbolPlacement -import dev.sargunv.maplibrecompose.core.expression.SymbolZOrder -import dev.sargunv.maplibrecompose.core.expression.TextJustify -import dev.sargunv.maplibrecompose.core.expression.TextPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.TextRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.TextTransform -import dev.sargunv.maplibrecompose.core.expression.TextVariableAnchorOffsetValue -import dev.sargunv.maplibrecompose.core.expression.TextWritingMode -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.IconPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconTextFit +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.SymbolPlacement +import dev.sargunv.maplibrecompose.expressions.value.SymbolZOrder +import dev.sargunv.maplibrecompose.expressions.value.TextJustify +import dev.sargunv.maplibrecompose.expressions.value.TextPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextTransform +import dev.sargunv.maplibrecompose.expressions.value.TextVariableAnchorOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextWritingMode +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class SymbolLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -35,245 +33,245 @@ internal actual class SymbolLayer actual constructor(id: String, source: Source) actual override var sourceLayer: String = TODO() - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { TODO() } - actual fun setSymbolPlacement(placement: Expression>) { + actual fun setSymbolPlacement(placement: CompiledExpression) { TODO() } - actual fun setSymbolSpacing(spacing: Expression) { + actual fun setSymbolSpacing(spacing: CompiledExpression) { TODO() } - actual fun setSymbolAvoidEdges(avoidEdges: Expression) { + actual fun setSymbolAvoidEdges(avoidEdges: CompiledExpression) { TODO() } - actual fun setSymbolSortKey(sortKey: Expression) { + actual fun setSymbolSortKey(sortKey: CompiledExpression) { TODO() } - actual fun setSymbolZOrder(zOrder: Expression>) { + actual fun setSymbolZOrder(zOrder: CompiledExpression) { TODO() } - actual fun setIconAllowOverlap(allowOverlap: Expression) { + actual fun setIconAllowOverlap(allowOverlap: CompiledExpression) { TODO() } - actual fun setIconOverlap(overlap: Expression) { + actual fun setIconOverlap(overlap: CompiledExpression) { TODO() } - actual fun setIconIgnorePlacement(ignorePlacement: Expression) { + actual fun setIconIgnorePlacement(ignorePlacement: CompiledExpression) { TODO() } - actual fun setIconOptional(optional: Expression) { + actual fun setIconOptional(optional: CompiledExpression) { TODO() } actual fun setIconRotationAlignment( - rotationAlignment: Expression> + rotationAlignment: CompiledExpression ) { TODO() } - actual fun setIconSize(size: Expression) { + actual fun setIconSize(size: CompiledExpression) { TODO() } - actual fun setIconTextFit(textFit: Expression>) { + actual fun setIconTextFit(textFit: CompiledExpression) { TODO() } - actual fun setIconTextFitPadding(textFitPadding: Expression) { + actual fun setIconTextFitPadding(textFitPadding: CompiledExpression) { TODO() } - actual fun setIconImage(image: Expression>) { + actual fun setIconImage(image: CompiledExpression) { TODO() } - actual fun setIconRotate(rotate: Expression) { + actual fun setIconRotate(rotate: CompiledExpression) { TODO() } - actual fun setIconPadding(padding: Expression) { + actual fun setIconPadding(padding: CompiledExpression) { TODO() } - actual fun setIconKeepUpright(keepUpright: Expression) { + actual fun setIconKeepUpright(keepUpright: CompiledExpression) { TODO() } - actual fun setIconOffset(offset: Expression) { + actual fun setIconOffset(offset: CompiledExpression) { TODO() } - actual fun setIconAnchor(anchor: Expression>) { + actual fun setIconAnchor(anchor: CompiledExpression) { TODO() } - actual fun setIconPitchAlignment(pitchAlignment: Expression>) { + actual fun setIconPitchAlignment(pitchAlignment: CompiledExpression) { TODO() } - actual fun setIconOpacity(opacity: Expression) { + actual fun setIconOpacity(opacity: CompiledExpression) { TODO() } - actual fun setIconColor(color: Expression) { + actual fun setIconColor(color: CompiledExpression) { TODO() } - actual fun setIconHaloColor(haloColor: Expression) { + actual fun setIconHaloColor(haloColor: CompiledExpression) { TODO() } - actual fun setIconHaloWidth(haloWidth: Expression) { + actual fun setIconHaloWidth(haloWidth: CompiledExpression) { TODO() } - actual fun setIconHaloBlur(haloBlur: Expression) { + actual fun setIconHaloBlur(haloBlur: CompiledExpression) { TODO() } - actual fun setIconTranslate(translate: Expression) { + actual fun setIconTranslate(translate: CompiledExpression) { TODO() } - actual fun setIconTranslateAnchor(translateAnchor: Expression>) { + actual fun setIconTranslateAnchor(translateAnchor: CompiledExpression) { TODO() } - actual fun setTextPitchAlignment(pitchAlignment: Expression>) { + actual fun setTextPitchAlignment(pitchAlignment: CompiledExpression) { TODO() } actual fun setTextRotationAlignment( - rotationAlignment: Expression> + rotationAlignment: CompiledExpression ) { TODO() } - actual fun setTextField(field: Expression>) { + actual fun setTextField(field: CompiledExpression) { TODO() } - actual fun setTextFont(font: Expression>) { + actual fun setTextFont(font: CompiledExpression>) { TODO() } - actual fun setTextSize(size: Expression) { + actual fun setTextSize(size: CompiledExpression) { TODO() } - actual fun setTextMaxWidth(maxWidth: Expression) { + actual fun setTextMaxWidth(maxWidth: CompiledExpression) { TODO() } - actual fun setTextLineHeight(lineHeight: Expression) { + actual fun setTextLineHeight(lineHeight: CompiledExpression) { TODO() } - actual fun setTextLetterSpacing(letterSpacing: Expression) { + actual fun setTextLetterSpacing(letterSpacing: CompiledExpression) { TODO() } - actual fun setTextJustify(justify: Expression>) { + actual fun setTextJustify(justify: CompiledExpression) { TODO() } - actual fun setTextRadialOffset(radialOffset: Expression) { + actual fun setTextRadialOffset(radialOffset: CompiledExpression) { TODO() } - actual fun setTextVariableAnchor(variableAnchor: Expression>>) { + actual fun setTextVariableAnchor(variableAnchor: CompiledExpression>) { TODO() } actual fun setTextVariableAnchorOffset( - variableAnchorOffset: Expression + variableAnchorOffset: CompiledExpression ) { TODO() } - actual fun setTextAnchor(anchor: Expression>) { + actual fun setTextAnchor(anchor: CompiledExpression) { TODO() } - actual fun setTextMaxAngle(maxAngle: Expression) { + actual fun setTextMaxAngle(maxAngle: CompiledExpression) { TODO() } - actual fun setTextWritingMode(writingMode: Expression>>) { + actual fun setTextWritingMode(writingMode: CompiledExpression>) { TODO() } - actual fun setTextRotate(rotate: Expression) { + actual fun setTextRotate(rotate: CompiledExpression) { TODO() } - actual fun setTextPadding(padding: Expression) { + actual fun setTextPadding(padding: CompiledExpression) { TODO() } - actual fun setTextKeepUpright(keepUpright: Expression) { + actual fun setTextKeepUpright(keepUpright: CompiledExpression) { TODO() } - actual fun setTextTransform(transform: Expression>) { + actual fun setTextTransform(transform: CompiledExpression) { TODO() } - actual fun setTextOffset(offset: Expression) { + actual fun setTextOffset(offset: CompiledExpression) { TODO() } - actual fun setTextAllowOverlap(allowOverlap: Expression) { + actual fun setTextAllowOverlap(allowOverlap: CompiledExpression) { TODO() } - actual fun setTextOverlap(overlap: Expression) { + actual fun setTextOverlap(overlap: CompiledExpression) { TODO() } - actual fun setTextIgnorePlacement(ignorePlacement: Expression) { + actual fun setTextIgnorePlacement(ignorePlacement: CompiledExpression) { TODO() } - actual fun setTextOptional(optional: Expression) { + actual fun setTextOptional(optional: CompiledExpression) { TODO() } - actual fun setTextOpacity(opacity: Expression) { + actual fun setTextOpacity(opacity: CompiledExpression) { TODO() } - actual fun setTextColor(color: Expression) { + actual fun setTextColor(color: CompiledExpression) { TODO() } - actual fun setTextHaloColor(haloColor: Expression) { + actual fun setTextHaloColor(haloColor: CompiledExpression) { TODO() } - actual fun setTextHaloWidth(haloWidth: Expression) { + actual fun setTextHaloWidth(haloWidth: CompiledExpression) { TODO() } - actual fun setTextHaloBlur(haloBlur: Expression) { + actual fun setTextHaloBlur(haloBlur: CompiledExpression) { TODO() } - actual fun setTextTranslate(translate: Expression) { + actual fun setTextTranslate(translate: CompiledExpression) { TODO() } - actual fun setTextTranslateAnchor(translateAnchor: Expression>) { + actual fun setTextTranslateAnchor(translateAnchor: CompiledExpression) { TODO() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt index e42479fd..1363abaf 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/IosMap.kt @@ -42,8 +42,6 @@ import cocoapods.MapLibre.MLNOrnamentPositionTopRight import cocoapods.MapLibre.MLNStyle import cocoapods.MapLibre.MLNZoomLevelForAltitude import cocoapods.MapLibre.allowsTilting -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression import dev.sargunv.maplibrecompose.core.util.toBoundingBox import dev.sargunv.maplibrecompose.core.util.toCGPoint import dev.sargunv.maplibrecompose.core.util.toCGRect @@ -53,6 +51,8 @@ import dev.sargunv.maplibrecompose.core.util.toFeature import dev.sargunv.maplibrecompose.core.util.toMLNOrnamentPosition import dev.sargunv.maplibrecompose.core.util.toNSPredicate import dev.sargunv.maplibrecompose.core.util.toPosition +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.Position @@ -180,6 +180,7 @@ internal class IosMap( @ObjCSignatureOverride override fun mapView( mapView: MLNMapView, + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") regionWillChangeWithReason: MLNCameraChangeReason, animated: Boolean, ) { @@ -203,6 +204,7 @@ internal class IosMap( @ObjCSignatureOverride override fun mapView( mapView: MLNMapView, + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") regionDidChangeWithReason: MLNCameraChangeReason, animated: Boolean, ) { @@ -439,7 +441,7 @@ internal class IosMap( override fun queryRenderedFeatures( offset: DpOffset, layerIds: Set?, - predicate: Expression?, + predicate: CompiledExpression?, ): List = mapView .visibleFeaturesAtPoint( @@ -452,7 +454,7 @@ internal class IosMap( override fun queryRenderedFeatures( rect: DpRect, layerIds: Set?, - predicate: Expression?, + predicate: CompiledExpression?, ): List = mapView .visibleFeaturesInRect( diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt index 38977c56..4dce19d2 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt @@ -1,28 +1,28 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNBackgroundStyleLayer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue import dev.sargunv.maplibrecompose.core.util.toNSExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue internal actual class BackgroundLayer actual constructor(id: String) : Layer() { override val impl = MLNBackgroundStyleLayer(id) - actual fun setBackgroundColor(color: Expression) { + actual fun setBackgroundColor(color: CompiledExpression) { impl.backgroundColor = color.toNSExpression() } - actual fun setBackgroundPattern(pattern: Expression>) { + actual fun setBackgroundPattern(pattern: CompiledExpression) { // TODO: figure out how to unset a pattern in iOS - if (pattern.value != null) { + if (pattern != NullLiteral) { impl.backgroundPattern = pattern.toNSExpression() } } - actual fun setBackgroundOpacity(opacity: Expression) { + actual fun setBackgroundOpacity(opacity: CompiledExpression) { impl.backgroundOpacity = opacity.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt index 3abc5eaa..aa62a935 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt @@ -1,19 +1,18 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNCircleStyleLayer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.CirclePitchAlignment -import dev.sargunv.maplibrecompose.core.expression.CirclePitchScale -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.core.util.toNSPredicate +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchScale +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class CircleLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -26,55 +25,55 @@ internal actual class CircleLayer actual constructor(id: String, source: Source) impl.sourceLayerIdentifier = value } - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.predicate = filter.toNSPredicate() } - actual fun setCircleSortKey(sortKey: Expression) { + actual fun setCircleSortKey(sortKey: CompiledExpression) { impl.circleSortKey = sortKey.toNSExpression() } - actual fun setCircleRadius(radius: Expression) { + actual fun setCircleRadius(radius: CompiledExpression) { impl.circleRadius = radius.toNSExpression() } - actual fun setCircleColor(color: Expression) { + actual fun setCircleColor(color: CompiledExpression) { impl.circleColor = color.toNSExpression() } - actual fun setCircleBlur(blur: Expression) { + actual fun setCircleBlur(blur: CompiledExpression) { impl.circleBlur = blur.toNSExpression() } - actual fun setCircleOpacity(opacity: Expression) { + actual fun setCircleOpacity(opacity: CompiledExpression) { impl.circleOpacity = opacity.toNSExpression() } - actual fun setCircleTranslate(translate: Expression) { + actual fun setCircleTranslate(translate: CompiledExpression) { impl.circleTranslation = translate.toNSExpression() } - actual fun setCircleTranslateAnchor(translateAnchor: Expression>) { + actual fun setCircleTranslateAnchor(translateAnchor: CompiledExpression) { impl.circleTranslationAnchor = translateAnchor.toNSExpression() } - actual fun setCirclePitchScale(pitchScale: Expression>) { + actual fun setCirclePitchScale(pitchScale: CompiledExpression) { impl.circleScaleAlignment = pitchScale.toNSExpression() } - actual fun setCirclePitchAlignment(pitchAlignment: Expression>) { + actual fun setCirclePitchAlignment(pitchAlignment: CompiledExpression) { impl.circlePitchAlignment = pitchAlignment.toNSExpression() } - actual fun setCircleStrokeWidth(strokeWidth: Expression) { + actual fun setCircleStrokeWidth(strokeWidth: CompiledExpression) { impl.circleStrokeWidth = strokeWidth.toNSExpression() } - actual fun setCircleStrokeColor(strokeColor: Expression) { + actual fun setCircleStrokeColor(strokeColor: CompiledExpression) { impl.circleStrokeColor = strokeColor.toNSExpression() } - actual fun setCircleStrokeOpacity(strokeOpacity: Expression) { + actual fun setCircleStrokeOpacity(strokeOpacity: CompiledExpression) { impl.circleStrokeOpacity = strokeOpacity.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt index 253acbfe..0e08ea0e 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt @@ -1,11 +1,11 @@ package dev.sargunv.maplibrecompose.core.layer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue internal actual sealed class FeatureLayer actual constructor(actual val source: Source) : Layer() { actual abstract var sourceLayer: String - actual abstract fun setFilter(filter: Expression) + actual abstract fun setFilter(filter: CompiledExpression) } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt index 724702c9..802e0ac5 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt @@ -1,18 +1,17 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNFillExtrusionStyleLayer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.core.util.toNSPredicate +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class FillExtrusionLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -25,40 +24,40 @@ internal actual class FillExtrusionLayer actual constructor(id: String, source: impl.sourceLayerIdentifier = value } - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.predicate = filter.toNSPredicate() } - actual fun setFillExtrusionOpacity(opacity: Expression) { + actual fun setFillExtrusionOpacity(opacity: CompiledExpression) { impl.fillExtrusionOpacity = opacity.toNSExpression() } - actual fun setFillExtrusionColor(color: Expression) { + actual fun setFillExtrusionColor(color: CompiledExpression) { impl.fillExtrusionColor = color.toNSExpression() } - actual fun setFillExtrusionTranslate(translate: Expression) { + actual fun setFillExtrusionTranslate(translate: CompiledExpression) { impl.fillExtrusionTranslation = translate.toNSExpression() } - actual fun setFillExtrusionTranslateAnchor(anchor: Expression>) { + actual fun setFillExtrusionTranslateAnchor(anchor: CompiledExpression) { impl.fillExtrusionTranslationAnchor = anchor.toNSExpression() } - actual fun setFillExtrusionPattern(pattern: Expression>) { + actual fun setFillExtrusionPattern(pattern: CompiledExpression) { // TODO figure out how to unset pattern - if (pattern.value != null) impl.fillExtrusionPattern = pattern.toNSExpression() + if (pattern != NullLiteral) impl.fillExtrusionPattern = pattern.toNSExpression() } - actual fun setFillExtrusionHeight(height: Expression) { + actual fun setFillExtrusionHeight(height: CompiledExpression) { impl.fillExtrusionHeight = height.toNSExpression() } - actual fun setFillExtrusionBase(base: Expression) { + actual fun setFillExtrusionBase(base: CompiledExpression) { impl.fillExtrusionBase = base.toNSExpression() } - actual fun setFillExtrusionVerticalGradient(verticalGradient: Expression) { + actual fun setFillExtrusionVerticalGradient(verticalGradient: CompiledExpression) { impl.fillExtrusionHasVerticalGradient = verticalGradient.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt index a38634a5..b77d0af2 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt @@ -1,18 +1,17 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNFillStyleLayer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.core.util.toNSPredicate +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class FillLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -25,41 +24,41 @@ internal actual class FillLayer actual constructor(id: String, source: Source) : impl.sourceLayerIdentifier = value } - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.predicate = filter.toNSPredicate() } - actual fun setFillSortKey(sortKey: Expression) { + actual fun setFillSortKey(sortKey: CompiledExpression) { impl.fillSortKey = sortKey.toNSExpression() } - actual fun setFillAntialias(antialias: Expression) { + actual fun setFillAntialias(antialias: CompiledExpression) { impl.fillAntialiased = antialias.toNSExpression() } - actual fun setFillOpacity(opacity: Expression) { + actual fun setFillOpacity(opacity: CompiledExpression) { impl.fillOpacity = opacity.toNSExpression() } - actual fun setFillColor(color: Expression) { + actual fun setFillColor(color: CompiledExpression) { impl.fillColor = color.toNSExpression() } - actual fun setFillOutlineColor(outlineColor: Expression) { + actual fun setFillOutlineColor(outlineColor: CompiledExpression) { impl.fillOutlineColor = outlineColor.toNSExpression() } - actual fun setFillTranslate(translate: Expression) { + actual fun setFillTranslate(translate: CompiledExpression) { impl.fillTranslation = translate.toNSExpression() } - actual fun setFillTranslateAnchor(translateAnchor: Expression>) { + actual fun setFillTranslateAnchor(translateAnchor: CompiledExpression) { impl.fillTranslationAnchor = translateAnchor.toNSExpression() } - actual fun setFillPattern(pattern: Expression>) { + actual fun setFillPattern(pattern: CompiledExpression) { // TODO: figure out how to unset a pattern in iOS - if (pattern.value != null) { + if (pattern != NullLiteral) { impl.fillPattern = pattern.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt index 9925dfc8..c046a7a0 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt @@ -1,14 +1,14 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNHeatmapStyleLayer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.core.util.toNSPredicate +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue internal actual class HeatmapLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -21,27 +21,27 @@ internal actual class HeatmapLayer actual constructor(id: String, source: Source impl.sourceLayerIdentifier = value } - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.predicate = filter.toNSPredicate() } - actual fun setHeatmapRadius(radius: Expression) { + actual fun setHeatmapRadius(radius: CompiledExpression) { impl.heatmapRadius = radius.toNSExpression() } - actual fun setHeatmapWeight(weight: Expression) { + actual fun setHeatmapWeight(weight: CompiledExpression) { impl.heatmapWeight = weight.toNSExpression() } - actual fun setHeatmapIntensity(intensity: Expression) { + actual fun setHeatmapIntensity(intensity: CompiledExpression) { impl.heatmapIntensity = intensity.toNSExpression() } - actual fun setHeatmapColor(color: Expression) { + actual fun setHeatmapColor(color: CompiledExpression) { impl.heatmapColor = color.toNSExpression() } - actual fun setHeatmapOpacity(opacity: Expression) { + actual fun setHeatmapOpacity(opacity: CompiledExpression) { impl.heatmapOpacity = opacity.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt index aef290d0..6bbeb070 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt @@ -1,40 +1,39 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNHillshadeStyleLayer -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.IlluminationAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor internal actual class HillshadeLayer actual constructor(id: String, actual val source: Source) : Layer() { override val impl = MLNHillshadeStyleLayer(id, source.impl) - actual fun setHillshadeIlluminationDirection(direction: Expression) { + actual fun setHillshadeIlluminationDirection(direction: CompiledExpression) { impl.hillshadeIlluminationDirection = direction.toNSExpression() } - actual fun setHillshadeIlluminationAnchor(anchor: Expression>) { + actual fun setHillshadeIlluminationAnchor(anchor: CompiledExpression) { impl.hillshadeIlluminationAnchor = anchor.toNSExpression() } - actual fun setHillshadeExaggeration(exaggeration: Expression) { + actual fun setHillshadeExaggeration(exaggeration: CompiledExpression) { impl.hillshadeExaggeration = exaggeration.toNSExpression() } - actual fun setHillshadeShadowColor(shadowColor: Expression) { + actual fun setHillshadeShadowColor(shadowColor: CompiledExpression) { impl.hillshadeShadowColor = shadowColor.toNSExpression() } - actual fun setHillshadeHighlightColor(highlightColor: Expression) { + actual fun setHillshadeHighlightColor(highlightColor: CompiledExpression) { impl.hillshadeHighlightColor = highlightColor.toNSExpression() } - actual fun setHillshadeAccentColor(accentColor: Expression) { + actual fun setHillshadeAccentColor(accentColor: CompiledExpression) { impl.hillshadeAccentColor = accentColor.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt index 30ccc022..8d1cb20e 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt @@ -1,22 +1,21 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNLineStyleLayer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.LineCap -import dev.sargunv.maplibrecompose.core.expression.LineJoin -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor -import dev.sargunv.maplibrecompose.core.expression.VectorValue import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.core.util.toNSPredicate +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor +import dev.sargunv.maplibrecompose.expressions.value.VectorValue internal actual class LineLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -29,74 +28,74 @@ internal actual class LineLayer actual constructor(id: String, source: Source) : impl.sourceLayerIdentifier = value } - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.predicate = filter.toNSPredicate() } - actual fun setLineCap(cap: Expression>) { + actual fun setLineCap(cap: CompiledExpression) { impl.lineCap = cap.toNSExpression() } - actual fun setLineJoin(join: Expression>) { + actual fun setLineJoin(join: CompiledExpression) { impl.lineJoin = join.toNSExpression() } - actual fun setLineMiterLimit(miterLimit: Expression) { + actual fun setLineMiterLimit(miterLimit: CompiledExpression) { impl.lineMiterLimit = miterLimit.toNSExpression() } - actual fun setLineRoundLimit(roundLimit: Expression) { + actual fun setLineRoundLimit(roundLimit: CompiledExpression) { impl.lineRoundLimit = roundLimit.toNSExpression() } - actual fun setLineSortKey(sortKey: Expression) { + actual fun setLineSortKey(sortKey: CompiledExpression) { impl.lineSortKey = sortKey.toNSExpression() } - actual fun setLineOpacity(opacity: Expression) { + actual fun setLineOpacity(opacity: CompiledExpression) { impl.lineOpacity = opacity.toNSExpression() } - actual fun setLineColor(color: Expression) { + actual fun setLineColor(color: CompiledExpression) { impl.lineColor = color.toNSExpression() } - actual fun setLineTranslate(translate: Expression) { + actual fun setLineTranslate(translate: CompiledExpression) { impl.lineTranslation = translate.toNSExpression() } - actual fun setLineTranslateAnchor(translateAnchor: Expression>) { + actual fun setLineTranslateAnchor(translateAnchor: CompiledExpression) { impl.lineTranslationAnchor = translateAnchor.toNSExpression() } - actual fun setLineWidth(width: Expression) { + actual fun setLineWidth(width: CompiledExpression) { impl.lineWidth = width.toNSExpression() } - actual fun setLineGapWidth(gapWidth: Expression) { + actual fun setLineGapWidth(gapWidth: CompiledExpression) { impl.lineGapWidth = gapWidth.toNSExpression() } - actual fun setLineOffset(offset: Expression) { + actual fun setLineOffset(offset: CompiledExpression) { impl.lineOffset = offset.toNSExpression() } - actual fun setLineBlur(blur: Expression) { + actual fun setLineBlur(blur: CompiledExpression) { impl.lineBlur = blur.toNSExpression() } - actual fun setLineDasharray(dasharray: Expression>) { + actual fun setLineDasharray(dasharray: CompiledExpression>) { impl.lineDashPattern = dasharray.toNSExpression() } - actual fun setLinePattern(pattern: Expression>) { + actual fun setLinePattern(pattern: CompiledExpression) { // TODO: figure out how to unset a pattern in iOS - if (pattern.value != null) { + if (pattern != NullLiteral) { impl.linePattern = pattern.toNSExpression() } } - actual fun setLineGradient(gradient: Expression) { + actual fun setLineGradient(gradient: CompiledExpression) { impl.lineGradient = gradient.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt index e6f0da65..72739b01 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt @@ -1,48 +1,47 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNRasterStyleLayer -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.MillisecondsValue -import dev.sargunv.maplibrecompose.core.expression.RasterResampling import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.RasterResampling internal actual class RasterLayer actual constructor(id: String, actual val source: Source) : Layer() { override val impl = MLNRasterStyleLayer(id, source.impl) - actual fun setRasterOpacity(opacity: Expression) { + actual fun setRasterOpacity(opacity: CompiledExpression) { impl.rasterOpacity = opacity.toNSExpression() } - actual fun setRasterHueRotate(hueRotate: Expression) { + actual fun setRasterHueRotate(hueRotate: CompiledExpression) { impl.rasterHueRotation = hueRotate.toNSExpression() } - actual fun setRasterBrightnessMin(brightnessMin: Expression) { + actual fun setRasterBrightnessMin(brightnessMin: CompiledExpression) { impl.minimumRasterBrightness = brightnessMin.toNSExpression() } - actual fun setRasterBrightnessMax(brightnessMax: Expression) { + actual fun setRasterBrightnessMax(brightnessMax: CompiledExpression) { impl.maximumRasterBrightness = brightnessMax.toNSExpression() } - actual fun setRasterSaturation(saturation: Expression) { + actual fun setRasterSaturation(saturation: CompiledExpression) { impl.rasterSaturation = saturation.toNSExpression() } - actual fun setRasterContrast(contrast: Expression) { + actual fun setRasterContrast(contrast: CompiledExpression) { impl.rasterContrast = contrast.toNSExpression() } - actual fun setRasterResampling(resampling: Expression>) { + actual fun setRasterResampling(resampling: CompiledExpression) { impl.rasterResamplingMode = resampling.toNSExpression() } - actual fun setRasterFadeDuration(fadeDuration: Expression) { + actual fun setRasterFadeDuration(fadeDuration: CompiledExpression) { impl.rasterFadeDuration = fadeDuration.toNSExpression() } } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt index 8871e634..5b87a74a 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt @@ -1,36 +1,35 @@ package dev.sargunv.maplibrecompose.core.layer import cocoapods.MapLibre.MLNSymbolStyleLayer -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.ColorValue -import dev.sargunv.maplibrecompose.core.expression.DpOffsetValue -import dev.sargunv.maplibrecompose.core.expression.DpPaddingValue -import dev.sargunv.maplibrecompose.core.expression.DpValue -import dev.sargunv.maplibrecompose.core.expression.EnumValue -import dev.sargunv.maplibrecompose.core.expression.Expression -import dev.sargunv.maplibrecompose.core.expression.FloatOffsetValue -import dev.sargunv.maplibrecompose.core.expression.FloatValue -import dev.sargunv.maplibrecompose.core.expression.FormattedValue -import dev.sargunv.maplibrecompose.core.expression.IconPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.IconRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.IconTextFit -import dev.sargunv.maplibrecompose.core.expression.ImageValue -import dev.sargunv.maplibrecompose.core.expression.ListValue -import dev.sargunv.maplibrecompose.core.expression.ResolvedValue -import dev.sargunv.maplibrecompose.core.expression.StringValue -import dev.sargunv.maplibrecompose.core.expression.SymbolAnchor -import dev.sargunv.maplibrecompose.core.expression.SymbolPlacement -import dev.sargunv.maplibrecompose.core.expression.SymbolZOrder -import dev.sargunv.maplibrecompose.core.expression.TextJustify -import dev.sargunv.maplibrecompose.core.expression.TextPitchAlignment -import dev.sargunv.maplibrecompose.core.expression.TextRotationAlignment -import dev.sargunv.maplibrecompose.core.expression.TextTransform -import dev.sargunv.maplibrecompose.core.expression.TextVariableAnchorOffsetValue -import dev.sargunv.maplibrecompose.core.expression.TextWritingMode -import dev.sargunv.maplibrecompose.core.expression.TranslateAnchor import dev.sargunv.maplibrecompose.core.source.Source import dev.sargunv.maplibrecompose.core.util.toNSExpression import dev.sargunv.maplibrecompose.core.util.toNSPredicate +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.IconPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconTextFit +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.SymbolPlacement +import dev.sargunv.maplibrecompose.expressions.value.SymbolZOrder +import dev.sargunv.maplibrecompose.expressions.value.TextJustify +import dev.sargunv.maplibrecompose.expressions.value.TextPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextTransform +import dev.sargunv.maplibrecompose.expressions.value.TextVariableAnchorOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextWritingMode +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor internal actual class SymbolLayer actual constructor(id: String, source: Source) : FeatureLayer(source) { @@ -43,248 +42,248 @@ internal actual class SymbolLayer actual constructor(id: String, source: Source) impl.sourceLayerIdentifier = value } - actual override fun setFilter(filter: Expression) { + actual override fun setFilter(filter: CompiledExpression) { impl.predicate = filter.toNSPredicate() } - actual fun setSymbolPlacement(placement: Expression>) { + actual fun setSymbolPlacement(placement: CompiledExpression) { impl.symbolPlacement = placement.toNSExpression() } - actual fun setSymbolSpacing(spacing: Expression) { + actual fun setSymbolSpacing(spacing: CompiledExpression) { impl.symbolSpacing = spacing.toNSExpression() } - actual fun setSymbolAvoidEdges(avoidEdges: Expression) { + actual fun setSymbolAvoidEdges(avoidEdges: CompiledExpression) { impl.symbolAvoidsEdges = avoidEdges.toNSExpression() } - actual fun setSymbolSortKey(sortKey: Expression) { + actual fun setSymbolSortKey(sortKey: CompiledExpression) { impl.symbolSortKey = sortKey.toNSExpression() } - actual fun setSymbolZOrder(zOrder: Expression>) { + actual fun setSymbolZOrder(zOrder: CompiledExpression) { impl.symbolZOrder = zOrder.toNSExpression() } - actual fun setIconAllowOverlap(allowOverlap: Expression) { + actual fun setIconAllowOverlap(allowOverlap: CompiledExpression) { impl.iconAllowsOverlap = allowOverlap.toNSExpression() } - actual fun setIconOverlap(overlap: Expression) { + actual fun setIconOverlap(overlap: CompiledExpression) { // TODO: warn not implemented by MapLibre-native iOS yet // impl.iconOverlap = overlap.toNSExpression() } - actual fun setIconIgnorePlacement(ignorePlacement: Expression) { + actual fun setIconIgnorePlacement(ignorePlacement: CompiledExpression) { impl.iconIgnoresPlacement = ignorePlacement.toNSExpression() } - actual fun setIconOptional(optional: Expression) { + actual fun setIconOptional(optional: CompiledExpression) { impl.iconOptional = optional.toNSExpression() } actual fun setIconRotationAlignment( - rotationAlignment: Expression> + rotationAlignment: CompiledExpression ) { impl.iconRotationAlignment = rotationAlignment.toNSExpression() } - actual fun setIconSize(size: Expression) { + actual fun setIconSize(size: CompiledExpression) { impl.iconScale = size.toNSExpression() } - actual fun setIconTextFit(textFit: Expression>) { + actual fun setIconTextFit(textFit: CompiledExpression) { impl.iconTextFit = textFit.toNSExpression() } - actual fun setIconTextFitPadding(textFitPadding: Expression) { + actual fun setIconTextFitPadding(textFitPadding: CompiledExpression) { impl.iconTextFitPadding = textFitPadding.toNSExpression() } - actual fun setIconImage(image: Expression>) { + actual fun setIconImage(image: CompiledExpression) { // TODO figure out how to unset an image - if (image.value != null) impl.iconImageName = image.toNSExpression() + if (image != NullLiteral) impl.iconImageName = image.toNSExpression() } - actual fun setIconRotate(rotate: Expression) { + actual fun setIconRotate(rotate: CompiledExpression) { impl.iconRotation = rotate.toNSExpression() } - actual fun setIconPadding(padding: Expression) { + actual fun setIconPadding(padding: CompiledExpression) { impl.iconPadding = padding.toNSExpression() } - actual fun setIconKeepUpright(keepUpright: Expression) { + actual fun setIconKeepUpright(keepUpright: CompiledExpression) { impl.keepsIconUpright = keepUpright.toNSExpression() } - actual fun setIconOffset(offset: Expression) { + actual fun setIconOffset(offset: CompiledExpression) { impl.iconOffset = offset.toNSExpression() } - actual fun setIconAnchor(anchor: Expression>) { + actual fun setIconAnchor(anchor: CompiledExpression) { impl.iconAnchor = anchor.toNSExpression() } - actual fun setIconPitchAlignment(pitchAlignment: Expression>) { + actual fun setIconPitchAlignment(pitchAlignment: CompiledExpression) { impl.iconPitchAlignment = pitchAlignment.toNSExpression() } - actual fun setIconOpacity(opacity: Expression) { + actual fun setIconOpacity(opacity: CompiledExpression) { impl.iconOpacity = opacity.toNSExpression() } - actual fun setIconColor(color: Expression) { + actual fun setIconColor(color: CompiledExpression) { impl.iconColor = color.toNSExpression() } - actual fun setIconHaloColor(haloColor: Expression) { + actual fun setIconHaloColor(haloColor: CompiledExpression) { impl.iconHaloColor = haloColor.toNSExpression() } - actual fun setIconHaloWidth(haloWidth: Expression) { + actual fun setIconHaloWidth(haloWidth: CompiledExpression) { impl.iconHaloWidth = haloWidth.toNSExpression() } - actual fun setIconHaloBlur(haloBlur: Expression) { + actual fun setIconHaloBlur(haloBlur: CompiledExpression) { impl.iconHaloBlur = haloBlur.toNSExpression() } - actual fun setIconTranslate(translate: Expression) { + actual fun setIconTranslate(translate: CompiledExpression) { impl.iconTranslation = translate.toNSExpression() } - actual fun setIconTranslateAnchor(translateAnchor: Expression>) { + actual fun setIconTranslateAnchor(translateAnchor: CompiledExpression) { impl.iconTranslationAnchor = translateAnchor.toNSExpression() } - actual fun setTextPitchAlignment(pitchAlignment: Expression>) { + actual fun setTextPitchAlignment(pitchAlignment: CompiledExpression) { impl.textPitchAlignment = pitchAlignment.toNSExpression() } actual fun setTextRotationAlignment( - rotationAlignment: Expression> + rotationAlignment: CompiledExpression ) { impl.textRotationAlignment = rotationAlignment.toNSExpression() } - actual fun setTextField(field: Expression>) { + actual fun setTextField(field: CompiledExpression) { impl.text = field.toNSExpression() } - actual fun setTextFont(font: Expression>) { + actual fun setTextFont(font: CompiledExpression>) { impl.textFontNames = font.toNSExpression() } - actual fun setTextSize(size: Expression) { + actual fun setTextSize(size: CompiledExpression) { impl.textFontSize = size.toNSExpression() } - actual fun setTextMaxWidth(maxWidth: Expression) { + actual fun setTextMaxWidth(maxWidth: CompiledExpression) { impl.maximumTextWidth = maxWidth.toNSExpression() } - actual fun setTextLineHeight(lineHeight: Expression) { + actual fun setTextLineHeight(lineHeight: CompiledExpression) { impl.textLineHeight = lineHeight.toNSExpression() } - actual fun setTextLetterSpacing(letterSpacing: Expression) { + actual fun setTextLetterSpacing(letterSpacing: CompiledExpression) { impl.textLetterSpacing = letterSpacing.toNSExpression() } - actual fun setTextJustify(justify: Expression>) { + actual fun setTextJustify(justify: CompiledExpression) { impl.textJustification = justify.toNSExpression() } - actual fun setTextRadialOffset(radialOffset: Expression) { + actual fun setTextRadialOffset(radialOffset: CompiledExpression) { impl.textRadialOffset = radialOffset.toNSExpression() } - actual fun setTextVariableAnchor(variableAnchor: Expression>>) { + actual fun setTextVariableAnchor(variableAnchor: CompiledExpression>) { impl.textVariableAnchor = variableAnchor.toNSExpression() } actual fun setTextVariableAnchorOffset( - variableAnchorOffset: Expression + variableAnchorOffset: CompiledExpression ) { impl.textVariableAnchorOffset = variableAnchorOffset.toNSExpression() } - actual fun setTextAnchor(anchor: Expression>) { + actual fun setTextAnchor(anchor: CompiledExpression) { impl.textAnchor = anchor.toNSExpression() } - actual fun setTextMaxAngle(maxAngle: Expression) { + actual fun setTextMaxAngle(maxAngle: CompiledExpression) { impl.maximumTextAngle = maxAngle.toNSExpression() } - actual fun setTextWritingMode(writingMode: Expression>>) { + actual fun setTextWritingMode(writingMode: CompiledExpression>) { impl.textWritingModes = writingMode.toNSExpression() } - actual fun setTextRotate(rotate: Expression) { + actual fun setTextRotate(rotate: CompiledExpression) { impl.textRotation = rotate.toNSExpression() } - actual fun setTextPadding(padding: Expression) { + actual fun setTextPadding(padding: CompiledExpression) { impl.textPadding = padding.toNSExpression() } - actual fun setTextKeepUpright(keepUpright: Expression) { + actual fun setTextKeepUpright(keepUpright: CompiledExpression) { impl.keepsTextUpright = keepUpright.toNSExpression() } - actual fun setTextTransform(transform: Expression>) { + actual fun setTextTransform(transform: CompiledExpression) { impl.textTransform = transform.toNSExpression() } - actual fun setTextOffset(offset: Expression) { + actual fun setTextOffset(offset: CompiledExpression) { impl.textOffset = offset.toNSExpression() } - actual fun setTextAllowOverlap(allowOverlap: Expression) { + actual fun setTextAllowOverlap(allowOverlap: CompiledExpression) { impl.textAllowsOverlap = allowOverlap.toNSExpression() } - actual fun setTextOverlap(overlap: Expression) { + actual fun setTextOverlap(overlap: CompiledExpression) { // not implemented by MapLibre-native iOS yet // impl.textOverlap = overlap.toNSExpression() } - actual fun setTextIgnorePlacement(ignorePlacement: Expression) { + actual fun setTextIgnorePlacement(ignorePlacement: CompiledExpression) { impl.textIgnoresPlacement = ignorePlacement.toNSExpression() } - actual fun setTextOptional(optional: Expression) { + actual fun setTextOptional(optional: CompiledExpression) { impl.textOptional = optional.toNSExpression() } - actual fun setTextOpacity(opacity: Expression) { + actual fun setTextOpacity(opacity: CompiledExpression) { impl.textOpacity = opacity.toNSExpression() } - actual fun setTextColor(color: Expression) { + actual fun setTextColor(color: CompiledExpression) { impl.textColor = color.toNSExpression() } - actual fun setTextHaloColor(haloColor: Expression) { + actual fun setTextHaloColor(haloColor: CompiledExpression) { impl.textHaloColor = haloColor.toNSExpression() } - actual fun setTextHaloWidth(haloWidth: Expression) { + actual fun setTextHaloWidth(haloWidth: CompiledExpression) { impl.textHaloWidth = haloWidth.toNSExpression() } - actual fun setTextHaloBlur(haloBlur: Expression) { + actual fun setTextHaloBlur(haloBlur: CompiledExpression) { impl.textHaloBlur = haloBlur.toNSExpression() } - actual fun setTextTranslate(translate: Expression) { + actual fun setTextTranslate(translate: CompiledExpression) { impl.textTranslation = translate.toNSExpression() } - actual fun setTextTranslateAnchor(translateAnchor: Expression>) { + actual fun setTextTranslateAnchor(translateAnchor: CompiledExpression) { impl.textTranslationAnchor = translateAnchor.toNSExpression() } } 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 b79fea70..bf113cf6 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 @@ -10,9 +10,10 @@ import cocoapods.MapLibre.MLNShapeSourceOptionMaximumZoomLevel import cocoapods.MapLibre.MLNShapeSourceOptionMaximumZoomLevelForClustering import cocoapods.MapLibre.MLNShapeSourceOptionMinimumZoomLevel import cocoapods.MapLibre.MLNShapeSourceOptionSimplificationTolerance -import dev.sargunv.maplibrecompose.core.expression.Expression 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 io.github.dellisd.spatialk.geojson.GeoJson import platform.Foundation.NSNumber import platform.Foundation.NSURL @@ -43,7 +44,7 @@ public actual class GeoJsonSource : Source { put( MLNShapeSourceOptionClusterProperties, options.clusterProperties.mapValues { (_, p) -> - Expression.ofList(listOf(p.operator, p.mapper.value)).toNSExpression() + FunctionCall.of(p.operator, p.mapper).compile(ExpressionContext.None).toNSExpression() }, ) } diff --git a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt index ae933e80..3f409d9f 100644 --- a/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt +++ b/lib/maplibre-compose/src/iosMain/kotlin/dev/sargunv/maplibrecompose/core/util/util.kt @@ -1,9 +1,6 @@ package dev.sargunv.maplibrecompose.core.util -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.Alignment -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asSkiaBitmap import androidx.compose.ui.unit.DpOffset @@ -22,8 +19,19 @@ import cocoapods.MapLibre.MLNOrnamentPositionTopRight import cocoapods.MapLibre.MLNShape import cocoapods.MapLibre.expressionWithMLNJSONObject import cocoapods.MapLibre.predicateWithMLNJSONObject -import dev.sargunv.maplibrecompose.core.expression.BooleanValue -import dev.sargunv.maplibrecompose.core.expression.Expression +import dev.sargunv.maplibrecompose.expressions.ast.BooleanLiteral +import dev.sargunv.maplibrecompose.expressions.ast.ColorLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.ast.CompiledFunctionCall +import dev.sargunv.maplibrecompose.expressions.ast.CompiledListLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledMapLiteral +import dev.sargunv.maplibrecompose.expressions.ast.CompiledOptions +import dev.sargunv.maplibrecompose.expressions.ast.DpPaddingLiteral +import dev.sargunv.maplibrecompose.expressions.ast.FloatLiteral +import dev.sargunv.maplibrecompose.expressions.ast.NullLiteral +import dev.sargunv.maplibrecompose.expressions.ast.OffsetLiteral +import dev.sargunv.maplibrecompose.expressions.ast.StringLiteral +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue import io.github.dellisd.spatialk.geojson.BoundingBox import io.github.dellisd.spatialk.geojson.Feature import io.github.dellisd.spatialk.geojson.GeoJson @@ -110,25 +118,46 @@ internal fun GeoJson.toMLNShape(): MLNShape { )!! } -internal fun Expression<*>.toNSExpression(): NSExpression = - when (value) { - null -> NSExpression.expressionForConstantValue(null) - else -> NSExpression.expressionWithMLNJSONObject(normalizeJsonLike(value)!!) +internal fun CompiledExpression<*>.toNSExpression(): NSExpression = + if (this == NullLiteral) NSExpression.expressionForConstantValue(null) + else NSExpression.expressionWithMLNJSONObject(normalizeJsonLike(false)!!) + +internal fun CompiledExpression.toNSPredicate(): NSPredicate? = + if (this == NullLiteral) null + else NSPredicate.predicateWithMLNJSONObject(normalizeJsonLike(false)!!) + +private fun buildLiteralList(inLiteral: Boolean, block: MutableList.() -> Unit): List { + return if (inLiteral) { + buildList { block() } + } else { + buildList { + add("literal") + add(buildList { block() }) + } } +} + +private fun buildLiteralMap( + inLiteral: Boolean, + block: MutableMap.() -> Unit, +): Map { + return if (inLiteral) { + buildMap { block() } + } else { + buildMap { put("literal", buildMap { block() }) } + } +} -internal fun Expression.toNSPredicate(): NSPredicate? = - value?.let { NSPredicate.predicateWithMLNJSONObject(normalizeJsonLike(it)!!) } - -private fun normalizeJsonLike(value: Any?): Any? = - when (value) { - null -> null - is Boolean -> value - is Number -> value - is String -> value - is List<*> -> value.map(::normalizeJsonLike) - is Map<*, *> -> value.mapValues { normalizeJsonLike(it.value) } - is Offset -> NSValue.valueWithCGVector(CGVectorMake(value.x.toDouble(), value.y.toDouble())) - is Color -> +private fun CompiledExpression<*>.normalizeJsonLike(inLiteral: Boolean): Any? = + when (this) { + NullLiteral -> null + is BooleanLiteral -> value + is FloatLiteral -> value + is StringLiteral -> value + is OffsetLiteral -> + NSValue.valueWithCGVector(CGVectorMake(value.x.toDouble(), value.y.toDouble())) + + is ColorLiteral -> UIColor.colorWithRed( red = value.red.toDouble(), green = value.green.toDouble(), @@ -136,7 +165,7 @@ private fun normalizeJsonLike(value: Any?): Any? = alpha = value.alpha.toDouble(), ) - is PaddingValues.Absolute -> + is DpPaddingLiteral -> NSValue.valueWithUIEdgeInsets( UIEdgeInsetsMake( top = value.calculateTopPadding().value.toDouble(), @@ -146,7 +175,20 @@ private fun normalizeJsonLike(value: Any?): Any? = ) ) - else -> throw IllegalArgumentException("Unsupported type: ${value::class}") + is CompiledFunctionCall -> + buildList { + add(name) + args.forEachIndexed { i, v -> add(v.normalizeJsonLike(inLiteral || isLiteralArg(i))) } + } + + is CompiledListLiteral<*> -> + buildLiteralList(inLiteral) { value.forEach { add(it.normalizeJsonLike(true)) } } + + is CompiledMapLiteral<*> -> + buildLiteralMap(inLiteral) { value.forEach { (k, v) -> put(k, v.normalizeJsonLike(true)) } } + + is CompiledOptions<*> -> + buildMap { value.forEach { (k, v) -> put(k, v.normalizeJsonLike(inLiteral)) } } } internal fun Alignment.toMLNOrnamentPosition(layoutDir: LayoutDirection): MLNOrnamentPosition { diff --git a/settings.gradle.kts b/settings.gradle.kts index cbb62238..59f1e279 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,4 +33,11 @@ dependencyResolutionManagement { } } -include(":lib:maplibre-compose", ":lib:maplibre-compose-material3", ":lib", ":demo-app", ":") +include( + ":lib:maplibre-compose-expressions", + ":lib:maplibre-compose-material3", + ":lib:maplibre-compose", + ":lib", + ":demo-app", + ":", +)