Fix for TAVRIDA-T-2

This commit is contained in:
Alexander Nozik 2022-07-11 20:29:07 +03:00
parent 792a755682
commit 2ac4bdfd4f
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
2 changed files with 85 additions and 75 deletions

View File

@ -16,10 +16,14 @@ data class MapTile(
interface MapTileProvider { interface MapTileProvider {
suspend fun loadTile(id: TileId): MapTile suspend fun loadTile(id: TileId): MapTile
fun toIndex(d: Double): Int = floor(d / DEFAULT_TILE_SIZE).toInt()
fun toCoordinate(i: Int): Double = (i * DEFAULT_TILE_SIZE).toDouble()
companion object{ val tileSize: Int get() = DEFAULT_TILE_SIZE
fun toIndex(d: Double): Int = floor(d / tileSize).toInt()
fun toCoordinate(i: Int): Double = (i * tileSize).toDouble()
companion object {
const val DEFAULT_TILE_SIZE = 256 const val DEFAULT_TILE_SIZE = 256
} }
} }

View File

@ -4,11 +4,9 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.clipRect import androidx.compose.ui.graphics.drawscope.clipRect
@ -20,6 +18,7 @@ import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.*
import centre.sciprog.maps.* import centre.sciprog.maps.*
import mu.KotlinLogging import mu.KotlinLogging
import org.jetbrains.skia.Font import org.jetbrains.skia.Font
@ -46,6 +45,7 @@ actual fun MapView(
onClick: (GeodeticMapCoordinates) -> Unit, onClick: (GeodeticMapCoordinates) -> Unit,
modifier: Modifier, modifier: Modifier,
) { ) {
var viewPoint by remember { mutableStateOf(initialViewPoint) } var viewPoint by remember { mutableStateOf(initialViewPoint) }
val zoom: Int by derivedStateOf { viewPoint.zoom.roundToInt() } val zoom: Int by derivedStateOf { viewPoint.zoom.roundToInt() }
@ -53,18 +53,39 @@ actual fun MapView(
val mapTiles = remember { mutableStateListOf<MapTile>() } val mapTiles = remember { mutableStateListOf<MapTile>() }
//var mapRectangle by remember { mutableStateOf(initialRectangle) } //var mapRectangle by remember { mutableStateOf(initialRectangle) }
var canvasSize by remember { mutableStateOf(Size(512f, 512f)) } var canvasSize by remember { mutableStateOf(DpSize(512.dp, 512.dp)) }
val centerCoordinates by derivedStateOf { WebMercatorProjection.toMercator(viewPoint.focus, zoom) } val centerCoordinates by derivedStateOf { WebMercatorProjection.toMercator(viewPoint.focus, zoom) }
fun DpOffset.toMercator(): WebMercatorCoordinates = WebMercatorCoordinates(
zoom,
(x - canvasSize.width / 2).value + centerCoordinates.x,
(y - canvasSize.height / 2).value + centerCoordinates.y,
)
fun DpOffset.toGeodetic() = WebMercatorProjection.toGeodetic(toMercator())
@OptIn(ExperimentalComposeUiApi::class)
val canvasModifier = modifier.onPointerEvent(PointerEventType.Press) {
val (xPos, yPos) = it.changes.first().position
onClick(DpOffset(xPos.toDp(), yPos.toDp()).toGeodetic())
}.onPointerEvent(PointerEventType.Scroll) {
viewPoint = viewPoint.zoom(-it.changes.first().scrollDelta.y.toDouble())
}.pointerInput(Unit) {
detectDragGestures { _: PointerInputChange, dragAmount: Offset ->
viewPoint = viewPoint.move(-dragAmount.x, +dragAmount.y)
}
}.fillMaxSize()
// Load tiles asynchronously // Load tiles asynchronously
LaunchedEffect(viewPoint, canvasSize) { LaunchedEffect(viewPoint, canvasSize) {
val left = centerCoordinates.x - canvasSize.width / 2 val left = centerCoordinates.x - canvasSize.width.value / 2
val right = centerCoordinates.x + canvasSize.width / 2 val right = centerCoordinates.x + canvasSize.width.value / 2
val horizontalIndices = mapTileProvider.toIndex(left)..mapTileProvider.toIndex(right) val horizontalIndices = mapTileProvider.toIndex(left)..mapTileProvider.toIndex(right)
val top = (centerCoordinates.y + canvasSize.height / 2) val top = (centerCoordinates.y + canvasSize.height.value / 2)
val bottom = (centerCoordinates.y - canvasSize.height / 2) val bottom = (centerCoordinates.y - canvasSize.height.value / 2)
val verticalIndices = mapTileProvider.toIndex(bottom)..mapTileProvider.toIndex(top) val verticalIndices = mapTileProvider.toIndex(bottom)..mapTileProvider.toIndex(top)
mapTiles.clear() mapTiles.clear()
@ -87,31 +108,16 @@ actual fun MapView(
} }
fun Offset.toMercator(): WebMercatorCoordinates = WebMercatorCoordinates(
zoom,
x + centerCoordinates.x - canvasSize.width / 2,
y + centerCoordinates.y - canvasSize.height / 2,
)
fun Offset.toGeodetic() = WebMercatorProjection.toGeodetic(toMercator()) Canvas(canvasModifier) {
fun WebMercatorCoordinates.toOffset(): Offset = Offset( fun WebMercatorCoordinates.toOffset(): Offset = Offset(
(canvasSize.width / 2 - centerCoordinates.x + x).toFloat(), (canvasSize.width / 2 - centerCoordinates.x.dp + x.dp).toPx(),
(canvasSize.height / 2 - centerCoordinates.y + y).toFloat() (canvasSize.height / 2 - centerCoordinates.y.dp + y.dp).toPx()
) )
fun GeodeticMapCoordinates.toOffset(): Offset = WebMercatorProjection.toMercator(this, zoom).toOffset() fun GeodeticMapCoordinates.toOffset(): Offset = WebMercatorProjection.toMercator(this, zoom).toOffset()
@OptIn(ExperimentalComposeUiApi::class)
val canvasModifier = modifier.onPointerEvent(PointerEventType.Press) {
onClick(it.changes.first().position.toGeodetic())
}.onPointerEvent(PointerEventType.Scroll) {
viewPoint = viewPoint.zoom(-it.changes.first().scrollDelta.y.toDouble())
}.pointerInput(Unit) {
detectDragGestures { _: PointerInputChange, dragAmount: Offset ->
viewPoint = viewPoint.move(-dragAmount.x, +dragAmount.y)
}
}.fillMaxSize()
fun DrawScope.drawFeature(zoom: Int, feature: MapFeature) { fun DrawScope.drawFeature(zoom: Int, feature: MapFeature) {
when (feature) { when (feature) {
@ -145,22 +151,22 @@ actual fun MapView(
} }
} }
if (canvasSize != size.toDpSize()) {
Canvas(canvasModifier) { canvasSize = size.toDpSize()
if (canvasSize != size) { logger.debug { "Recalculate canvas. Size: $size" }
canvasSize = size
logger.debug { "Redraw canvas. Size: $size" }
} }
clipRect { clipRect {
val tileSize = IntSize(mapTileProvider.tileSize.dp.roundToPx(), mapTileProvider.tileSize.dp.roundToPx())
mapTiles.forEach { (id, image) -> mapTiles.forEach { (id, image) ->
//converting back from tile index to screen offset //converting back from tile index to screen offset
val offset = Offset( val offset = IntOffset(
(canvasSize.width / 2 - centerCoordinates.x + mapTileProvider.toCoordinate(id.i)).toFloat(), (canvasSize.width / 2 - centerCoordinates.x.dp + mapTileProvider.toCoordinate(id.i).dp).roundToPx(),
(canvasSize.height / 2 - centerCoordinates.y + mapTileProvider.toCoordinate(id.j)).toFloat() (canvasSize.height / 2 - centerCoordinates.y.dp + mapTileProvider.toCoordinate(id.j).dp).roundToPx()
) )
drawImage( drawImage(
image = image, image = image,
topLeft = offset dstOffset = offset,
dstSize = tileSize
) )
} }
features.values.filter { zoom in it.zoomRange }.forEach { feature -> features.values.filter { zoom in it.zoomRange }.forEach { feature ->