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>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<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">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/secondbeam/.android/avd/Pixel_3.avd" />
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=96DX21GPR" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ fun Main(vm: TileViewModel = viewModel()) {
|
||||||
Slider(
|
Slider(
|
||||||
value = sliderValue.floatValue,
|
value = sliderValue.floatValue,
|
||||||
onValueChange = { sliderValue.floatValue = it },
|
onValueChange = { sliderValue.floatValue = it },
|
||||||
valueRange = 1F..14F,
|
valueRange = 2F..14F,
|
||||||
modifier = Modifier.align(Alignment.CenterStart)
|
modifier = Modifier.align(Alignment.CenterStart)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,10 @@ import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableFloatState
|
import androidx.compose.runtime.MutableFloatState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableFloatStateOf
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
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.geometry.Size
|
||||||
|
|
@ -22,6 +24,7 @@ import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MapCanvas(
|
fun MapCanvas(
|
||||||
|
|
@ -31,15 +34,15 @@ fun MapCanvas(
|
||||||
tileContainer: TileContainer,
|
tileContainer: TileContainer,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val offsetX = rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
|
var offsetX by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
|
||||||
val offsetY = rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
|
var offsetY by rememberSaveable { mutableFloatStateOf(-TILE_SIZE) }
|
||||||
val textMeasurer = rememberTextMeasurer()
|
val textMeasurer = rememberTextMeasurer()
|
||||||
Canvas(
|
Canvas(
|
||||||
modifier = modifier.fillMaxSize()
|
modifier = modifier.fillMaxSize()
|
||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectDragGestures { _, distance ->
|
detectDragGestures { _, distance ->
|
||||||
offsetX.floatValue -= distance.x
|
offsetX -= distance.x
|
||||||
offsetY.floatValue -= distance.y
|
offsetY -= distance.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
@ -53,24 +56,24 @@ fun MapCanvas(
|
||||||
|
|
||||||
if (levelDiff < 0) {
|
if (levelDiff < 0) {
|
||||||
repeat (levelDiff.absoluteValue) {
|
repeat (levelDiff.absoluteValue) {
|
||||||
offsetX.floatValue -= size.width / 2F + TILE_SIZE
|
offsetX -= size.width / 2F + TILE_SIZE
|
||||||
offsetY.floatValue -= size.height / 2F + TILE_SIZE
|
offsetY -= size.height / 2F + TILE_SIZE
|
||||||
offsetX.floatValue /= 2F
|
offsetX /= 2F
|
||||||
offsetY.floatValue /= 2F
|
offsetY /= 2F
|
||||||
}
|
}
|
||||||
} else if (levelDiff > 0) {
|
} else if (levelDiff > 0) {
|
||||||
repeat (levelDiff) {
|
repeat (levelDiff) {
|
||||||
offsetX.floatValue *= 2F
|
offsetX *= 2F
|
||||||
offsetY.floatValue *= 2F
|
offsetY *= 2F
|
||||||
offsetX.floatValue += size.width / 2F + TILE_SIZE
|
offsetX += size.width / 2F + TILE_SIZE
|
||||||
offsetY.floatValue += size.height / 2F + TILE_SIZE
|
offsetY += size.height / 2F + TILE_SIZE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tileOffsetX = (offsetX.floatValue / TILE_SIZE).toInt()
|
val tileOffsetX = (offsetX / TILE_SIZE).toInt()
|
||||||
val tileOffsetY = (offsetY.floatValue / TILE_SIZE).toInt()
|
val tileOffsetY = (offsetY / TILE_SIZE).toInt()
|
||||||
|
|
||||||
val strippedOffsetX = offsetX.floatValue % TILE_SIZE
|
val strippedOffsetX = offsetX % TILE_SIZE
|
||||||
val strippedOffsetY = offsetY.floatValue % TILE_SIZE
|
val strippedOffsetY = offsetY % TILE_SIZE
|
||||||
|
|
||||||
val offset = Offset(strippedOffsetX, strippedOffsetY)
|
val offset = Offset(strippedOffsetX, strippedOffsetY)
|
||||||
|
|
||||||
|
|
@ -89,19 +92,20 @@ fun MapCanvas(
|
||||||
val localOffsetY = TILE_SIZE * (cellY - 1)
|
val localOffsetY = TILE_SIZE * (cellY - 1)
|
||||||
|
|
||||||
val bitmap = tiles.find { it.x == tileX && it.y == tileY && it.level == level }?.toBitmap()
|
val bitmap = tiles.find { it.x == tileX && it.y == tileY && it.level == level }?.toBitmap()
|
||||||
|
val totalOffset = Offset(localOffsetX, localOffsetY) - offset
|
||||||
|
|
||||||
bitmap?.let {
|
bitmap?.let {
|
||||||
val imageBitmap = bitmap.asImageBitmap()
|
val imageBitmap = bitmap.asImageBitmap()
|
||||||
drawImage(
|
drawImage(
|
||||||
image = imageBitmap,
|
image = imageBitmap,
|
||||||
topLeft = Offset(localOffsetX, localOffsetY) - offset
|
topLeft = totalOffset
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
drawRect(
|
drawRect(
|
||||||
color = gridColor,
|
color = gridColor,
|
||||||
size = Size(TILE_SIZE, TILE_SIZE),
|
size = Size(TILE_SIZE, TILE_SIZE),
|
||||||
topLeft = Offset(localOffsetX, localOffsetY) - offset,
|
topLeft = totalOffset,
|
||||||
style = Stroke(width = 4F)
|
style = Stroke(width = 4F)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -110,14 +114,17 @@ fun MapCanvas(
|
||||||
text = buildAnnotatedString {
|
text = buildAnnotatedString {
|
||||||
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
|
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
|
||||||
withStyle(SpanStyle(color = gridColor)) {
|
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)
|
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