Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions Examples/ClusteringClassic.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.maptiler.examples

import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import com.maptiler.maptilersdk.MTConfig
import com.maptiler.maptilersdk.map.MTMapOptions
import com.maptiler.maptilersdk.map.MTMapViewClassic
import com.maptiler.maptilersdk.map.MTMapViewController
import com.maptiler.maptilersdk.map.style.MTMapReferenceStyle
import com.maptiler.maptilersdk.map.style.MTMapStyleVariant
import com.maptiler.maptilersdk.map.style.MTStyle
import com.maptiler.maptilersdk.map.style.dsl.Expression
import com.maptiler.maptilersdk.map.style.dsl.Filter
import com.maptiler.maptilersdk.map.style.dsl.MTFeatureKey
import com.maptiler.maptilersdk.map.style.dsl.PropertyValue
import com.maptiler.maptilersdk.map.style.layer.circle.MTCircleLayer
import com.maptiler.maptilersdk.map.style.layer.circle.colorConst
import com.maptiler.maptilersdk.map.style.layer.circle.colorExpr
import com.maptiler.maptilersdk.map.style.layer.circle.radiusConst
import com.maptiler.maptilersdk.map.style.layer.circle.radiusExpr
import com.maptiler.maptilersdk.map.style.layer.symbol.MTSymbolLayer
import com.maptiler.maptilersdk.map.style.layer.symbol.textAllowOverlap
import com.maptiler.maptilersdk.map.style.layer.symbol.MTTextAnchor
import com.maptiler.maptilersdk.map.style.layer.symbol.textAnchor
import com.maptiler.maptilersdk.map.style.layer.symbol.textColorConst
import com.maptiler.maptilersdk.map.style.layer.symbol.textField
import com.maptiler.maptilersdk.map.style.layer.symbol.textFont
import com.maptiler.maptilersdk.map.style.layer.symbol.textSize
import com.maptiler.maptilersdk.map.style.dsl.MTTextToken
import com.maptiler.maptilersdk.map.style.source.MTGeoJSONSource
import java.net.URL

/**
* View (XML) example building the same clustering demo as Compose.
*/
class ClusteringClassicActivity : ComponentActivity() {
private lateinit var mapView: MTMapViewClassic
private lateinit var controller: MTMapViewController

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MTConfig.apiKey = "YOUR_API_KEY"

controller = MTMapViewController(baseContext)
setContentView(R.layout.activity_basic_classic_map_view)
mapView = findViewById(R.id.classicMapView)
mapView.initialize(
referenceStyle = MTMapReferenceStyle.DATAVIZ,
options = MTMapOptions(),
controller = controller,
styleVariant = MTMapStyleVariant.DARK,
)

// Build once map/bridge is ready
controller.delegate = object : com.maptiler.maptilersdk.map.MTMapViewDelegate {
override fun onMapViewInitialized() {
setupClusters(controller.style!!)
}
}
}

private fun setupClusters(style: MTStyle) {
val src = MTGeoJSONSource.fromUrl(
identifier = "earthquakes",
url = URL("https://docs.maptiler.com/sdk-js/assets/earthquakes.geojson"),
).apply {
isCluster = true
clusterRadius = 50.0
clusterMaxZoom = 14.0
}
style.addSource(src)

val clusters =
MTCircleLayer("clusters", src.identifier)
.apply {
withFilter(Filter.clusters())
colorExpr(
Expression.step(
input = Expression.get(MTFeatureKey.POINT_COUNT),
default = PropertyValue.Color(Color.parseColor("#51bbd6")),
stops = listOf(
100.0 to PropertyValue.Color(Color.parseColor("#f1f075")),
750.0 to PropertyValue.Color(Color.parseColor("#f28cb1")),
),
),
)
radiusExpr(
Expression.step(
input = Expression.get(MTFeatureKey.POINT_COUNT),
default = PropertyValue.Num(20.0),
stops = listOf(
100.0 to PropertyValue.Num(30.0),
750.0 to PropertyValue.Num(40.0),
),
),
)
}
style.addLayer(clusters)

val labels =
MTSymbolLayer("clusterCount", src.identifier)
.apply {
withFilter(Filter.clusters())
textField(MTTextToken.POINT_COUNT_ABBREVIATED)
textSize(12.0)
textAllowOverlap(true)
textAnchor(MTTextAnchor.CENTER)
textFont(listOf("DIN Offc Pro Medium", "Arial Unicode MS Bold"))
textColorConst(Color.WHITE)
}
style.addLayer(labels)

val unclustered =
MTCircleLayer("unclusteredPoint", src.identifier)
.apply {
withFilter(Filter.unclustered())
colorConst(Color.parseColor("#11b4da"))
radiusConst(4.0)
}
style.addLayer(unclustered)
}
}
147 changes: 147 additions & 0 deletions Examples/ClusteringCompose.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package com.maptiler.examples

