Fix for TAVRIDA-T-2
This commit is contained in:
parent
792a755682
commit
2ac4bdfd4f
@ -16,8 +16,12 @@ 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()
|
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 {
|
companion object {
|
||||||
const val DEFAULT_TILE_SIZE = 256
|
const val DEFAULT_TILE_SIZE = 256
|
||||||
|
@ -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 ->
|
||||||
|
Loading…
Reference in New Issue
Block a user