Working box by features

This commit is contained in:
Alexander Nozik 2022-07-14 20:32:31 +03:00
parent 9392c0f991
commit d3809aca8d
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
5 changed files with 30 additions and 36 deletions

View File

@ -1,8 +1,8 @@
package centre.sciprog.maps
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import androidx.compose.ui.unit.DpSize
import centre.sciprog.maps.compose.MapFeature
import kotlin.math.*
class GmcBox(val a: GeodeticMapCoordinates, val b: GeodeticMapCoordinates)
@ -28,9 +28,10 @@ val GmcBox.width get() = abs(a.longitude - b.longitude)
val GmcBox.height get() = abs(a.latitude - b.latitude)
/**
* Compute a minimal bounding box including all given boxes
* Compute a minimal bounding box including all given boxes. Return null if collection is empty
*/
fun Iterable<GmcBox>.wrapAll(): GmcBox {
fun Collection<GmcBox>.wrapAll(): GmcBox? {
if (isEmpty()) return null
//TODO optimize computation
val minLat = minOf { it.bottom }
val maxLat = maxOf { it.top }

View File

@ -17,7 +17,7 @@ sealed class MapFeature(val zoomRange: IntRange) {
abstract fun getBoundingBox(zoom: Int): GmcBox
}
fun Iterable<MapFeature>.computeBoundingBox(zoom: Int): GmcBox = map { it.getBoundingBox(zoom) }.wrapAll()
fun Iterable<MapFeature>.computeBoundingBox(zoom: Int): GmcBox? = map { it.getBoundingBox(zoom) }.wrapAll()
internal fun Pair<Double, Double>.toCoordinates() = GeodeticMapCoordinates.ofDegrees(first, second)

View File

@ -12,6 +12,7 @@ import kotlin.math.min
data class MapViewConfig(
val zoomSpeed: Double = 1.0 / 3.0,
val inferViewBoxFromFeatures: Boolean = false
)
@Composable
@ -62,27 +63,4 @@ fun MapView(
MapViewPoint(box.center, zoom)
}
MapView(mapTileProvider, computeViewPoint, featuresBuilder.build(), onClick, config, modifier)
}
/**
* Create a MapView with initial [MapViewPoint] inferred from features
*
* @param defaultZoom the zoom, for which the bounding box is computed
*/
@Composable
fun MapViewWithFeatures(
mapTileProvider: MapTileProvider,
features: Map<FeatureId, MapFeature> = emptyMap(),
onClick: (GeodeticMapCoordinates) -> Unit = {},
config: MapViewConfig = MapViewConfig(),
defaultZoom: Int = 1,
modifier: Modifier = Modifier.fillMaxSize(),
buildFeatures: @Composable (FeatureBuilder.() -> Unit),
) {
val featuresBuilder = MapFeatureBuilder(features)
featuresBuilder.buildFeatures()
val featureSet = featuresBuilder.build()
if(featureSet.isEmpty()) error("Can't create `MapViewWithFeatures` from empty feature set")
val box: GmcBox = featureSet.values.map { it.getBoundingBox(defaultZoom) }.wrapAll()
MapView(mapTileProvider, box, features, onClick, config, modifier, buildFeatures)
}

View File

@ -10,7 +10,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import centre.sciprog.maps.GeodeticMapCoordinates
import centre.sciprog.maps.GmcBox
import centre.sciprog.maps.MapViewPoint
import centre.sciprog.maps.compose.*
import io.ktor.client.HttpClient
@ -41,7 +40,12 @@ fun App() {
Column {
//display click coordinates
Text(coordinates?.toString() ?: "")
MapViewWithFeatures(mapTileProvider, onClick = { gmc: GeodeticMapCoordinates -> coordinates = gmc }) {
MapView(
mapTileProvider,
viewPoint,
onClick = { gmc -> coordinates = gmc },
config = MapViewConfig(inferViewBoxFromFeatures = true)
) {
val pointOne = 55.568548 to 37.568604
val pointTwo = 55.929444 to 37.518434

View File

@ -20,10 +20,7 @@ import centre.sciprog.maps.*
import mu.KotlinLogging
import org.jetbrains.skia.Font
import org.jetbrains.skia.Paint
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.log2
import kotlin.math.pow
import kotlin.math.*
private fun Color.toPaint(): Paint = Paint().apply {
@ -48,7 +45,21 @@ actual fun MapView(
) {
var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
var viewPointOverride by remember { mutableStateOf<MapViewPoint?>(null) }
var viewPointOverride by remember { mutableStateOf<MapViewPoint?>(
if(config.inferViewBoxFromFeatures){
features.values.computeBoundingBox(1)?.let { box ->
val zoom = log2(
min(
canvasSize.width.value / box.width,
canvasSize.height.value / box.height
) * PI / mapTileProvider.tileSize
)
MapViewPoint(box.center, zoom)
}
} else {
null
}
) }
val viewPoint by derivedStateOf { viewPointOverride ?: computeViewPoint(canvasSize) }