Merge remote-tracking branch 'space/main'

# Conflicts:
#	maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt
#	maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeature.kt
This commit is contained in:
Alexander Nozik 2022-08-29 22:23:24 +03:00
commit d490b3f37c
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
7 changed files with 56 additions and 54 deletions

View File

@ -133,3 +133,13 @@ public class MapFeatureGroup(
) : MapFeature { ) : MapFeature {
override fun getBoundingBox(zoom: Int): GmcRectangle? = children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapAll() override fun getBoundingBox(zoom: Int): GmcRectangle? = children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapAll()
} }
public class MapTextFeature(
public val position: GeodeticMapCoordinates,
public val text: String,
override val zoomRange: IntRange = defaultZoomRange,
public val color: Color,
public val fontConfig: MapTextFeatureFont.() -> Unit,
) : MapFeature {
override fun getBoundingBox(zoom: Int): GmcBox = GmcBox(position, position)
}

View File

@ -144,3 +144,21 @@ public fun MapFeatureBuilder.group(
val feature = MapFeatureGroup(map, zoomRange) val feature = MapFeatureGroup(map, zoomRange)
return addFeature(id, feature) return addFeature(id, feature)
} }
public fun MapFeatureBuilder.text(
position: GeodeticMapCoordinates,
text: String,
zoomRange: IntRange = defaultZoomRange,
color: Color = Color.Red,
font: MapTextFeatureFont.() -> Unit = { size = 16f },
id: FeatureId? = null,
): FeatureId = addFeature(id, MapTextFeature(position, text, zoomRange, color, font))
public fun MapFeatureBuilder.text(
position: Pair<Double, Double>,
text: String,
zoomRange: IntRange = defaultZoomRange,
color: Color = Color.Red,
font: MapTextFeatureFont.() -> Unit = { size = 16f },
id: FeatureId? = null,
): FeatureId = addFeature(id, MapTextFeature(position.toCoordinates(), text, zoomRange, color, font))

View File

@ -0,0 +1,5 @@
package center.sciprog.maps.compose
public expect class MapTextFeatureFont {
public var size: Float
}

View File

@ -1,35 +0,0 @@
package center.sciprog.maps.compose
import androidx.compose.ui.graphics.Color
import center.sciprog.maps.coordinates.GeodeticMapCoordinates
import center.sciprog.maps.coordinates.GmcRectangle
import org.jetbrains.skia.Font
public class MapTextFeature(
public val position: GeodeticMapCoordinates,
public val text: String,
override val zoomRange: IntRange = defaultZoomRange,
public val color: Color,
public val fontConfig: Font.() -> Unit,
) : MapFeature {
override fun getBoundingBox(zoom: Int): GmcRectangle = GmcRectangle(position, position)
}
public fun MapFeatureBuilder.text(
position: GeodeticMapCoordinates,
text: String,
zoomRange: IntRange = defaultZoomRange,
color: Color = Color.Red,
font: Font.() -> Unit = { size = 16f },
id: FeatureId? = null,
): FeatureId = addFeature(id, MapTextFeature(position, text, zoomRange, color, font))
public fun MapFeatureBuilder.text(
position: Pair<Double, Double>,
text: String,
zoomRange: IntRange = defaultZoomRange,
color: Color = Color.Red,
font: Font.() -> Unit = { size = 16f },
id: FeatureId? = null,
): FeatureId = addFeature(id, MapTextFeature(position.toCoordinates(), text, zoomRange, color, font))

View File

@ -0,0 +1,5 @@
package center.sciprog.maps.compose
import org.jetbrains.skia.Font
public actual typealias MapTextFeatureFont = Font

View File

@ -197,20 +197,21 @@ public actual fun MapView(
for (j in verticalIndices) { for (j in verticalIndices) {
for (i in horizontalIndices) { for (i in horizontalIndices) {
val id = TileId(zoom, i, j) val id = TileId(zoom, i, j)
//start all try {
val deferred = loadTileAsync(id) //start all
//wait asynchronously for it to finish val deferred = loadTileAsync(id)
launch { //wait asynchronously for it to finish
try { launch {
mapTiles += deferred.await() mapTiles += deferred.await()
} catch (ex: Exception) { }
if (ex !is CancellationException) { } catch (ex: Exception) {
//displaying the error is maps responsibility if (ex !is CancellationException) {
logger.error(ex) { "Failed to load tile with id=$id" } //displaying the error is maps responsibility
} logger.error(ex) { "Failed to load tile with id=$id" }
} }
} }
} }
} }
} }
} }

View File

@ -24,7 +24,7 @@ import kotlin.io.path.*
public class OpenStreetMapTileProvider( public class OpenStreetMapTileProvider(
private val client: HttpClient, private val client: HttpClient,
private val cacheDirectory: Path, private val cacheDirectory: Path,
parallelism: Int = 1, parallelism: Int = 4,
cacheCapacity: Int = 200, cacheCapacity: Int = 200,
) : MapTileProvider { ) : MapTileProvider {
private val semaphore = Semaphore(parallelism) private val semaphore = Semaphore(parallelism)
@ -37,7 +37,7 @@ public class OpenStreetMapTileProvider(
/** /**
* Download and cache the tile image * Download and cache the tile image
*/ */
private fun CoroutineScope.downloadImageAsync(id: TileId) = async(Dispatchers.IO) { private fun CoroutineScope.downloadImageAsync(id: TileId): Deferred<ImageBitmap> = async(Dispatchers.IO) {
id.cacheFilePath()?.let { path -> id.cacheFilePath()?.let { path ->
if (path.exists()) { if (path.exists()) {
@ -54,9 +54,7 @@ public class OpenStreetMapTileProvider(
semaphore.withPermit { semaphore.withPermit {
val url = id.osmUrl() val url = id.osmUrl()
val byteArray = client.get(url).readBytes() val byteArray = client.get(url).readBytes()
logger.debug { "Finished downloading map tile with id $id from $url" } logger.debug { "Finished downloading map tile with id $id from $url" }
id.cacheFilePath()?.let { path -> id.cacheFilePath()?.let { path ->
logger.debug { "Caching map tile $id to $path" } logger.debug { "Caching map tile $id to $path" }
@ -79,11 +77,11 @@ public class OpenStreetMapTileProvider(
//collect the result asynchronously //collect the result asynchronously
return async { return async {
val image = try { val image: ImageBitmap = try {
imageDeferred.await() imageDeferred.await()
} catch (ex: Exception) { } catch (ex: Exception) {
cache.remove(tileId) cache.remove(tileId)
if(ex !is CancellationException) { if (ex !is CancellationException) {
logger.error(ex) { "Failed to load tile image with id=$tileId" } logger.error(ex) { "Failed to load tile image with id=$tileId" }
} }
throw ex throw ex