This commit is contained in:
Alexey 2025-08-25 15:15:51 +03:00
commit 8a530d9d48
4 changed files with 84 additions and 25 deletions

View file

@ -100,7 +100,7 @@ fun Main(vm: TileViewModel = viewModel()) {
Slider(
value = sliderValue.floatValue,
onValueChange = { sliderValue.floatValue = it },
valueRange = 1F..14F,
valueRange = 2F..14F,
modifier = Modifier.align(Alignment.CenterStart)
)
}

View file

@ -5,8 +5,10 @@ import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableFloatState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
@ -22,6 +24,7 @@ import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import kotlin.math.absoluteValue
import kotlin.math.sqrt
@Composable
fun MapCanvas(
@ -31,15 +34,15 @@ fun MapCanvas(
tileContainer: TileContainer,
modifier: Modifier = Modifier
) {
val offsetX = rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
val offsetY = rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
var offsetX by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
var offsetY by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
val textMeasurer = rememberTextMeasurer()
Canvas(
modifier = modifier.fillMaxSize()
.pointerInput(Unit) {
detectDragGestures { _, distance ->
offsetX.floatValue -= distance.x
offsetY.floatValue -= distance.y
offsetX -= distance.x
offsetY -= distance.y
}
}
) {
@ -53,24 +56,24 @@ fun MapCanvas(
if (levelDiff < 0) {
repeat (levelDiff.absoluteValue) {
offsetX.floatValue -= size.width / 2F + TILE_SIZE
offsetY.floatValue -= size.height / 2F + TILE_SIZE
offsetX.floatValue /= 2F
offsetY.floatValue /= 2F
offsetX -= size.width / 2F + TILE_SIZE
offsetY -= size.height / 2F + TILE_SIZE
offsetX /= 2F
offsetY /= 2F
}
} else if (levelDiff > 0) {
repeat (levelDiff) {
offsetX.floatValue *= 2F
offsetY.floatValue *= 2F
offsetX.floatValue += size.width / 2F + TILE_SIZE
offsetY.floatValue += size.height / 2F + TILE_SIZE
offsetX *= 2F
offsetY *= 2F
offsetX += size.width / 2F + TILE_SIZE
offsetY += size.height / 2F + TILE_SIZE
}
}
val tileOffsetX = (offsetX.floatValue / TILE_SIZE).toInt()
val tileOffsetY = (offsetY.floatValue / TILE_SIZE).toInt()
val tileOffsetX = (offsetX / TILE_SIZE).toInt()
val tileOffsetY = (offsetY / TILE_SIZE).toInt()
val strippedOffsetX = offsetX.floatValue % TILE_SIZE
val strippedOffsetY = offsetY.floatValue % TILE_SIZE
val strippedOffsetX = offsetX % TILE_SIZE
val strippedOffsetY = offsetY % TILE_SIZE
val offset = Offset(strippedOffsetX, strippedOffsetY)
@ -89,19 +92,20 @@ fun MapCanvas(
val localOffsetY = TILE_SIZE * (cellY - 1)
val bitmap = tiles.find { it.x == tileX && it.y == tileY && it.level == level }?.toBitmap()
val totalOffset = Offset(localOffsetX, localOffsetY) - offset
bitmap?.let {
val imageBitmap = bitmap.asImageBitmap()
drawImage(
image = imageBitmap,
topLeft = Offset(localOffsetX, localOffsetY) - offset
topLeft = totalOffset
)
}
/*
drawRect(
color = gridColor,
size = Size(TILE_SIZE, TILE_SIZE),
topLeft = Offset(localOffsetX, localOffsetY) - offset,
topLeft = totalOffset,
style = Stroke(width = 4F)
)
@ -110,14 +114,17 @@ fun MapCanvas(
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(SpanStyle(color = gridColor)) {
append("x:%d, y:%d".format(tileX, tileY))
val mapSize = (1 shl (level - 1)).toDouble()
val mappedY = (tileY.toDouble() - (mapSize / 2.0))
val mercX = SphereMercator.sx2lon((tileX * TILE_SIZE).toDouble(), level) - 180.0
val mercY = -SphereMercator.sy2lat(mappedY * TILE_SIZE, level - 1)
append("%.6f, %.6f,\n%d, %d".format(mercX, mercY, tileX, tileY))
}
}
},
topLeft = Offset(localOffsetX, localOffsetY) - offset,
topLeft = totalOffset,
size = Size(TILE_SIZE, TILE_SIZE)
)
*/
}
}
}

View file

@ -0,0 +1,52 @@
package com.mirenkov.ktheightmap
import java.lang.Math.toRadians
import java.lang.Math.toDegrees
import kotlin.math.atan
import kotlin.math.exp
import kotlin.math.ln
import kotlin.math.tan
import kotlin.math.PI
class SphereMercator {
companion object {
const val RADIUS = 6378137
const val METERS_PER_PIXEL = 156543.0339
fun x2lon(x: Double): Double {
return toDegrees(x / RADIUS)
}
fun y2lat(y: Double): Double {
return toDegrees(atan(exp(y / RADIUS / 2)) * 2 - PI / 2)
}
fun lon2x(longitude: Double): Double {
return toRadians(longitude) * RADIUS
}
fun lat2y(latitude: Double): Double {
return ln(tan(PI / 4 + toRadians(latitude) / 2)) * RADIUS
}
fun scaledMetersPerPixel(level: Int): Double {
return METERS_PER_PIXEL / ( 1 shl (level - 1) )
}
fun sx2lon(x: Double, level: Int): Double {
return x2lon(x * scaledMetersPerPixel(level))
}
fun sy2lat(y: Double, level: Int): Double {
return y2lat( y * scaledMetersPerPixel(level))
}
fun lon2sx(longitude: Double, level: Int) : Double {
return lon2x(longitude) / scaledMetersPerPixel(level)
}
fun lat2sy(latitude: Double, level: Int): Double {
return lat2y(latitude) / scaledMetersPerPixel(level)
}
}
}