loadTileAsync refactor

loadTileAsync consumes all tileIds
This commit is contained in:
Lev Shagalov 2022-07-15 10:39:04 +03:00
parent fc0f223766
commit 5d3db81c4f
3 changed files with 49 additions and 42 deletions

View File

@ -2,7 +2,6 @@ package centre.sciprog.maps.compose
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlin.math.floor import kotlin.math.floor
data class TileId( data class TileId(
@ -17,7 +16,7 @@ data class MapTile(
) )
interface MapTileProvider { interface MapTileProvider {
suspend fun loadTileAsync(id: TileId, scope: CoroutineScope): Deferred<MapTile> suspend fun loadTileAsync(tileIds: List<TileId>, scope: CoroutineScope, onTileLoad: (mapTile: MapTile) -> Unit)
val tileSize: Int get() = DEFAULT_TILE_SIZE val tileSize: Int get() = DEFAULT_TILE_SIZE

View File

@ -17,7 +17,6 @@ import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.* import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import centre.sciprog.maps.* import centre.sciprog.maps.*
import kotlinx.coroutines.launch
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.skia.Font import org.jetbrains.skia.Font
import org.jetbrains.skia.Paint import org.jetbrains.skia.Paint
@ -46,21 +45,23 @@ actual fun MapView(
) { ) {
var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) } var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
var viewPointOverride by remember { mutableStateOf<MapViewPoint?>( var viewPointOverride by remember {
if(config.inferViewBoxFromFeatures){ mutableStateOf<MapViewPoint?>(
features.values.computeBoundingBox(1)?.let { box -> if (config.inferViewBoxFromFeatures) {
val zoom = log2( features.values.computeBoundingBox(1)?.let { box ->
min( val zoom = log2(
canvasSize.width.value / box.width, min(
canvasSize.height.value / box.height canvasSize.width.value / box.width,
) * PI / mapTileProvider.tileSize canvasSize.height.value / box.height
) ) * PI / mapTileProvider.tileSize
MapViewPoint(box.center, zoom) )
MapViewPoint(box.center, zoom)
}
} else {
null
} }
} else { )
null }
}
) }
val viewPoint by derivedStateOf { viewPointOverride ?: computeViewPoint(canvasSize) } val viewPoint by derivedStateOf { viewPointOverride ?: computeViewPoint(canvasSize) }
@ -163,21 +164,17 @@ actual fun MapView(
mapTiles.clear() mapTiles.clear()
verticalIndices val tileIds = verticalIndices
.flatMap { j -> .flatMap { j ->
horizontalIndices horizontalIndices
.asSequence() .asSequence()
.map { TileId(zoom, it, j) } .map { TileId(zoom, it, j) }
} }
.forEach {
try { mapTileProvider.loadTileAsync(
launch { tileIds = tileIds,
mapTiles += mapTileProvider.loadTileAsync(it, this).await() scope = this
} ) { mapTiles += it }
} catch (ex: Exception) {
logger.error(ex) { "Failed to load tile $it" }
}
}
} }

View File

@ -6,10 +6,7 @@ import centre.sciprog.maps.LruCache
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.* import io.ktor.client.statement.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.Semaphore
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.skia.Image import org.jetbrains.skia.Image
@ -63,17 +60,31 @@ class OpenStreetMapTileProvider(
Image.makeFromEncoded(byteArray).toComposeImageBitmap() Image.makeFromEncoded(byteArray).toComposeImageBitmap()
} }
override suspend fun loadTileAsync(id: TileId, scope: CoroutineScope) = scope.async { override suspend fun loadTileAsync(
semaphore.acquire() tileIds: List<TileId>,
try { scope: CoroutineScope,
val image = cache.getOrPut(id) { downloadImageAsync(id) } onTileLoad: (mapTile: MapTile) -> Unit,
MapTile(id, image.await()) ) {
} catch (e: Exception) { tileIds
cache.remove(id) .forEach { id ->
throw e try {
} finally { scope.launch {
semaphore.release() semaphore.acquire()
} try {
val image = cache.getOrPut(id) { downloadImageAsync(id) }
val result = MapTile(id, image.await())
onTileLoad(result)
} catch (e: Exception) {
cache.remove(id)
throw e
} finally {
semaphore.release()
}
}
} catch (ex: Exception) {
logger.error(ex) { "Failed to load tile $id" }
}
}
} }
companion object { companion object {