ref: f7001336c32940112f2229735bd2b5135826b8ba
app/src/main/java/xyz/apiote/bimba/czwek/search/Query.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
// SPDX-FileCopyrightText: Adam Evyčędo // // SPDX-License-Identifier: GPL-3.0-or-later package xyz.apiote.bimba.czwek.search import android.content.Context import android.location.Location import android.os.Parcelable import android.util.Log import com.google.openlocationcode.OpenLocationCode import kotlinx.parcelize.Parcelize import xyz.apiote.bimba.czwek.repo.Position @Parcelize class Query(var raw: String, var mode: Mode, var position: Position?) : Parcelable { @Parcelize enum class Mode : Parcelable { LOCATION, POSITION, NAME, LOCATION_PLUS_CODE, UNKNOWN } constructor(raw: String) : this(raw, Mode.UNKNOWN, null) constructor(raw: String, mode: Mode) : this(raw, mode, null) constructor(mode: Mode) : this("", mode, null) { if (mode != Mode.LOCATION) { throw Exception("Cannot initialise Query from bare Mode other than LOCATION") } } constructor(position: Position) : this(position.toString(), Mode.POSITION, position) private constructor(mode: Mode, raw: String, position: Position?) : this(raw, mode, position) fun parse(context: Context) { if (mode != Mode.UNKNOWN) { return } if (OpenLocationCode.isValidCode(raw)) { val olc = OpenLocationCode(raw) if (!olc.isFull) { mode = Mode.LOCATION_PLUS_CODE } else { val area = olc.decode() mode = Mode.POSITION position = Position(area.centerLatitude, area.centerLongitude) } } else if (OpenLocationCode.isValidCode(raw.trim().split(" ").first().trim(',').trim())) { mode = Mode.POSITION geocode(context) } else if (seemsCoordinatesDegrees(raw)) { val coords = raw.split(", ", ",", " ") try { position = Position(Location.convert(coords[0]), Location.convert(coords[1])) // TODO check coordinates valid; TEST mode = Mode.POSITION } catch (e: Exception) { Log.i("Query", "while parsing degrees: $e") mode = Mode.NAME } } else if (seemsCoordinatesDegreesMinutesSeconds(raw)) { val coords = raw.replace(Regex("° ?"), ":").replace(Regex("' ?"), ":").replace(Regex("""" ?"""), "") .split(" ").map { it.replace(",", "") }.toMutableList() try { val northSouth = if (coords[0].last() == 'N') 1 else -1 val eastWest = if (coords[1].last() == 'E') 1 else -1 coords[0] = coords[0].replace(Regex("[NS]"), "") coords[1] = coords[1].replace(Regex("[EW]"), "") position = Position(Location.convert(coords[0]) * northSouth, Location.convert(coords[1]) * eastWest) // TODO check coordinates valid; TEST mode = Mode.POSITION } catch (e: Exception) { Log.i("Query", "while parsing deg min sec: $e") mode = Mode.NAME } } else { mode = Mode.NAME } } private fun seemsCoordinatesDegrees(s: String): Boolean { return Regex("""[+-]?[0-9]+(\.[0-9]+)?(,| |, )[+-]?[0-9]+(\.[0-9]+)?""").matches(s) } private fun seemsCoordinatesDegreesMinutesSeconds(s: String): Boolean { return Regex("""[0-9]+° ?[0-9]+' ?[0-9]+(\.[0-9]+)?" ?[NS] [0-9]+° ?[0-9]+' ?[0-9]+(\.[0-9]+)?" ?[EW]""").matches( s ) } private fun geocode(context: Context) { val split = raw.trim().split(" ") val code = split.first().trim(',').trim() val freePart = split.drop(1).joinToString(" ") val location = findPlace(context, freePart) if (location == null) { throw GeocodingException() } else { val area = OpenLocationCode(code).recover(location.latitude, location.longitude).decode() position = Position(area.centerLatitude, area.centerLongitude) } } override fun toString(): String { return when (mode) { Mode.UNKNOWN -> raw Mode.LOCATION -> "here" Mode.POSITION -> "%.2f, %.2f".format( position!!.latitude, position!!.longitude ) // TODO settings for position format Mode.NAME -> raw Mode.LOCATION_PLUS_CODE -> raw } } } |