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
10 changes: 5 additions & 5 deletions docs/src/experimental/marker-native-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ val strategy = SimpleNativeParallelStrategy<ActualMarker>(
)
```

#### NativeSpatialMarkerRenderingStrategy
#### NativeSpatialMarkerStrategy

Advanced spatial rendering with clustering:

```kotlin
import com.mapconductor.marker.nativestrategy.NativeSpatialMarkerRenderingStrategy
import com.mapconductor.marker.nativestrategy.NativeSpatialMarkerStrategy

val spatialStrategy = NativeSpatialMarkerRenderingStrategy<ActualMarker>(
val spatialStrategy = NativeSpatialMarkerStrategy<ActualMarker>(
clusteringEnabled = true,
clusterThreshold = 100, // Cluster when > 100 markers in area
geocell = HexGeocellImpl.defaultGeocell()
Expand Down Expand Up @@ -265,7 +265,7 @@ fun DynamicNativeLoadingExample() {
@Composable
fun NativeClusteringExample() {
val clusteringStrategy = remember {
NativeSpatialMarkerRenderingStrategy<GoogleMapActualMarker>(
NativeSpatialMarkerStrategy<GoogleMapActualMarker>(
clusteringEnabled = true,
clusterThreshold = 50, // Cluster when > 50 markers nearby
clusterRadius = 100.0, // 100-meter clustering radius
Expand Down Expand Up @@ -518,4 +518,4 @@ class MarkerActivity : ComponentActivity() {
}
```

The marker native strategy module provides significant performance improvements for marker-heavy applications, but requires careful resource management and thorough testing due to its experimental nature.
The marker native strategy module provides significant performance improvements for marker-heavy applications, but requires careful resource management and thorough testing due to its experimental nature.
56 changes: 28 additions & 28 deletions docs/src/experimental/marker-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ dependencies {

## Core Strategies

### DefaultMarkerRenderingStrategy
### DefaultMarkerStrategy

Optimal for Google Maps and ArcGIS providers that handle add/remove operations efficiently:

```kotlin
import com.mapconductor.marker.strategy.DefaultMarkerRenderingStrategy
import com.mapconductor.marker.strategy.DefaultMarkerStrategy

val defaultStrategy = DefaultMarkerRenderingStrategy<GoogleMapActualMarker>(
val defaultStrategy = DefaultMarkerStrategy<GoogleMapActualMarker>(
expandMargin = 0.2, // 20% viewport expansion
semaphore = Semaphore(1),
geocell = HexGeocellImpl.defaultGeocell()
Expand All @@ -53,14 +53,14 @@ val defaultStrategy = DefaultMarkerRenderingStrategy<GoogleMapActualMarker>(
- **Memory Efficient**: Only keeps visible markers in memory
- **Smooth Scrolling**: Reduces pop-in/pop-out during map movement

### SimpleMarkerRenderingStrategy
### SimpleMarkerStrategy

Lightweight strategy for smaller datasets or providers with different performance characteristics:

```kotlin
import com.mapconductor.marker.strategy.SimpleMarkerRenderingStrategy
import com.mapconductor.marker.strategy.SimpleMarkerStrategy

val simpleStrategy = SimpleMarkerRenderingStrategy<MapboxActualMarker>(
val simpleStrategy = SimpleMarkerStrategy<MapboxActualMarker>(
expandMargin = 0.15,
geocell = HexGeocellImpl.defaultGeocell()
)
Expand All @@ -71,14 +71,14 @@ val simpleStrategy = SimpleMarkerRenderingStrategy<MapboxActualMarker>(
- **Lower Overhead**: Minimal computational overhead
- **Good for Mapbox**: Optimized for providers that prefer simpler marker management

### SpatialMarkerRenderingStrategy
### SpatialMarkerStrategy

Advanced strategy with spatial clustering and optimization:

```kotlin
import com.mapconductor.marker.strategy.SpatialMarkerRenderingStrategy
import com.mapconductor.marker.strategy.SpatialMarkerStrategy

val spatialStrategy = SpatialMarkerRenderingStrategy<HereActualMarker>(
val spatialStrategy = SpatialMarkerStrategy<HereActualMarker>(
clusteringEnabled = true,
clusterRadius = 100.0, // 100-meter clustering radius
maxMarkersPerCluster = 10, // Maximum markers in a cluster
Expand All @@ -102,7 +102,7 @@ fun DefaultStrategyExample() {
val mapViewState = rememberGoogleMapViewState()

val markerStrategy = remember {
DefaultMarkerRenderingStrategy<GoogleMapActualMarker>(
DefaultMarkerStrategy<GoogleMapActualMarker>(
expandMargin = 0.2
)
}
Expand All @@ -127,7 +127,7 @@ fun DefaultStrategyExample() {
@Composable
fun StrategyMarkerManagement() {
val markerStrategy = remember {
DefaultMarkerRenderingStrategy<GoogleMapActualMarker>()
DefaultMarkerStrategy<GoogleMapActualMarker>()
}

LaunchedEffect(Unit) {
Expand Down Expand Up @@ -167,7 +167,7 @@ fun DynamicLoadingExample() {
var loadedMarkers by remember { mutableStateOf<Set<String>>(emptySet()) }

val strategy = remember {
DefaultMarkerRenderingStrategy<GoogleMapActualMarker>(
DefaultMarkerStrategy<GoogleMapActualMarker>(
expandMargin = 0.3 // Larger margin for preloading
)
}
Expand Down Expand Up @@ -212,7 +212,7 @@ fun DynamicLoadingExample() {
@Composable
fun ClusteringStrategyExample() {
val clusterStrategy = remember {
SpatialMarkerRenderingStrategy<GoogleMapActualMarker>(
SpatialMarkerStrategy<GoogleMapActualMarker>(
clusteringEnabled = true,
clusterRadius = 50.0, // 50-meter clustering
maxMarkersPerCluster = 5, // Small clusters
Expand Down Expand Up @@ -260,7 +260,7 @@ fun ClusteringStrategyExample() {
@Composable
fun RemoteSpatialExample() {
val remoteStrategy = remember {
RemoteSpatialMarkerRenderingStrategy<GoogleMapActualMarker>(
RemoteSpatialMarkerStrategy<GoogleMapActualMarker>(
apiEndpoint = "https://api.example.com/markers",
cacheTimeout = 300000, // 5 minutes
maxConcurrentRequests = 3
Expand All @@ -286,32 +286,32 @@ fun RemoteSpatialExample() {

| Strategy | Best For | Memory Usage | Network | Complexity |
|----------|----------|--------------|---------|------------|
| DefaultMarkerRenderingStrategy | Google Maps, ArcGIS | Medium | None | Medium |
| SimpleMarkerRenderingStrategy | Mapbox, HERE | Low | None | Low |
| SpatialMarkerRenderingStrategy | Large datasets | High | None | High |
| RemoteSpatialMarkerRenderingStrategy | Server-side data | Low | High | High |
| DefaultMarkerStrategy | Google Maps, ArcGIS | Medium | None | Medium |
| SimpleMarkerStrategy | Mapbox, HERE | Low | None | Low |
| SpatialMarkerStrategy | Large datasets | High | None | High |
| RemoteSpatialMarkerStrategy | Server-side data | Low | High | High |

### Use Case Guidelines

#### Choose DefaultMarkerRenderingStrategy when:
#### Choose DefaultMarkerStrategy when:
- Using Google Maps or ArcGIS
- Have moderate marker counts (1,000-50,000)
- Want smooth viewport-based rendering
- Markers are loaded locally

#### Choose SimpleMarkerRenderingStrategy when:
#### Choose SimpleMarkerStrategy when:
- Using Mapbox or HERE Maps
- Have smaller marker counts (<10,000)
- Want minimal overhead
- Simple rendering requirements

#### Choose SpatialMarkerRenderingStrategy when:
#### Choose SpatialMarkerStrategy when:
- Have very large marker datasets (50,000+)
- Need clustering functionality
- Want advanced spatial optimization
- Can afford higher memory usage

#### Choose RemoteSpatialMarkerRenderingStrategy when:
#### Choose RemoteSpatialMarkerStrategy when:
- Markers are stored server-side
- Want on-demand loading
- Have network connectivity
Expand All @@ -322,7 +322,7 @@ fun RemoteSpatialExample() {
### Extending AbstractViewportStrategy

```kotlin
class CustomMarkerRenderingStrategy<ActualMarker>(
class CustomMarkerStrategy<ActualMarker>(
semaphore: Semaphore = Semaphore(1),
geocell: HexGeocell = HexGeocellImpl.defaultGeocell()
) : AbstractViewportStrategy<ActualMarker>(semaphore, geocell) {
Expand Down Expand Up @@ -378,7 +378,7 @@ class CustomMarkerRenderingStrategy<ActualMarker>(

```kotlin
// High-performance configuration
val performanceStrategy = DefaultMarkerRenderingStrategy<ActualMarker>(
val performanceStrategy = DefaultMarkerStrategy<ActualMarker>(
expandMargin = 0.1, // Smaller margin for less preloading
semaphore = Semaphore(2), // Allow some parallelism
geocell = HexGeocellImpl(
Expand All @@ -388,7 +388,7 @@ val performanceStrategy = DefaultMarkerRenderingStrategy<ActualMarker>(
)

// Memory-optimized configuration
val memoryStrategy = SimpleMarkerRenderingStrategy<ActualMarker>(
val memoryStrategy = SimpleMarkerStrategy<ActualMarker>(
expandMargin = 0.05, // Minimal expansion
geocell = HexGeocellImpl(
baseHexSideLength = 2000.0, // Very large cells
Expand All @@ -402,7 +402,7 @@ val memoryStrategy = SimpleMarkerRenderingStrategy<ActualMarker>(
```kotlin
@Composable
fun StrategyPerformanceMonitoring() {
val strategy = remember { DefaultMarkerRenderingStrategy<GoogleMapActualMarker>() }
val strategy = remember { DefaultMarkerStrategy<GoogleMapActualMarker>() }
var performanceStats by remember { mutableStateOf<String>("") }

LaunchedEffect(Unit) {
Expand Down Expand Up @@ -467,7 +467,7 @@ fun BasicMarkers() {
// After: Strategy-based management
@Composable
fun StrategyMarkers() {
val strategy = remember { DefaultMarkerRenderingStrategy<ActualMarker>() }
val strategy = remember { DefaultMarkerStrategy<ActualMarker>() }

LaunchedEffect(markers) {
markers.forEach { markerData ->
Expand All @@ -488,4 +488,4 @@ fun StrategyMarkers() {
}
```

The marker strategy module provides sophisticated marker management capabilities for applications requiring high performance with large datasets or complex rendering requirements.
The marker strategy module provides sophisticated marker management capabilities for applications requiring high performance with large datasets or complex rendering requirements.
2 changes: 1 addition & 1 deletion docs/src/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Advanced marker rendering strategies for performance optimization.
implementation "com.mapconductor:marker-strategy"
```

**Provides**: DefaultMarkerRenderingStrategy, SpatialMarkerRenderingStrategy
**Provides**: DefaultMarkerStrategy, SpatialMarkerStrategy
**Stability**: Experimental
**Size**: ~XXX KB

Expand Down
28 changes: 20 additions & 8 deletions example-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,26 @@ dependencies {
// implementation("com.mapconductor:marker-strategy")
// implementation("com.mapconductor:marker-native-strategy")

implementation(project(":mapconductor-core"))
implementation(project(":mapconductor-icons"))
implementation(project(":mapconductor-for-googlemaps"))
implementation(project(":mapconductor-for-here"))
implementation(project(":mapconductor-for-mapbox"))
implementation(project(":mapconductor-for-arcgis"))
implementation(project(":mapconductor-marker-strategy"))
implementation(project(":mapconductor-marker-native-strategy"))
// Use project dependency for debug, Maven artifact for release
// Align versions in release via the project BOM
releaseImplementation(platform(project(":mapconductor-bom")))
releaseImplementation(libs.mapconductor.core)
releaseImplementation(libs.mapconductor.icons)
releaseImplementation(libs.mapconductor.googlemaps)
releaseImplementation(libs.mapconductor.here)
releaseImplementation(libs.mapconductor.mapbox)
releaseImplementation(libs.mapconductor.arcgis)
releaseImplementation(libs.mapconductor.marker.strategy)
releaseImplementation(libs.mapconductor.marker.native.strategy)

debugImplementation(project(":mapconductor-core"))
debugImplementation(project(":mapconductor-icons"))
debugImplementation(project(":mapconductor-for-googlemaps"))
debugImplementation(project(":mapconductor-for-here"))
debugImplementation(project(":mapconductor-for-mapbox"))
debugImplementation(project(":mapconductor-for-arcgis"))
debugImplementation(project(":mapconductor-marker-strategy"))
debugImplementation(project(":mapconductor-marker-native-strategy"))

implementation(libs.androidx.vectordrawable)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.mapconductor.example.pages.map.flyto

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.mapconductor.core.map.MapViewState
import com.mapconductor.core.marker.Marker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import com.mapconductor.example.ui.DemoMapPageScaffold
import com.mapconductor.googlemaps.GoogleMapActualMarker
import com.mapconductor.here.HereActualMarker
import com.mapconductor.mapbox.MapboxActualMarker
import com.mapconductor.marker.nativestrategy.NativeSpatialMarkerRenderingStrategy
import com.mapconductor.marker.nativestrategy.NativeSpatialMarkerStrategy
import com.mapconductor.marker.nativestrategy.spatial.NativeRemoteSpatialMarkerStrategy

@Composable
fun PostOfficeMapPage(
Expand All @@ -30,10 +31,12 @@ fun PostOfficeMapPage(
val dataLoader = remember { PostOfficeDataLoader(context) }
val strategies =
remember {
val google = NativeSpatialMarkerRenderingStrategy<GoogleMapActualMarker>()
val mapbox = NativeSpatialMarkerRenderingStrategy<MapboxActualMarker>()
val here = NativeSpatialMarkerRenderingStrategy<HereActualMarker>()
val arcgis = NativeSpatialMarkerRenderingStrategy<ArcGISActualMarker>()
val google = NativeRemoteSpatialMarkerStrategy<GoogleMapActualMarker>(context)
// val google = NativeSpatialMarkerStrategy<GoogleMapActualMarker>()
// val google = NativeParallelMarkerStrategy<GoogleMapActualMarker>()
val mapbox = NativeSpatialMarkerStrategy<MapboxActualMarker>()
val here = NativeSpatialMarkerStrategy<HereActualMarker>()
val arcgis = NativeSpatialMarkerStrategy<ArcGISActualMarker>()
Strategies(
google = google,
mapbox = mapbox,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import com.mapconductor.here.HereActualMarker
import com.mapconductor.here.HereViewState
import com.mapconductor.mapbox.MapboxActualMarker
import com.mapconductor.mapbox.MapboxViewState
import com.mapconductor.marker.strategy.SimpleMarkerRenderingStrategy
import com.mapconductor.marker.strategy.spatial.RemoteSpatialMarkerRenderingStrategy
import com.mapconductor.marker.strategy.SimpleMarkerStrategy
import com.mapconductor.marker.strategy.spatial.RemoteSpatialMarkerStrategy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -92,6 +92,8 @@ class PostOfficeViewModelImpl(
override fun loadPostOfficeData() {
if (_markerList.value.isNotEmpty()) return
coroutine.launch {
// Wait until map tiles are rendered.
delay(2500)
val postOffices = dataLoader.loadAllPostOffices()

val markerStates =
Expand All @@ -117,8 +119,6 @@ class PostOfficeViewModelImpl(

override fun onMapLoaded(mapViewState: MapViewState<*>) {
coroutine.launch {
// Wait until map tiles are rendered.
delay(3000)
_isMapLoaded.value = true
}
}
Expand All @@ -145,14 +145,14 @@ class PostOfficeViewModelImpl(
is MapboxViewState -> strategies.mapbox
is HereViewState -> strategies.here
is ArcGISMapViewState -> strategies.arcgis
else -> SimpleMarkerRenderingStrategy<Any>()
else -> SimpleMarkerStrategy<Any>()
} as MarkerRenderingStrategy<Any>?
_isMapLoaded.value = false
}

override fun onCleared() {
super.onCleared()
// Clean up remote strategy if it's being used
(renderingStrategy as? RemoteSpatialMarkerRenderingStrategy<*>)?.destroy()
(renderingStrategy as? RemoteSpatialMarkerStrategy<*>)?.destroy()
}
}
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ isMinifyEnabled=true
android.enableR8.fullMode=true
android.r8.ignoreAllowListBinaryFiles=true
android.enableJetifier=true
# Enable support for flexible page sizes (e.g., 16KB pages on Android 15+)
APP_SUPPORT_FLEXIBLE_PAGE_SIZES = true

8 changes: 8 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "compose" }

# Mapbox
mapconductor-core = { module = "com.mapconductor:core" }
mapconductor-icons = { module = "com.mapconductor:icons" }
mapconductor-googlemaps = { module = "com.mapconductor:for-googlemaps" }
mapconductor-mapbox = { module = "com.mapconductor:for-mapbox" }
mapconductor-here = { module = "com.mapconductor:for-here" }
mapconductor-arcgis = { module = "com.mapconductor:for-arcgis" }
mapconductor-marker-strategy = { module = "com.mapconductor:marker-strategy" }
mapconductor-marker-native-strategy = { module = "com.mapconductor:marker-native-strategy" }
mapbox-android = { module = "com.mapbox.maps:android-ndk27", version.ref = "mapboxAndroid" }

# Google Maps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ open class MapViewScope {

init {
CoroutineScope(Dispatchers.IO).launch {
markerAddSharedFlow.debounceBatch(5.milliseconds, 100).collect { states ->
markerAddSharedFlow.debounceBatch(5.milliseconds, 300).collect { states ->
val newMap = markerFlow.value.toMutableMap()
states.forEach { state ->
newMap.set(state.id, state)
Expand Down
Loading