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,8 +16,12 @@ data class MapTile(
interface MapTileProvider {
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()
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

View File

@ -4,11 +4,9 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
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.onPointerEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.*
import centre.sciprog.maps.*
import mu.KotlinLogging
import org.jetbrains.skia.Font
@ -46,6 +45,7 @@ actual fun MapView(
onClick: (GeodeticMapCoordinates) -> Unit,
modifier: Modifier,
) {
var viewPoint by remember { mutableStateOf(initialViewPoint) }
val zoom: Int by derivedStateOf { viewPoint.zoom.roundToInt() }
@ -53,18 +53,39 @@ actual fun MapView(
val mapTiles = remember { mutableStateListOf<MapTile>() }
//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) }
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
LaunchedEffect(viewPoint, canvasSize) {
val left = centerCoordinates.x - canvasSize.width / 2
val right = centerCoordinates.x + canvasSize.width / 2
val left = centerCoordinates.x - canvasSize.width.value / 2
val right = centerCoordinates.x + canvasSize.width.value / 2
val horizontalIndices = mapTileProvider.toIndex(left)..mapTileProvider.toIndex(right)
val top = (centerCoordinates.y + canvasSize.height / 2)
val bottom = (centerCoordinates.y - canvasSize.height / 2)
val top = (centerCoordinates.y + canvasSize.height.value / 2)
val bottom = (centerCoordinates.y - canvasSize.height.value / 2)
val verticalIndices = mapTileProvider.toIndex(bottom)..mapTileProvider.toIndex(top)
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(
(canvasSize.width / 2 - centerCoordinates.x + x).toFloat(),
(canvasSize.height / 2 - centerCoordinates.y + y).toFloat()
(canvasSize.width / 2 - centerCoordinates.x.dp + x.dp).toPx(),
(canvasSize.height / 2 - centerCoordinates.y.dp + y.dp).toPx()
)
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) {
when (feature) {
@ -145,22 +151,22 @@ actual fun MapView(
}
}
Canvas(canvasModifier) {
if (canvasSize != size) {
canvasSize = size
logger.debug { "Redraw canvas. Size: $size" }
if (canvasSize != size.toDpSize()) {
canvasSize = size.toDpSize()
logger.debug { "Recalculate canvas. Size: $size" }
}
clipRect {
val tileSize = IntSize(mapTileProvider.tileSize.dp.roundToPx(), mapTileProvider.tileSize.dp.roundToPx())
mapTiles.forEach { (id, image) ->
//converting back from tile index to screen offset
val offset = Offset(
(canvasSize.width / 2 - centerCoordinates.x + mapTileProvider.toCoordinate(id.i)).toFloat(),
(canvasSize.height / 2 - centerCoordinates.y + mapTileProvider.toCoordinate(id.j)).toFloat()
val offset = IntOffset(
(canvasSize.width / 2 - centerCoordinates.x.dp + mapTileProvider.toCoordinate(id.i).dp).roundToPx(),
(canvasSize.height / 2 - centerCoordinates.y.dp + mapTileProvider.toCoordinate(id.j).dp).roundToPx()
)
drawImage(
image = image,
topLeft = offset
dstOffset = offset,
dstSize = tileSize
)
}
features.values.filter { zoom in it.zoomRange }.forEach { feature ->