Height on distance

This commit is contained in:
Alexey 2025-09-23 12:51:23 +03:00
commit 470535cac8
5 changed files with 99 additions and 39 deletions

View file

@ -76,5 +76,13 @@ class KhmParser {
return height
}
}
fun getLonPerValue(): Float {
return header?.lonPerValue ?: 0F
}
fun getLatPerValue(): Float {
return header?.latPerValue ?: 0F
}
}
}

View file

@ -3,7 +3,6 @@ package com.mirenkov.ktheightmap
import android.app.Application
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@ -32,7 +31,6 @@ import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@ -43,7 +41,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.toUpperCase
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@ -122,12 +119,13 @@ fun Main(vm: TileViewModel = viewModel()) {
}
}
@Suppress("VariableNeverRead", "AssignedValueIsNeverRead")
@Composable
fun ToolButton(viewModel: TileViewModel) {
val context = LocalContext.current
var expanded by rememberSaveable{ mutableStateOf(false) }
var debug by rememberSaveable{ viewModel.debug }
var logRequested by remember{ viewModel.logRequested }
var pointRequested by remember{ viewModel.pointRequested }
val dialogShown = rememberSaveable{ mutableStateOf(false) }
FloatingActionButton(
onClick = {
@ -147,9 +145,9 @@ fun ToolButton(viewModel: TileViewModel) {
}
)
DropdownMenuItem(
text = { Text("Log") },
text = { Text("Place point") },
onClick = {
logRequested = true
pointRequested = true
expanded = false
}
)
@ -175,7 +173,6 @@ fun ToolButton(viewModel: TileViewModel) {
@Suppress("VariableNeverRead", "AssignedValueIsNeverRead")
@Composable
fun SetLocationDialog(vm: TileViewModel, dialogShown: MutableState<Boolean>) {
val ctx = LocalContext.current
var showLocationDialog by dialogShown
if (showLocationDialog) {
var latitudeText by remember { vm.latitudeText }
@ -210,12 +207,8 @@ fun SetLocationDialog(vm: TileViewModel, dialogShown: MutableState<Boolean>) {
confirmButton = { TextButton(onClick = {
val latitude = latitudeText.text.toDoubleOrNull() ?: 0.0
val longitude = longitudeText.text.toDoubleOrNull() ?: 0.0
Log.i(TAG, "Lat: %.6f, Lon: %.6f".format(latitude, longitude))
Log.i(TAG, "X = %.6f".format(SphereMercator.mercateLon(longitude, vm.scale.floatValue.toInt(), -TILE_SIZE.toDouble())))
Log.i(TAG, "Y = %.6f".format(SphereMercator.mercateLat(latitude, vm.scale.floatValue.toInt(), -TILE_SIZE.toDouble())))
offsetX = SphereMercator.mercateLon(longitude, vm.scale.floatValue.toInt(), -vm.halvedOffsetX!!.toDouble() - TILE_SIZE).toFloat()
offsetY = SphereMercator.mercateLat(latitude, vm.scale.floatValue.toInt(), -vm.halvedOffsetY!!.toDouble() - TILE_SIZE).toFloat()
Log.i(TAG, "%d".format(KhmParser.getHeight(longitude.toFloat(), latitude.toFloat(), ctx).toInt()))
}) {
Text("Confirm".uppercase())
} },

View file

@ -1,6 +1,5 @@
package com.mirenkov.ktheightmap
import android.util.Log
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.fillMaxSize
@ -25,6 +24,10 @@ 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.ceil
import kotlin.math.floor
import kotlin.math.pow
import kotlin.math.sqrt
@Composable
fun MapCanvas(
@ -40,7 +43,9 @@ fun MapCanvas(
val scale by rememberSaveable { viewModel.scale }
val textMeasurer = rememberTextMeasurer()
val debug by rememberSaveable { viewModel.debug }
var logRequested by rememberSaveable { viewModel.logRequested }
var pointRequested by rememberSaveable { viewModel.pointRequested }
var pointLat by rememberSaveable { viewModel.rememberedPointLat }
var pointLon by rememberSaveable { viewModel.rememberedPointLon }
Canvas(
modifier = modifier.fillMaxSize()
.pointerInput(Unit) {
@ -57,16 +62,6 @@ fun MapCanvas(
val halvedX = viewModel.halvedOffsetX!!
val halvedY = viewModel.halvedOffsetY!!
drawRect(
color = backColor,
size = size
)
if (logRequested) {
Log.i(TAG, "Offset: %.6f, %.6f".format(offsetX, offsetY))
logRequested = false
}
val oldLevel = tileContainer.getLevel()
val level = scale.toInt()
val levelDiff = level - oldLevel
@ -86,12 +81,27 @@ fun MapCanvas(
offsetY += halvedY + TILE_SIZE
}
}
val tileOffsetX = (offsetX / TILE_SIZE).toInt()
val tileOffsetY = (offsetY / TILE_SIZE).toInt()
val centerTileX = (1 + (offsetX + halvedX) / TILE_SIZE).toDouble()
val centerTileY = (1 + (offsetY + halvedY) / TILE_SIZE).toDouble()
val lon = SphereMercator.mercateX(centerTileX, level).toFloat()
val lat = SphereMercator.mercateY(centerTileY, level).toFloat()
if (pointRequested) {
if (pointLat == 0F) {
pointLat = lat
pointLon = lon
} else {
pointLat = 0F
pointLon = 0F
}
pointRequested = false
}
val tileOffsetX = (offsetX / TILE_SIZE).toInt()
val tileOffsetY = (offsetY / TILE_SIZE).toInt()
val strippedOffsetX = offsetX % TILE_SIZE
val strippedOffsetY = offsetY % TILE_SIZE
@ -110,6 +120,12 @@ fun MapCanvas(
val latLonSize = Size(216F, 96F + additionalSize)
val latLonOffset = Offset(16F, 16F)
// Background
drawRect(
color = backColor,
size = size
)
for (cellX in 0 .. gridWidth + 2) {
val tileX = tileOffsetX + cellX
val localOffsetX = TILE_SIZE * (cellX - 1)
@ -121,6 +137,7 @@ fun MapCanvas(
val bitmap = tile.toBitmap()
val totalOffset = Offset(localOffsetX, localOffsetY) - offset
// Tile
bitmap?.let {
val imageBitmap = bitmap.asImageBitmap()
drawImage(
@ -129,6 +146,7 @@ fun MapCanvas(
)
}
// Debug grid
if (debug) {
drawRect(
color = gridColor,
@ -153,6 +171,51 @@ fun MapCanvas(
)
}
// Placed point and line to center
if (pointLat != 0F) {
val pointOffsetX = SphereMercator.mercateLon(pointLon.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetX
val pointOffsetY = SphereMercator.mercateLat(pointLat.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetY
drawRect(
color = gridColor,
size = Size(8F, 8F),
topLeft = Offset(pointOffsetX - 4, pointOffsetY - 4)
)
drawLine(
color = gridColor,
start = Offset(pointOffsetX, pointOffsetY),
end = Offset(halvedX, halvedY)
)
val startHeight = KhmParser.getHeight(pointLon, pointLat, ctx)
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString { withStyle(SpanStyle(color = Color.White)) {
append("${startHeight}m")
} },
topLeft = Offset(pointOffsetX, pointOffsetY - 32)
)
val latPV = KhmParser.getLatPerValue()
val lonPV = KhmParser.getLonPerValue()
val valueDistance = sqrt(latPV.pow(2) + lonPV.pow(2))
val latDiff = lat - pointLat
val lonDiff = lon - pointLon
val distance = sqrt((latDiff).pow(2) + (lonDiff).pow(2))
val valuesCount = floor(distance / valueDistance).toInt()
for (step in 0 .. valuesCount) {
val interCoef = 1F - step.toFloat() / valuesCount
val stepLat = lat - latDiff * interCoef
val stepLon = lon - lonDiff * interCoef
val stepOffsetX = SphereMercator.mercateLon(stepLon.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetX
val stepOffsetY = SphereMercator.mercateLat(stepLat.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetY
val height = KhmParser.getHeight(stepLon, stepLat, ctx)
drawRect(
color = if (height > startHeight) Color.Red else Color.Green,
size = Size(8F, 8F),
topLeft = Offset(stepOffsetX - 4, stepOffsetY - 4)
)
}
}
// Cursor path
val path = Path()
path.moveTo(halvedX - crossRadius, halvedY)
path.lineTo(halvedX + crossRadius, halvedY)
@ -160,27 +223,27 @@ fun MapCanvas(
path.lineTo(halvedX, halvedY + crossRadius)
path.close()
// Cursor
drawPath(
path,
Color.White,
style = Stroke(width = 6F)
)
// Info box
drawRect(
color = backColor,
size = latLonSize,
topLeft = latLonOffset
)
val lon = SphereMercator.mercateX(centerTileX, level)
val lat = SphereMercator.mercateY(centerTileY, level)
// Info box content
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(SpanStyle(color = gridColor)) {
append("%.6f\n%.6f".format(lon, lat))
append("%.6f\n%.6f".format(lat, lon))
if (debug) {
append("\n%.0f\n%.0f".format(offsetX, offsetY))
}
@ -191,15 +254,12 @@ fun MapCanvas(
topLeft = latLonOffset
)
// Height under cursor
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(SpanStyle(color = Color.White)) {
append(
"%dm".format(
KhmParser.getHeight(lon.toFloat(), lat.toFloat(), ctx).toInt()
)
)
append("${KhmParser.getHeight(lon, lat, ctx)}m")
}
},
topLeft = Offset(halvedX + crossRadius, halvedY + crossRadius)

View file

@ -89,10 +89,7 @@ fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher<DocumentFil
val t = getTile(it.x, it.y, it.level)
if (t == null) {
pushTile(it)
Log.i(TAG, "pushed to db: %d, %d, %d".format(it.level, it.x, it.y))
}
else
Log.i(TAG, "found in db: %d, %d, %d".format(t.level, t.x, t.y))
}
}
} },
@ -124,7 +121,6 @@ suspend fun processZip(filePath: String?): List<Tile> = coroutineScope {
if (entry == null)
break
}
Log.i(TAG, "got list")
}
}
return@coroutineScope list

View file

@ -10,7 +10,7 @@ class TileViewModel(application: Application): ViewModel() {
val repository: TileRepository
var debug = mutableStateOf(false)
var logRequested = mutableStateOf(false)
var pointRequested = mutableStateOf(false)
var latitudeText = mutableStateOf(TextFieldValue(""))
var longitudeText = mutableStateOf(TextFieldValue(""))
@ -22,6 +22,9 @@ class TileViewModel(application: Application): ViewModel() {
var halvedOffsetX: Float? = null
var halvedOffsetY: Float? = null
var rememberedPointLon = mutableFloatStateOf(0F)
var rememberedPointLat = mutableFloatStateOf(0F)
init {
val tileDb = TileDB.getInstance(application)
val tileDao = tileDb.tileDao()