diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt index 202e30b..f235db8 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeature.kt @@ -132,4 +132,14 @@ public class MapFeatureGroup( override val zoomRange: IntRange = defaultZoomRange, ) : MapFeature { override fun getBoundingBox(zoom: Int): GmcBox? = children.values.mapNotNull { it.getBoundingBox(zoom) }.wrapAll() -} \ No newline at end of file +} + +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) +} diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeatureBuilder.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeatureBuilder.kt index e916f12..c11a0b6 100644 --- a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeatureBuilder.kt +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapFeatureBuilder.kt @@ -143,4 +143,22 @@ public fun MapFeatureBuilder.group( val map = MapFeatureBuilderImpl(emptyMap()).apply(builder).build() val feature = MapFeatureGroup(map, zoomRange) return addFeature(id, feature) -} \ No newline at end of file +} + +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, + 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)) diff --git a/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapTextFeatureFont.kt b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapTextFeatureFont.kt new file mode 100644 index 0000000..defe478 --- /dev/null +++ b/maps-kt-compose/src/commonMain/kotlin/center/sciprog/maps/compose/MapTextFeatureFont.kt @@ -0,0 +1,5 @@ +package center.sciprog.maps.compose + +public expect class MapTextFeatureFont { + public var size: Float +} \ No newline at end of file diff --git a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeature.kt b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeature.kt deleted file mode 100644 index fef40fb..0000000 --- a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeature.kt +++ /dev/null @@ -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.GmcBox -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): GmcBox = GmcBox(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, - 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)) diff --git a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeatureFontJvm.kt b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeatureFontJvm.kt new file mode 100644 index 0000000..1a2023a --- /dev/null +++ b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapTextFeatureFontJvm.kt @@ -0,0 +1,5 @@ +package center.sciprog.maps.compose + +import org.jetbrains.skia.Font + +public actual typealias MapTextFeatureFont = Font \ No newline at end of file diff --git a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt index 27f9496..9c6443f 100644 --- a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt +++ b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/MapViewJvm.kt @@ -195,20 +195,21 @@ public actual fun MapView( for (j in verticalIndices) { for (i in horizontalIndices) { val id = TileId(zoom, i, j) - //start all - val deferred = loadTileAsync(id) - //wait asynchronously for it to finish - launch { - try { + try { + //start all + val deferred = loadTileAsync(id) + //wait asynchronously for it to finish + launch { mapTiles += deferred.await() - } catch (ex: Exception) { - if (ex !is CancellationException) { - //displaying the error is maps responsibility - logger.error(ex) { "Failed to load tile with id=$id" } - } + } + } catch (ex: Exception) { + if (ex !is CancellationException) { + //displaying the error is maps responsibility + logger.error(ex) { "Failed to load tile with id=$id" } } } } + } } } @@ -232,6 +233,7 @@ public actual fun MapView( feature.size, center = feature.center.toOffset() ) + is MapRectangleFeature -> drawRect( feature.color, topLeft = feature.center.toOffset() - Offset( @@ -240,6 +242,7 @@ public actual fun MapView( ), size = feature.size.toSize() ) + is MapLineFeature -> drawLine(feature.color, feature.a.toOffset(), feature.b.toOffset()) is MapArcFeature -> { val topLeft = feature.oval.topLeft.toOffset() @@ -252,6 +255,7 @@ public actual fun MapView( drawPath(path, color = feature.color, style = Stroke()) } + is MapBitmapImageFeature -> drawImage(feature.image, feature.position.toOffset()) is MapVectorImageFeature -> { val offset = feature.position.toOffset() @@ -262,6 +266,7 @@ public actual fun MapView( } } } + is MapTextFeature -> drawIntoCanvas { canvas -> val offset = feature.position.toOffset() canvas.nativeCanvas.drawString( @@ -272,17 +277,20 @@ public actual fun MapView( feature.color.toPaint() ) } + is MapDrawFeature -> { val offset = feature.position.toOffset() translate(offset.x, offset.y) { feature.drawFeature(this) } } + is MapFeatureGroup -> { feature.children.values.forEach { drawFeature(zoom, it) } } + is MapPointsFeature -> { val points = feature.points.map { it.toOffset() } drawPoints( @@ -292,6 +300,7 @@ public actual fun MapView( pointMode = feature.pointMode ) } + else -> { logger.error { "Unrecognized feature type: ${feature::class}" } } diff --git a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/OpenStreetMapTileProvider.kt b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/OpenStreetMapTileProvider.kt index 4929254..4a98ae4 100644 --- a/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/OpenStreetMapTileProvider.kt +++ b/maps-kt-compose/src/jvmMain/kotlin/center/sciprog/maps/compose/OpenStreetMapTileProvider.kt @@ -24,7 +24,7 @@ import kotlin.io.path.* public class OpenStreetMapTileProvider( private val client: HttpClient, private val cacheDirectory: Path, - parallelism: Int = 1, + parallelism: Int = 4, cacheCapacity: Int = 200, ) : MapTileProvider { private val semaphore = Semaphore(parallelism) @@ -37,7 +37,7 @@ public class OpenStreetMapTileProvider( /** * Download and cache the tile image */ - private fun CoroutineScope.downloadImageAsync(id: TileId) = async(Dispatchers.IO) { + private fun CoroutineScope.downloadImageAsync(id: TileId): Deferred = async(Dispatchers.IO) { id.cacheFilePath()?.let { path -> if (path.exists()) { @@ -54,9 +54,7 @@ public class OpenStreetMapTileProvider( semaphore.withPermit { val url = id.osmUrl() val byteArray = client.get(url).readBytes() - logger.debug { "Finished downloading map tile with id $id from $url" } - id.cacheFilePath()?.let { path -> logger.debug { "Caching map tile $id to $path" } @@ -79,11 +77,11 @@ public class OpenStreetMapTileProvider( //collect the result asynchronously return async { - val image = try { + val image: ImageBitmap = try { imageDeferred.await() } catch (ex: Exception) { cache.remove(tileId) - if(ex !is CancellationException) { + if (ex !is CancellationException) { logger.error(ex) { "Failed to load tile image with id=$tileId" } } throw ex @@ -96,4 +94,4 @@ public class OpenStreetMapTileProvider( public companion object { private val logger = KotlinLogging.logger("OpenStreetMapCache") } -} +} \ No newline at end of file