mercator
This commit is contained in:
parent
47cd68fcfe
commit
8a530d9d48
4 changed files with 84 additions and 25 deletions
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
|
|
@ -4,10 +4,10 @@
|
|||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-08-08T10:50:02.571721922Z">
|
||||
<DropdownSelection timestamp="2025-08-21T11:17:37.943341883Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/secondbeam/.android/avd/Pixel_3.avd" />
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=96DX21GPR" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
52
app/src/main/java/com/mirenkov/ktheightmap/SphereMercator.kt
Normal file
52
app/src/main/java/com/mirenkov/ktheightmap/SphereMercator.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue