Skip to content

Commit

Permalink
OSM: implement progress indicator for full download
Browse files Browse the repository at this point in the history
  • Loading branch information
johan12345 committed Nov 11, 2023
1 parent 2b56de9 commit 869de4c
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 46 deletions.
15 changes: 7 additions & 8 deletions app/src/main/java/net/vonforst/evmap/api/ChargepointApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,8 @@ interface ChargepointApi<out T : ReferenceData> {
* Fetches all available chargers from this API.
*
* This may take a long time and should only be used when the user explicitly wants to download all chargers.
*
* TODO: add an optional callback parameter to this function to be able to receive updates on the download progress?
* TODO: Should this also include getting the ReferenceData, instead of taking it as an argument?
* ReferenceData typically includes information that is needed to create the filter options, e.g.
* mappings between IDs and readable names (for operators etc.). So probably for OSM it makes sense
* to generate that within this function (e.g. build the list of available operators using all the
* operators found in the dataset).
*/
suspend fun fullDownload(referenceData: ReferenceData): Sequence<ChargeLocation>
suspend fun fullDownload(): FullDownloadResult<T>
}

interface StringProvider {
Expand Down Expand Up @@ -135,4 +128,10 @@ data class ChargepointList(val items: List<ChargepointListItem>, val isComplete:
companion object {
fun empty() = ChargepointList(emptyList(), true)
}
}

interface FullDownloadResult<out T : ReferenceData> {
val chargers: Sequence<ChargeLocation>
val progress: Float
val referenceData: T
}
1 change: 1 addition & 0 deletions app/src/main/java/net/vonforst/evmap/api/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import okhttp3.Response
import org.json.JSONArray
import java.io.IOException
import kotlin.coroutines.resumeWithException
import kotlin.experimental.ExperimentalTypeInference
import kotlin.math.abs

operator fun <T> JSONArray.iterator(): Iterator<T> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class GoingElectricApiWrapper(
override val supportsOnlineQueries = true
override val supportsFullDownload = false

override suspend fun fullDownload(referenceData: ReferenceData): Sequence<ChargeLocation> {
override suspend fun fullDownload(): FullDownloadResult<GEReferenceData> {
throw NotImplementedError()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class OpenChargeMapApiWrapper(
override val supportsOnlineQueries = true
override val supportsFullDownload = false

override suspend fun fullDownload(referenceData: ReferenceData): Sequence<ChargeLocation> {
override suspend fun fullDownload(): FullDownloadResult<OCMReferenceData> {
throw NotImplementedError()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ package net.vonforst.evmap.api.openstreetmap
import android.database.DatabaseUtils
import com.car2go.maps.model.LatLng
import com.car2go.maps.model.LatLngBounds
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.Moshi
import net.vonforst.evmap.BuildConfig
import net.vonforst.evmap.R
import net.vonforst.evmap.addDebugInterceptors
import net.vonforst.evmap.api.ChargepointApi
import net.vonforst.evmap.api.ChargepointList
import net.vonforst.evmap.api.FiltersSQLQuery
import net.vonforst.evmap.api.FullDownloadResult
import net.vonforst.evmap.api.StringProvider
import net.vonforst.evmap.api.mapPower
import net.vonforst.evmap.api.mapPowerInverse
Expand All @@ -32,14 +31,11 @@ import net.vonforst.evmap.model.getMultipleChoiceValue
import net.vonforst.evmap.model.getSliderValue
import net.vonforst.evmap.viewmodel.Resource
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import java.io.IOException
import java.time.Duration
import java.time.Instant

interface OpenStreetMapApi {
@GET("charging-stations-osm.json")
Expand Down Expand Up @@ -108,7 +104,7 @@ class OpenStreetMapApiWrapper(baseurl: String = "https://evmap-dev.vonforst.net"
}

override suspend fun getReferenceData(): Resource<OSMReferenceData> {
TODO("Not yet implemented")
throw NotImplementedError()
}

override fun getFilters(
Expand Down Expand Up @@ -215,20 +211,34 @@ class OpenStreetMapApiWrapper(baseurl: String = "https://evmap-dev.vonforst.net"
return true
}

override suspend fun fullDownload(referenceData: ReferenceData): Sequence<ChargeLocation> {
override suspend fun fullDownload(): FullDownloadResult<OSMReferenceData> {
val response = api.getAllChargingStations()
if (!response.isSuccessful) {
throw IOException(response.message())
} else {
val body = response.body()!!
return OSMFullDownloadResult(body)
}
}
}

data class OSMReferenceData(val test: String) : ReferenceData()

class OSMFullDownloadResult(private val body: OSMDocument) : FullDownloadResult<OSMReferenceData> {
private var downloadProgress = 0f
override val chargers: Sequence<ChargeLocation>
get() {
val time = body.timestamp
return sequence {
body.elements.forEach {
body.elements.forEachIndexed { i, it ->
yield(it.convert(time))
downloadProgress = i.toFloat() / body.count
}
}
}
}
}
override val progress: Float
get() = downloadProgress
override val referenceData: OSMReferenceData
get() = TODO("Not yet implemented")

data class OSMReferenceData(val test: String) : ReferenceData()
}
69 changes: 50 additions & 19 deletions app/src/main/java/net/vonforst/evmap/storage/ChargeLocationsDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import com.car2go.maps.model.LatLngBounds
import com.car2go.maps.util.SphericalUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.cancel
import kotlinx.coroutines.launch
import net.vonforst.evmap.api.ChargepointApi
import net.vonforst.evmap.api.ChargepointList
import net.vonforst.evmap.api.StringProvider
Expand Down Expand Up @@ -154,6 +161,8 @@ class ChargeLocationsRepository(

private val chargeLocationsDao = db.chargeLocationsDao()
private val savedRegionDao = db.savedRegionDao()
private var fullDownloadJob: Job? = null
private var fullDownloadProgress: MutableStateFlow<Float?> = MutableStateFlow(null)

fun getChargepoints(
bounds: LatLngBounds,
Expand Down Expand Up @@ -225,7 +234,16 @@ class ChargeLocationsRepository(
} else {
return liveData {
if (!savedRegionResult.await()) {
fullDownload()
val job = fullDownloadJob ?: scope.launch {
fullDownload()
}.also { fullDownloadJob = it }
val progressJob = scope.launch {
fullDownloadProgress.collect {
emit(Resource.loading(null, it))
}
}
job.join()
progressJob.cancelAndJoin()
}
emit(Resource.success(dbResult.await()))
}
Expand Down Expand Up @@ -307,7 +325,16 @@ class ChargeLocationsRepository(
} else {
return liveData {
if (!savedRegionResult.await()) {
fullDownload()
val job = fullDownloadJob ?: scope.launch {
fullDownload()
}.also { fullDownloadJob = it }
val progressJob = scope.launch {
fullDownloadProgress.collect {
emit(Resource.loading(null, it))
}
}
job.join()
progressJob.cancelAndJoin()
}
emit(Resource.success(dbResult.await()))
}
Expand Down Expand Up @@ -470,25 +497,29 @@ class ChargeLocationsRepository(
val api = api.value!!
if (!api.supportsFullDownload) return

val refData = referenceData.await()
val time = Instant.now()
val result = api.fullDownload(refData)
result.chunked(100).forEach {
chargeLocationsDao.insert(*it.toTypedArray())
}
val region = Mbr(
-180.0,
-90.0,
180.0,
90.0, 4326
).asPolygon()
savedRegionDao.insert(
SavedRegion(
region, api.id, time,
null,
true
val result = api.fullDownload()
try {
result.chargers.chunked(100).forEach {
chargeLocationsDao.insert(*it.toTypedArray())
fullDownloadProgress.value = result.progress
}
val region = Mbr(
-180.0,
-90.0,
180.0,
90.0, 4326
).asPolygon()
savedRegionDao.insert(
SavedRegion(
region, api.id, time,
null,
true
)
)
)
} finally {
fullDownloadProgress.value = null
}
}


Expand Down
15 changes: 10 additions & 5 deletions app/src/main/java/net/vonforst/evmap/viewmodel/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,24 @@ enum class Status {
* trying to write it to a Parcel if the type parameter does not implement Parcelable.
*/
@Parcelize
data class Resource<out T>(val status: Status, val data: @RawValue T?, val message: String?) :
data class Resource<out T>(
val status: Status,
val data: @RawValue T?,
val message: String?,
val progress: Float? = null
) :
Parcelable {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
return Resource(Status.SUCCESS, data, null, null)
}

fun <T> error(msg: String?, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
return Resource(Status.ERROR, data, msg, null)
}

fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
fun <T> loading(data: T?, progress: Float? = null): Resource<T> {
return Resource(Status.LOADING, data, null, progress)
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/res/layout/fragment_map.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@
android:layout_height="wrap_content"
android:layout_marginTop="-10dp"
android:layout_marginBottom="-7dp"
android:indeterminate="true"
android:indeterminate="@{ vm.chargepoints.progress == null }"
android:progress="@{ vm.chargepoints.progress != null ? Math.round(vm.chargepoints.progress * 100f) : 0 }"
android:max="100"
android:visibility="visible"
app:goneUnless="@{ vm.chargepoints.status == Status.LOADING }" />

Expand Down

0 comments on commit 869de4c

Please sign in to comment.