import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.maptiler.maptilersdk.MTConfig
import com.maptiler.maptilersdk.events.MTEvent
import com.maptiler.maptilersdk.map.MTMapOptions
import com.maptiler.maptilersdk.map.MTMapView
import com.maptiler.maptilersdk.map.MTMapViewController
import com.maptiler.maptilersdk.map.MTMapViewDelegate
import com.maptiler.maptilersdk.map.style.MTMapReferenceStyle
import com.maptiler.maptilersdk.map.style.MTMapStyleVariant
import com.maptiler.maptilersdk.map.style.MTStyle
import com.maptiler.maptilersdk.map.style.dsl.Expression
import com.maptiler.maptilersdk.map.style.dsl.Filter
import com.maptiler.maptilersdk.map.style.dsl.MTFeatureKey
import com.maptiler.maptilersdk.map.style.dsl.PropertyValue
import com.maptiler.maptilersdk.map.style.layer.circle.MTCircleLayer
import com.maptiler.maptilersdk.map.style.layer.circle.colorConst
import com.maptiler.maptilersdk.map.style.layer.circle.colorExpr
import com.maptiler.maptilersdk.map.style.layer.circle.radiusConst
import com.maptiler.maptilersdk.map.style.layer.circle.radiusExpr
import com.maptiler.maptilersdk.map.style.layer.symbol.MTSymbolLayer
import com.maptiler.maptilersdk.map.style.layer.symbol.textAllowOverlap
import com.maptiler.maptilersdk.map.style.layer.symbol.MTTextAnchor
import com.maptiler.maptilersdk.map.style.layer.symbol.textAnchor
import com.maptiler.maptilersdk.map.style.layer.symbol.textColorConst
import com.maptiler.maptilersdk.map.style.layer.symbol.textField
import com.maptiler.maptilersdk.map.style.layer.symbol.textFont
import com.maptiler.maptilersdk.map.style.layer.symbol.textSize
import com.maptiler.maptilersdk.map.style.dsl.MTTextToken
import com.maptiler.maptilersdk.map.style.source.MTGeoJSONSource
import com.maptiler.maptilersdk.map.types.MTData
import java.net.URL

/**
* Compose example demonstrating cluster circles + count labels + unclustered circles
* using the typed style DSL and helpers.
*/
@Composable
fun ClusteringCompose() {
val controller = remember { MTMapViewController(baseContext) }

LaunchedEffect(controller) {
controller.delegate = object : MTMapViewDelegate {
override fun onMapViewInitialized() {
setupClusters(controller.style!!)
}

override fun onEventTriggered(event: MTEvent, data: MTData?) {
// no-op
}
}
}

DisposableEffect(controller) { onDispose { controller.delegate = null } }

MTMapView(
referenceStyle = MTMapReferenceStyle.DATAVIZ,
options = MTMapOptions(),
controller = controller,
modifier = Modifier.fillMaxSize(),
styleVariant = MTMapStyleVariant.DARK,
)
}

private fun setupClusters(style: MTStyle) {
// 1) Source with clustering
val src = MTGeoJSONSource.fromUrl(
identifier = "earthquakes",
url = URL("https://docs.maptiler.com/sdk-js/assets/earthquakes.geojson"),
).apply {
isCluster = true
clusterRadius = 50.0
clusterMaxZoom = 14.0
}
style.addSource(src)

// 2) Cluster circles (inline config)
val clusters =
MTCircleLayer(identifier = "clusters", sourceIdentifier = src.identifier)
.apply {
withFilter(Filter.clusters())
colorExpr(
Expression.step(
input = Expression.get(MTFeatureKey.POINT_COUNT),
default = PropertyValue.Color(Color.parseColor("#51bbd6")),
stops = listOf(
100.0 to PropertyValue.Color(Color.parseColor("#f1f075")),
750.0 to PropertyValue.Color(Color.parseColor("#f28cb1")),
),
),
)
radiusExpr(
Expression.step(
input = Expression.get(MTFeatureKey.POINT_COUNT),
default = PropertyValue.Num(20.0),
stops = listOf(
100.0 to PropertyValue.Num(30.0),
750.0 to PropertyValue.Num(40.0),
),
),
)
}
style.addLayer(clusters)

// 3) Cluster count labels (inline config)
val labels =
MTSymbolLayer(identifier = "clusterCount", sourceIdentifier = src.identifier)
.apply {
withFilter(Filter.clusters())
textField(MTTextToken.POINT_COUNT_ABBREVIATED)
textSize(12.0)
textAllowOverlap(true)
textAnchor(MTTextAnchor.CENTER)
textFont(listOf("DIN Offc Pro Medium", "Arial Unicode MS Bold"))
textColorConst(Color.WHITE)
}
style.addLayer(labels)

// 4) Unclustered points (inline config)
val unclustered =
MTCircleLayer(identifier = "unclusteredPoint", sourceIdentifier = src.identifier)
.apply {
withFilter(Filter.unclustered())
colorConst(Color.parseColor("#11b4da"))
radiusConst(4.0)
}
style.addLayer(unclustered)
}

/** Optional Activity wrapper to run the composable. */
class ClusteringComposeActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MTConfig.apiKey = "YOUR_API_KEY"
setContent { ClusteringCompose() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.maptiler.maptilersdk.bridge.MTCommand
import com.maptiler.maptilersdk.helpers.ImageHelper
import com.maptiler.maptilersdk.helpers.JsonConfig
import com.maptiler.maptilersdk.map.style.layer.MTLayer
import com.maptiler.maptilersdk.map.style.layer.circle.MTCircleLayer
import com.maptiler.maptilersdk.map.style.layer.fill.MTFillLayer
import com.maptiler.maptilersdk.map.style.layer.line.MTLineLayer
import com.maptiler.maptilersdk.map.style.layer.raster.MTRasterLayer
Expand All @@ -31,6 +32,8 @@ internal data class AddLayer(
handleLineLayer(layer)
} else if (layer is MTRasterLayer) {
handleRasterLayer(layer)
} else if (layer is MTCircleLayer) {
handleCircleLayer(layer)
} else {
// Fallback to a generic addLayer for any future-supported layer types
val layerString: JSString = JsonConfig.json.encodeToString(layer)
Expand Down Expand Up @@ -79,4 +82,10 @@ internal data class AddLayer(

return "${MTBridge.MAP_OBJECT}.addLayer($layerString);"
}

private fun handleCircleLayer(layer: MTCircleLayer): JSString {
val layerString: JSString = JsonConfig.json.encodeToString(layer)

return "${MTBridge.MAP_OBJECT}.addLayer($layerString);"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2025, MapTiler
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
*/

package com.maptiler.maptilersdk.commands.style

import com.maptiler.maptilersdk.bridge.MTBridge
import com.maptiler.maptilersdk.bridge.MTCommand
import com.maptiler.maptilersdk.map.style.dsl.PropertyValue

internal data class SetFilter(
val layerId: String,
val filter: PropertyValue,
) : MTCommand {
override val isPrimitiveReturnType: Boolean = false

override fun toJS(): String = "${MTBridge.MAP_OBJECT}.setFilter(\"$layerId\", ${filter.asCode()});"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025, MapTiler
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
*/

package com.maptiler.maptilersdk.commands.style

import com.maptiler.maptilersdk.bridge.MTBridge
import com.maptiler.maptilersdk.bridge.MTCommand
import com.maptiler.maptilersdk.map.style.dsl.PropertyValue

internal data class SetLayoutProperty(
val layerId: String,
val name: String,
val value: PropertyValue,
) : MTCommand {
override val isPrimitiveReturnType: Boolean = false

override fun toJS(): String = "${MTBridge.MAP_OBJECT}.setLayoutProperty(\"$layerId\", \"$name\", ${value.asCode()});"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025, MapTiler
* All rights reserved.
* SPDX-License-Identifier: BSD 3-Clause
*/

package com.maptiler.maptilersdk.commands.style

import com.maptiler.maptilersdk.bridge.MTBridge
import com.maptiler.maptilersdk.bridge.MTCommand
import com.maptiler.maptilersdk.map.style.dsl.PropertyValue

internal data class SetPaintProperty(
val layerId: String,
val name: String,
val value: PropertyValue,
) : MTCommand {
override val isPrimitiveReturnType: Boolean = false

override fun toJS(): String = "${MTBridge.MAP_OBJECT}.setPaintProperty(\"$layerId\", \"$name\", ${value.asCode()});"
}
Loading