Deducing height on the move
This commit is contained in:
parent
49cd9c6154
commit
af7429fad7
3 changed files with 57 additions and 21 deletions
2
.idea/deploymentTargetSelector.xml
generated
2
.idea/deploymentTargetSelector.xml
generated
|
|
@ -4,7 +4,7 @@
|
||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-08-26T11:06:03.805342749Z">
|
<DropdownSelection timestamp="2025-09-22T09:08:24.101225364Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=/home/secondbeam/.config/.android/avd/Virtual_Pixel_3.avd" />
|
<DeviceId pluginId="LocalEmulator" identifier="path=/home/secondbeam/.config/.android/avd/Virtual_Pixel_3.avd" />
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
private data class HeightInfo(
|
private data class HeightInfo(
|
||||||
val minLon: Float,
|
val minLon: Float,
|
||||||
|
|
@ -16,46 +18,63 @@ private data class HeightInfo(
|
||||||
|
|
||||||
class KhmParser {
|
class KhmParser {
|
||||||
companion object {
|
companion object {
|
||||||
|
private var header: HeightInfo? = null
|
||||||
const val HEIGHT_FILE: String = "height.khm"
|
const val HEIGHT_FILE: String = "height.khm"
|
||||||
fun load(filePath: String, ctx: Context) {
|
fun load(filePath: String, ctx: Context) {
|
||||||
if (ctx.getFileStreamPath(HEIGHT_FILE).exists()) {
|
if (ctx.getFileStreamPath(HEIGHT_FILE).exists()) {
|
||||||
Log.i(TAG, "height.khm already exists")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val inp = FileInputStream(filePath)
|
val inp = FileInputStream(filePath)
|
||||||
ctx.openFileOutput(HEIGHT_FILE, Context.MODE_PRIVATE).use {
|
ctx.openFileOutput(HEIGHT_FILE, Context.MODE_PRIVATE).use {
|
||||||
it.write(inp.readBytes())
|
it.write(inp.readBytes())
|
||||||
Log.i(TAG, "Copied %s to height.khm".format(filePath))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {
|
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {
|
||||||
Log.i(TAG, "Offset for (%d, %d) = %d".format(x, y, y * header.height + x))
|
|
||||||
return (x * header.width + y) * 2
|
return (x * header.width + y) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readHeader(dis: DataInputStream): HeightInfo {
|
private fun readHeader(dis: DataInputStream): HeightInfo {
|
||||||
return with(dis) {
|
if (header == null) {
|
||||||
val minLon = readFloat()
|
header = with(dis) {
|
||||||
val minLat = readFloat()
|
val minLon = readFloat()
|
||||||
val lonPerValue = readFloat()
|
val minLat = readFloat()
|
||||||
val latPerValue = readFloat()
|
val lonPerValue = readFloat()
|
||||||
val width = readInt()
|
val latPerValue = readFloat()
|
||||||
val height = readInt()
|
val width = readInt()
|
||||||
Log.i(TAG, "Header: %.6f, %.6f, %.6f, %.6f, %d, %d".format(minLon, minLat, lonPerValue, latPerValue, width, height))
|
val height = readInt()
|
||||||
HeightInfo( minLon, minLat, lonPerValue, latPerValue, width, height )
|
HeightInfo(minLon, minLat, lonPerValue, latPerValue, width, height)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dis.skipBytes(4 * 6)
|
||||||
}
|
}
|
||||||
|
return header!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inBounds(header: HeightInfo, lon: Float, lat: Float): Boolean {
|
||||||
|
val maxLon = header.minLon + (header.lonPerValue * header.width)
|
||||||
|
val maxLat = header.minLat + (header.latPerValue * header.height)
|
||||||
|
val miLon = min(header.minLon, maxLon)
|
||||||
|
val maLon = max(header.minLon, maxLon)
|
||||||
|
val miLat = min(header.minLat, maxLat)
|
||||||
|
val maLat = max(header.minLat, maxLat)
|
||||||
|
return lon > miLon && lat > miLat && lon < maLon && lat < maLat
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getHeight(lon: Float, lat: Float, ctx: Context): UShort {
|
fun getHeight(lon: Float, lat: Float, ctx: Context): UShort {
|
||||||
val dis = DataInputStream(ctx.openFileInput(HEIGHT_FILE))
|
val dis = DataInputStream(ctx.openFileInput(HEIGHT_FILE))
|
||||||
val header = readHeader(dis)
|
dis.use {
|
||||||
val x = ((lon - header.minLon) / header.lonPerValue).toInt()
|
val header = readHeader(dis)
|
||||||
val y = ((lat - header.minLat) / header.latPerValue).toInt()
|
if (!inBounds(header, lon, lat))
|
||||||
val offset = getOffset(header, x, y)
|
return 0u
|
||||||
dis.skipBytes(offset)
|
val x = ((lon - header.minLon) / header.lonPerValue).toInt()
|
||||||
return dis.readUnsignedShort().toUShort()
|
val y = ((lat - header.minLat) / header.latPerValue).toInt()
|
||||||
|
val offset = getOffset(header, x, y)
|
||||||
|
dis.skipBytes(offset)
|
||||||
|
val height = dis.readUnsignedShort().toUShort()
|
||||||
|
return height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ import androidx.compose.ui.graphics.Path
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.ParagraphStyle
|
import androidx.compose.ui.text.ParagraphStyle
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
|
@ -33,6 +34,7 @@ fun MapCanvas(
|
||||||
tileContainer: TileContainer,
|
tileContainer: TileContainer,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val ctx = LocalContext.current
|
||||||
var offsetX by rememberSaveable { viewModel.mapOffsetX }
|
var offsetX by rememberSaveable { viewModel.mapOffsetX }
|
||||||
var offsetY by rememberSaveable { viewModel.mapOffsetY }
|
var offsetY by rememberSaveable { viewModel.mapOffsetY }
|
||||||
val scale by rememberSaveable { viewModel.scale }
|
val scale by rememberSaveable { viewModel.scale }
|
||||||
|
|
@ -170,13 +172,14 @@ fun MapCanvas(
|
||||||
topLeft = latLonOffset
|
topLeft = latLonOffset
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val lon = SphereMercator.mercateX(centerTileX, level)
|
||||||
|
val lat = SphereMercator.mercateY(centerTileY, level)
|
||||||
|
|
||||||
drawText(
|
drawText(
|
||||||
textMeasurer = textMeasurer,
|
textMeasurer = textMeasurer,
|
||||||
text = buildAnnotatedString {
|
text = buildAnnotatedString {
|
||||||
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
|
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
|
||||||
withStyle(SpanStyle(color = gridColor)) {
|
withStyle(SpanStyle(color = gridColor)) {
|
||||||
val lon = SphereMercator.mercateX(centerTileX, level)
|
|
||||||
val lat = SphereMercator.mercateY(centerTileY, level)
|
|
||||||
append("%.6f\n%.6f".format(lon, lat))
|
append("%.6f\n%.6f".format(lon, lat))
|
||||||
if (debug) {
|
if (debug) {
|
||||||
append("\n%.0f\n%.0f".format(offsetX, offsetY))
|
append("\n%.0f\n%.0f".format(offsetX, offsetY))
|
||||||
|
|
@ -187,6 +190,20 @@ fun MapCanvas(
|
||||||
size = latLonSize,
|
size = latLonSize,
|
||||||
topLeft = latLonOffset
|
topLeft = latLonOffset
|
||||||
)
|
)
|
||||||
|
|
||||||
|
drawText(
|
||||||
|
textMeasurer = textMeasurer,
|
||||||
|
text = buildAnnotatedString {
|
||||||
|
withStyle(SpanStyle(color = Color.White)) {
|
||||||
|
append(
|
||||||
|
"%dm".format(
|
||||||
|
KhmParser.getHeight(lon.toFloat(), lat.toFloat(), ctx).toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
topLeft = Offset(halvedX + crossRadius, halvedY + crossRadius)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue