Skip to content

Commit ab12eec

Browse files
committed
Handle async merchant queries and split bounds across antimeridian
1 parent 1eba2fa commit ab12eec

1 file changed

Lines changed: 72 additions & 19 deletions

File tree

app/src/main/kotlin/org/btcmap/map/MerchantsCache.kt

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,84 @@
11
package org.btcmap.map
22

3-
import android.util.Log
3+
import kotlinx.coroutines.CoroutineScope
4+
import kotlinx.coroutines.Dispatchers
5+
import kotlinx.coroutines.Job
6+
import kotlinx.coroutines.SupervisorJob
7+
import kotlinx.coroutines.cancel
48
import kotlinx.coroutines.flow.MutableStateFlow
5-
import kotlinx.coroutines.flow.update
9+
import kotlinx.coroutines.launch
10+
import kotlinx.coroutines.withContext
611
import org.btcmap.db.Database
712
import org.btcmap.db.table.place.Marker
8-
import org.btcmap.db.table.place.MarkerProjection
913
import org.maplibre.android.geometry.LatLngBounds
1014
import org.maplibre.android.maps.MapLibreMap
15+
import java.util.concurrent.atomic.AtomicReference
1116

1217
class MerchantsCache(
1318
private val map: MapLibreMap,
1419
private val db: Database,
1520
) : MapLibreMap.OnCameraIdleListener {
16-
private val merchants: MutableSet<Marker> =
17-
mutableSetOf<MarkerProjection>().toHashSet()
21+
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
22+
private val pendingQuery = AtomicReference<Job?>(null)
23+
private val seenIds: MutableSet<Long> = mutableSetOf()
24+
private val merchants: MutableSet<Marker> = mutableSetOf()
1825
val geoJson = MutableStateFlow(merchants.toGeoJson())
1926

2027
init {
21-
Log.d("merchants_cache", "init")
2228
map.addOnCameraIdleListener(this)
2329
}
2430

2531
override fun onCameraIdle() {
26-
Log.d("merchants_cache", "camera idle")
2732
val bounds = map.projection.visibleRegion.latLngBounds
28-
Log.d("merchants_cache", "real map bounds: $bounds")
2933
val expandedBounds = expandBounds(bounds)
30-
val merchantsInBounds = db.place.selectMerchantsByBounds(
31-
expandedBounds.latitudeSouth,
32-
expandedBounds.latitudeNorth,
33-
expandedBounds.longitudeWest,
34-
expandedBounds.longitudeEast,
35-
minVerifiedAt = null,
36-
).toHashSet()
37-
Log.d("merchants_cache", "merchants in bounds: ${merchantsInBounds.size}")
38-
merchants.addAll(merchantsInBounds)
39-
geoJson.update { merchants.toGeoJson() }
34+
35+
pendingQuery.getAndSet(
36+
scope.launch {
37+
val (lonRange1, lonRange2) = expandedBounds.toLonRanges()
38+
val merchantsInBounds = withContext(Dispatchers.IO) {
39+
if (lonRange2 == null) {
40+
db.place.selectMerchantsByBounds(
41+
expandedBounds.latitudeSouth,
42+
expandedBounds.latitudeNorth,
43+
lonRange1.first,
44+
lonRange1.second,
45+
minVerifiedAt = null,
46+
).toHashSet()
47+
} else {
48+
val first = db.place.selectMerchantsByBounds(
49+
expandedBounds.latitudeSouth,
50+
expandedBounds.latitudeNorth,
51+
lonRange1.first,
52+
lonRange1.second,
53+
minVerifiedAt = null,
54+
)
55+
val second = db.place.selectMerchantsByBounds(
56+
expandedBounds.latitudeSouth,
57+
expandedBounds.latitudeNorth,
58+
lonRange2.first,
59+
lonRange2.second,
60+
minVerifiedAt = null,
61+
)
62+
(first + second).toHashSet()
63+
}
64+
}
65+
66+
val newOnes = merchantsInBounds.filter { it.id !in seenIds }
67+
if (newOnes.isEmpty()) return@launch
68+
69+
seenIds.addAll(newOnes.map { it.id })
70+
merchants.addAll(newOnes)
71+
72+
val next = withContext(Dispatchers.Default) { merchants.toGeoJson() }
73+
geoJson.value = next
74+
}
75+
)?.cancel()
4076
}
4177

4278
fun destroy() {
43-
Log.d("merchants_cache", "destroy")
4479
map.removeOnCameraIdleListener(this)
80+
pendingQuery.getAndSet(null)?.cancel()
81+
scope.cancel()
4582
}
4683

4784
private fun expandBounds(bounds: LatLngBounds, scaleFactor: Double = 2.0): LatLngBounds {
@@ -58,6 +95,22 @@ class MerchantsCache(
5895
)
5996
}
6097

98+
private fun LatLngBounds.toLonRanges(): Pair<Pair<Double, Double>, Pair<Double, Double>?> {
99+
var west = longitudeWest
100+
var east = longitudeEast
101+
if (west > 180.0) {
102+
west -= 360.0
103+
}
104+
if (east > 180.0) {
105+
east -= 360.0
106+
}
107+
return if (west <= east) {
108+
Pair(west to east, null)
109+
} else {
110+
Pair(-180.0 to east, west to 180.0)
111+
}
112+
}
113+
61114
private fun Set<Marker>.toGeoJson(): String {
62115
val sb = StringBuilder()
63116
sb.append(

0 commit comments

Comments
 (0)