From c7b22f012b7da06b808e1a050ad91d869052d4f2 Mon Sep 17 00:00:00 2001 From: 2ndbeam <2ndbeam@disroot.org> Date: Wed, 24 Sep 2025 16:20:10 +0300 Subject: [PATCH] Optimized heights parsing --- .../com/mirenkov/ktheightmap/KhmParser.kt | 39 ++++++---- .../com/mirenkov/ktheightmap/MapCanvas.kt | 77 +++++++++++-------- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt b/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt index 460e126..43118af 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/KhmParser.kt @@ -88,30 +88,35 @@ class KhmParser { dis.use { val header = readHeader(dis) - val glist: Array, Int>> = Array(coords.size) { i -> - val x = ((coords[i].first - header.minLon) / header.lonPerValue).toInt() - val y = ((coords[i].second - header.minLat) / header.latPerValue).toInt() + val glist: MutableMap, Int> = mutableMapOf() + + for (coord in coords) { + val x = ((coord.first - header.minLon) / header.lonPerValue).toInt() + val y = ((coord.second - header.minLat) / header.latPerValue).toInt() val offset = getOffset(header, x, y) - Pair(coords[i], offset) - } - glist.sortBy { - it.second + glist.put(coord, offset) } + val sortedGlist = glist.toList().sortedBy { (_, v) -> v }.toMap() + val glistKeys = sortedGlist.keys.toTypedArray() - coords.sortBy { oit -> - glist.find { it.first == oit }!!.second + val cutOffsets = IntArray(glist.size) {-1} + if (inBounds(header, coords[0].first, coords[0].second)) { + val key = glistKeys[0] + cutOffsets[0] = sortedGlist.getOrDefault(key, 0) } - - val cutOffsets = IntArray(glist.size) - cutOffsets[0] = glist[0].second - for (i in 1 until glist.size) { - cutOffsets[i] = glist[i].second - glist[i-1].second - 2 - assert(cutOffsets[i] >= 0) + for (i in 1 until sortedGlist.size) { + if (inBounds(header, coords[i].first, coords[i].second) && cutOffsets[i-1] >= 0) { + val prevKey = glistKeys[i-1] + val key = glistKeys[i] + cutOffsets[i] = sortedGlist.getOrDefault(key, 0) - sortedGlist.getOrDefault(prevKey, 0) - 2 + } else break } return Pair(UShortArray(coords.size) { i -> - dis.skipBytes(cutOffsets[i]) - dis.readUnsignedShort().toUShort() + if (cutOffsets[i] > 0) { + dis.skipBytes(cutOffsets[i]) + dis.readUnsignedShort().toUShort() + } else 0u }, coords) } } diff --git a/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt b/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt index d186ac7..b1789e5 100644 --- a/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt +++ b/app/src/main/java/com/mirenkov/ktheightmap/MapCanvas.kt @@ -5,6 +5,8 @@ import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -46,12 +48,21 @@ fun MapCanvas( var pointRequested by rememberSaveable { viewModel.pointRequested } var pointLat by rememberSaveable { viewModel.rememberedPointLat } var pointLon by rememberSaveable { viewModel.rememberedPointLon } + var invokeHeightCalc by remember { mutableStateOf(false) } Canvas( modifier = modifier.fillMaxSize() .pointerInput(Unit) { - detectDragGestures { _, distance -> + detectDragGestures ( + onDragEnd = { + invokeHeightCalc = true + }, + onDragCancel = { + invokeHeightCalc = true + } + ) { _, distance -> offsetX -= distance.x offsetY -= distance.y + invokeHeightCalc = false } } ) { @@ -186,37 +197,41 @@ fun MapCanvas( 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() - val array: Array> = Array(valuesCount) { step -> - val interCoef = 1F - step.toFloat() / valuesCount - Pair(lon - lonDiff * interCoef, lat - latDiff * interCoef) - } - val heightPair = KhmParser.getHeightsMul(ctx, array) - val heights = heightPair.first - val coords = heightPair.second - for (step in 0 until coords.size) { - val stepLat = coords[step].second - val stepLon = coords[step].first - val stepOffsetX = SphereMercator.mercateLon(stepLon.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetX - val stepOffsetY = SphereMercator.mercateLat(stepLat.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetY - drawRect( - color = if (heights[step] > startHeight) Color.Red else Color.Green, - size = Size(8F, 8F), - topLeft = Offset(stepOffsetX - 4, stepOffsetY - 4) + if (pointOffsetX >= 0 && pointOffsetY >= 0 && pointOffsetX < size.width && pointOffsetY < size.height) + drawText( + textMeasurer = textMeasurer, + text = buildAnnotatedString { withStyle(SpanStyle(color = Color.White)) { + append("${startHeight}m") + } }, + topLeft = Offset(pointOffsetX, pointOffsetY - 32) ) + if (invokeHeightCalc) { + 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() + val array: Array> = Array(valuesCount) { step -> + val interCoef = 1F - step.toFloat() / valuesCount + Pair(lon - lonDiff * interCoef, lat - latDiff * interCoef) + } + val heightPair = KhmParser.getHeightsMul(ctx, array) + val heights = heightPair.first + val coords = heightPair.second.reversed() + for (step in 0 until coords.size) { + val stepLat = coords[step].second + val stepLon = coords[step].first + val stepOffsetX = SphereMercator.mercateLon(stepLon.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetX + val stepOffsetY = SphereMercator.mercateLat(stepLat.toDouble(), level, -TILE_SIZE.toDouble()).toFloat() - offsetY + if (stepOffsetX >= 0 && stepOffsetY >= 0 && stepOffsetX < size.width && stepOffsetY < size.height) + drawRect( + color = if (heights[step] > startHeight) Color.Red else Color.Green, + size = Size(8F, 8F), + topLeft = Offset(stepOffsetX - 4, stepOffsetY - 4) + ) + } } }