Sergey Akhapkin
03/23/2020, 12:07 AMobject LocationTable: IntIdTable("LOCATION") {
...
val latitude = double("latitude")
val longitude = double("longitude")
private val doubleT by lazyOf(DoubleColumnType())
fun Expression<Double>.limitToOne() = CustomFunction<Double>("least", doubleT, this, 1.0.lit())
fun Expression<Double>.radians() = CustomFunction<Double>("radians", doubleT, this)
fun Expression<Double>.sin() = CustomFunction<Double>("sin", doubleT, this)
fun Expression<Double>.cos() = CustomFunction<Double>("cos", doubleT, this)
fun Expression<Double>.acos() = CustomFunction<Double>("acos", doubleT, this)
fun Double.lit() = LiteralOp(doubleT, this)
infix fun Expression<Double>.times(v: Expression<Double>) = TimesOp(this, v, doubleT)
infix fun Expression<Double>.times(v: Double) = TimesOp(this, v.lit(), doubleT)
infix fun Expression<Double>.plus(v: Expression<Double>) = PlusOp(this, v, doubleT)
infix fun Column<Double>.minus(v: Double) = BracketOp(MinusOp(this, LiteralOp(doubleT, v), doubleT))
fun distance(coords: Coordinates) =
(latitude.radians().sin() times Math.sin(Math.toRadians(coords.latitude)) plus
latitude.radians().cos() times Math.cos(Math.toRadians(coords.latitude)) times (longitude minus coords.longitude).radians().cos()).limitToOne().acos() times Coordinates.TO_METRIC
}
where
data class Coordinates(val latitude: Double, val longitude: Double) {
companion object {
const val TO_METRIC = 1852.0 * 60.0 * 180.0 / Math.PI
}
}
and this function can be used as following:
query.andWhere { LocationTable.distance(basePoint) lessEq distanceInMeters }
DISCLAIMER:
1. I'm not sure that's 100% right (or idiomatic) way to do that with the exposed (still newcomer for the exposed)
2. I'm pretty sure that's kind of filtering 'closest/nearest' objects is very inefficient (indexes cannot be used), so let consider to use postgis extension or optimizing search in someway e.g. with bounding boxes.DownloadPizza
03/24/2020, 4:29 PM