Bimba.git

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
		}
	}
}