From f92ccb4838a7a1cb2d7d86cf5f3a0de44eb4a43a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 14 Jul 2022 20:53:41 +0300 Subject: [PATCH] Async load --- .../sciprog/maps/compose/MapTileProvider.kt | 7 ++++-- .../centre/sciprog/maps/compose/MapViewJvm.kt | 7 ++++-- .../maps/compose/OpenStreetMapTileProvider.kt | 23 +++++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/commonMain/kotlin/centre/sciprog/maps/compose/MapTileProvider.kt b/src/commonMain/kotlin/centre/sciprog/maps/compose/MapTileProvider.kt index dfba9db..20366b0 100644 --- a/src/commonMain/kotlin/centre/sciprog/maps/compose/MapTileProvider.kt +++ b/src/commonMain/kotlin/centre/sciprog/maps/compose/MapTileProvider.kt @@ -1,6 +1,7 @@ package centre.sciprog.maps.compose import androidx.compose.ui.graphics.ImageBitmap +import kotlinx.coroutines.Deferred import kotlin.math.floor data class TileId( @@ -15,7 +16,7 @@ data class MapTile( ) interface MapTileProvider { - suspend fun loadTile(id: TileId): MapTile + fun loadTileAsync(id: TileId): Deferred val tileSize: Int get() = DEFAULT_TILE_SIZE @@ -26,4 +27,6 @@ interface MapTileProvider { companion object { const val DEFAULT_TILE_SIZE = 256 } -} \ No newline at end of file +} + +suspend fun MapTileProvider.loadTile(id: TileId): MapTile = loadTileAsync(id).await() \ No newline at end of file diff --git a/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt b/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt index 09cee4c..fbb544c 100644 --- a/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt +++ b/src/jvmMain/kotlin/centre/sciprog/maps/compose/MapViewJvm.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.* import androidx.compose.ui.unit.* import centre.sciprog.maps.* +import kotlinx.coroutines.launch import mu.KotlinLogging import org.jetbrains.skia.Font import org.jetbrains.skia.Paint @@ -149,8 +150,10 @@ actual fun MapView( if (i in indexRange && j in indexRange) { val tileId = TileId(zoom, i, j) try { - val tile = mapTileProvider.loadTile(tileId) - mapTiles.add(tile) + launch { + val tile = mapTileProvider.loadTileAsync(tileId) + mapTiles.add(tile.await()) + } } catch (ex: Exception) { logger.error(ex) { "Failed to load tile $tileId" } } diff --git a/src/jvmMain/kotlin/centre/sciprog/maps/compose/OpenStreetMapTileProvider.kt b/src/jvmMain/kotlin/centre/sciprog/maps/compose/OpenStreetMapTileProvider.kt index 439c0c6..351a2c0 100644 --- a/src/jvmMain/kotlin/centre/sciprog/maps/compose/OpenStreetMapTileProvider.kt +++ b/src/jvmMain/kotlin/centre/sciprog/maps/compose/OpenStreetMapTileProvider.kt @@ -5,7 +5,10 @@ import androidx.compose.ui.graphics.toComposeImageBitmap import io.ktor.client.HttpClient import io.ktor.client.request.get import io.ktor.client.statement.readBytes -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import mu.KotlinLogging import org.jetbrains.skia.Image import java.net.URL @@ -16,7 +19,11 @@ import kotlin.math.pow /** * A [MapTileProvider] based on Open Street Map API. With in-memory and file cache */ -public class OpenStreetMapTileProvider(private val scope: CoroutineScope, private val client: HttpClient, private val cacheDirectory: Path): MapTileProvider { +public class OpenStreetMapTileProvider( + private val scope: CoroutineScope, + private val client: HttpClient, + private val cacheDirectory: Path, +) : MapTileProvider { private val cache = HashMap>() private fun TileId.osmUrl() = URL("https://tile.openstreetmap.org/${zoom}/${i}/${j}.png") @@ -53,20 +60,22 @@ public class OpenStreetMapTileProvider(private val scope: CoroutineScope, privat Image.makeFromEncoded(byteArray).toComposeImageBitmap() } - override suspend fun loadTile(id: TileId): MapTile { + override fun loadTileAsync(id: TileId): Deferred { val indexRange = indexRange(id.zoom) - if(id.i !in indexRange || id.j !in indexRange){ + if (id.i !in indexRange || id.j !in indexRange) { error("Indices (${id.i}, ${id.j}) are not in index range $indexRange for zoom ${id.zoom}") } val image = cache.getOrPut(id) { downloadImageAsync(id) - }.await() + } - return MapTile(id, image) + return scope.async { + MapTile(id, image.await()) + } } - companion object{ + companion object { private val logger = KotlinLogging.logger("OpenStreetMapCache") private fun indexRange(zoom: Int): IntRange = 0 until 2.0.pow(zoom).toInt() }