-
-
Notifications
You must be signed in to change notification settings - Fork 42
wip: map snapshotter #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
michalgwo
wants to merge
32
commits into
maplibre:main
Choose a base branch
from
michalgwo:map-snapshotter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
wip: map snapshotter #266
Changes from 19 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
3d9b1a8
refactoring
michalgwo 42f3194
created snapshotter and implemented for android
michalgwo 1c7f907
minor fix
michalgwo 9eb77d9
removed params not available on ios
michalgwo 6cc1bd9
snapshotter implementation for ios
michalgwo 33da024
removed unused class
michalgwo c530f06
created empty JsMapSnapshotter
michalgwo 2924d62
demo - cancelling snapshotter on pause
michalgwo cd513a2
docs
michalgwo 899bc6c
code formatting
michalgwo a15e808
fixed padding bug
michalgwo 8e1f386
added comment that snapshotter is missing on JS
michalgwo 5939370
made MapSnapshotter internal and functions nonpublic
michalgwo c174270
fixed docs
michalgwo eedfe37
based MapSnapshotter on coroutines
michalgwo 75835bf
code formatting
michalgwo e5d48f3
snapshot returns ImageBitmap, fixed cancelling
michalgwo bdea821
docs
michalgwo b63fd03
fixed error handling
michalgwo 6dc11ab
fixed crash
michalgwo 87d7396
fixed ios map snapshotter
michalgwo 4902ac3
code formatting
michalgwo a25596a
Merge branch 'main' into map-snapshotter
michalgwo 6e7fc51
docs
michalgwo b9cbd25
Merge branch 'main' into map-snapshotter
michalgwo e6e4c39
fixed merge
michalgwo a673c8e
update layoutDir and density when it changes
michalgwo de7e30f
set with to px and default style
michalgwo 3223d69
show AlertDialog on error
michalgwo dfbd3db
imports
michalgwo fa8f5d2
fixed LocalLifecycleOwner
michalgwo 02a6fd2
Merge branch 'main' into map-snapshotter
michalgwo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
152 changes: 152 additions & 0 deletions
152
demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/demos/SnapshotterDemo.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| package dev.sargunv.maplibrecompose.demoapp.demos | ||
|
|
||
| import androidx.compose.foundation.Image | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Box | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||
| import androidx.compose.material3.Button | ||
| import androidx.compose.material3.Card | ||
| import androidx.compose.material3.CircularProgressIndicator | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.DisposableEffect | ||
| import androidx.compose.runtime.MutableState | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.runtime.mutableStateOf | ||
| import androidx.compose.runtime.remember | ||
| import androidx.compose.runtime.rememberCoroutineScope | ||
| import androidx.compose.runtime.setValue | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.graphics.ImageBitmap | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.compose.ui.window.Dialog | ||
| import androidx.lifecycle.Lifecycle | ||
| import androidx.lifecycle.LifecycleEventObserver | ||
| import androidx.lifecycle.compose.LocalLifecycleOwner | ||
| import dev.sargunv.maplibrecompose.compose.CameraState | ||
| import dev.sargunv.maplibrecompose.compose.MaplibreMap | ||
| import dev.sargunv.maplibrecompose.compose.rememberCameraState | ||
| import dev.sargunv.maplibrecompose.compose.rememberStyleState | ||
| import dev.sargunv.maplibrecompose.core.SnapshotException | ||
| import dev.sargunv.maplibrecompose.demoapp.ALL_STYLES | ||
| 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 kotlin.coroutines.cancellation.CancellationException | ||
| import kotlinx.coroutines.Job | ||
| import kotlinx.coroutines.launch | ||
|
|
||
| object SnapshotterDemo : Demo { | ||
| override val name = "Snapshotter" | ||
| override val description = "Take a snapshot of the map" | ||
|
|
||
| @Composable | ||
| override fun Component(navigateUp: () -> Unit) { | ||
| val cameraState = rememberCameraState() | ||
| val styleState = rememberStyleState() | ||
| val isLoading = remember { mutableStateOf(false) } | ||
| val snapshot = remember { mutableStateOf<ImageBitmap?>(null) } | ||
| val lifeCycleOwner = LocalLifecycleOwner.current | ||
|
|
||
| DisposableEffect(lifeCycleOwner) { | ||
| val observer = LifecycleEventObserver { _, event -> | ||
| if (event == Lifecycle.Event.ON_PAUSE) { | ||
| isLoading.value = false | ||
| } | ||
| } | ||
| lifeCycleOwner.lifecycle.addObserver(observer) | ||
| onDispose { lifeCycleOwner.lifecycle.removeObserver(observer) } | ||
| } | ||
|
|
||
| DemoScaffold(this, navigateUp) { | ||
| Column { | ||
| Box(modifier = Modifier.weight(1f)) { | ||
| MaplibreMap( | ||
| styleUri = DEFAULT_STYLE, | ||
| cameraState = cameraState, | ||
| styleState = styleState, | ||
| ornamentSettings = DemoOrnamentSettings(), | ||
| ) | ||
| DemoMapControls(cameraState, styleState) | ||
|
|
||
| if (isLoading.value) { | ||
| CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) | ||
| } | ||
| } | ||
|
|
||
| SnapshotterControls(cameraState, isLoading, snapshot) | ||
|
|
||
| snapshot.value?.let { | ||
| SnapshotDialog(snapshot = it, onDismissRequest = { snapshot.value = null }) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun SnapshotterControls( | ||
| cameraState: CameraState, | ||
| isLoading: MutableState<Boolean>, | ||
| snapshot: MutableState<ImageBitmap?>, | ||
| ) { | ||
| val scope = rememberCoroutineScope() | ||
| var snapshotJob by remember { mutableStateOf<Job?>(null) } | ||
|
|
||
| Row( | ||
| modifier = Modifier.padding(16.dp).fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.SpaceEvenly, | ||
| ) { | ||
| Button( | ||
| onClick = { | ||
| snapshotJob = | ||
| scope.launch { | ||
| isLoading.value = true | ||
| try { | ||
| val response = | ||
| cameraState.snapshot( | ||
| width = 500.dp, | ||
| height = 500.dp, | ||
| styleUri = DEFAULT_STYLE, | ||
| cameraPosition = cameraState.position, | ||
| ) | ||
|
|
||
| snapshot.value = response | ||
| } catch (error: SnapshotException) { | ||
| println("Error during snapshot generation: ${error.message}") | ||
| } catch (error: CancellationException) { | ||
| println("Snapshot generation cancelled") | ||
| } | ||
|
|
||
| isLoading.value = false | ||
| snapshotJob = null | ||
| } | ||
| } | ||
| ) { | ||
| Text("Take snapshot") | ||
| } | ||
| Button(enabled = snapshotJob != null, onClick = { snapshotJob?.cancel() }) { | ||
| Text("Cancel snapshot") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| fun SnapshotDialog(snapshot: ImageBitmap, onDismissRequest: () -> Unit) { | ||
| Dialog(onDismissRequest = { onDismissRequest() }) { | ||
| Card(modifier = Modifier.padding(16.dp), shape = RoundedCornerShape(16.dp)) { | ||
| Image( | ||
| modifier = Modifier.padding(16.dp), | ||
| bitmap = snapshot, | ||
| contentDescription = "Snapshot", | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
...-compose/src/androidMain/kotlin/dev/sargunv/maplibrecompose/core/AndroidMapSnapshotter.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| package dev.sargunv.maplibrecompose.core | ||
|
|
||
| import android.content.Context | ||
| import androidx.compose.ui.graphics.ImageBitmap | ||
| import androidx.compose.ui.graphics.asImageBitmap | ||
| import androidx.compose.ui.unit.Density | ||
| import androidx.compose.ui.unit.Dp | ||
| import androidx.compose.ui.unit.LayoutDirection | ||
| import dev.sargunv.maplibrecompose.core.util.correctedAndroidUri | ||
| import dev.sargunv.maplibrecompose.core.util.toLatLngBounds | ||
| import dev.sargunv.maplibrecompose.core.util.toMLNCameraPosition | ||
| import io.github.dellisd.spatialk.geojson.BoundingBox | ||
| import kotlin.coroutines.resume | ||
| import kotlinx.coroutines.suspendCancellableCoroutine | ||
| import org.maplibre.android.maps.Style | ||
| import kotlin.coroutines.resumeWithException | ||
| import org.maplibre.android.snapshotter.MapSnapshotter as MLNMapSnapshotter | ||
|
|
||
| internal class AndroidMapSnapshotter( | ||
| private val context: Context, | ||
| private val layoutDir: LayoutDirection, | ||
| private val density: Density, | ||
| ) : MapSnapshotter { | ||
|
|
||
| override suspend fun snapshot( | ||
| width: Dp, | ||
| height: Dp, | ||
| styleUri: String, | ||
| region: BoundingBox?, | ||
| cameraPosition: CameraPosition?, | ||
| showLogo: Boolean, | ||
| ): ImageBitmap { | ||
| with(density) { | ||
| val styleBuilder = Style.Builder().fromUri(styleUri.correctedAndroidUri()) | ||
| val options = | ||
| MLNMapSnapshotter.Options(width.roundToPx(), height.roundToPx()) | ||
| .withStyleBuilder(styleBuilder) | ||
| .withRegion(region?.toLatLngBounds()) | ||
| .withCameraPosition(cameraPosition?.toMLNCameraPosition(this, layoutDir)) | ||
| .withLogo(showLogo) | ||
|
|
||
| val snapshotter = MLNMapSnapshotter(context, options) | ||
|
|
||
| return suspendCancellableCoroutine { cont -> | ||
| snapshotter.start({ snapshot -> cont.resume(snapshot.bitmap.asImageBitmap()) }) { error -> | ||
| cont.resumeWithException(SnapshotException(error)) | ||
| } | ||
| cont.invokeOnCancellation { snapshotter.cancel() } | ||
| } | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.