diff --git a/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt b/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt index aabae44..02f6914 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt @@ -76,5 +76,13 @@ class KhmParser { return height } } + + fun getLonPerValue(): Float { + return header?.lonPerValue ?: 0F + } + + fun getLatPerValue(): Float { + return header?.latPerValue ?: 0F + } } } \ No newline at end of file diff --git a/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt b/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt index 53d6a6b..ce22994 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/MainActivity.kt @@ -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) { - 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) { 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()) } }, diff --git a/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt b/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt index 266a568..0a8e7e5 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt @@ -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) diff --git a/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt b/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt index 1bfed6c..7e2985f 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/SettingsActivity.kt @@ -89,10 +89,7 @@ fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher = coroutineScope { if (entry == null) break } - Log.i(TAG, "got list") } } return@coroutineScope list diff --git a/app/src/main/java/com/mirenkov/ktheightmap/TileViewModel.kt b/app/src/main/java/com/mirenkov/ktheightmap/TileViewModel.kt index 06e829d..e915479 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/TileViewModel.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/TileViewModel.kt @@ -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()