add GTFS static data
This commit is contained in:
parent
077c26eec7
commit
ca2c5fe385
184
app/src/main/java/org/pupes/mhdrunpathfinder/GTFSUtils.kt
Normal file
184
app/src/main/java/org/pupes/mhdrunpathfinder/GTFSUtils.kt
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
package org.pupes.mhdrunpathfinder
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import ovh.plrapps.mapcompose.api.addMarker
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GTFSRoute(
|
||||||
|
val agency_id: String,
|
||||||
|
val is_night: Boolean,
|
||||||
|
val route_color: String,
|
||||||
|
val route_desc: String? = null,
|
||||||
|
val route_id: String,
|
||||||
|
val route_long_name: String,
|
||||||
|
val route_short_name: String,
|
||||||
|
val route_text_color: String,
|
||||||
|
val route_type: Int,
|
||||||
|
val route_url: String,
|
||||||
|
val is_regional: Boolean,
|
||||||
|
val is_substitute_transport: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type alias for array of GTFS routes
|
||||||
|
typealias GTFSRoutes = List<GTFSRoute>
|
||||||
|
|
||||||
|
var gtfsRoutes: GTFSRoutes? = null
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GTFSStopGeometry(
|
||||||
|
val coordinates: List<Double>,
|
||||||
|
val type: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GTFSStopProperties(
|
||||||
|
val location_type: Int,
|
||||||
|
val parent_station: String? = null,
|
||||||
|
val platform_code: String? = null,
|
||||||
|
val stop_id: String,
|
||||||
|
val stop_name: String,
|
||||||
|
val wheelchair_boarding: Int,
|
||||||
|
val zone_id: String? = null,
|
||||||
|
val level_id: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GTFSStop(
|
||||||
|
val geometry: GTFSStopGeometry,
|
||||||
|
val properties: GTFSStopProperties,
|
||||||
|
val type: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class GTFSStops(
|
||||||
|
val features: List<GTFSStop>,
|
||||||
|
val type: String
|
||||||
|
)
|
||||||
|
|
||||||
|
var gtfsStops: GTFSStops? = null
|
||||||
|
|
||||||
|
|
||||||
|
// OkHttpClient for making HTTP requests
|
||||||
|
val gtfshttpClient = OkHttpClient.Builder()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val GTFSMainHandler = Handler(Looper.getMainLooper())
|
||||||
|
private var GTFSTrackingThread: Thread? = null
|
||||||
|
private var GTFSIsTracking = false
|
||||||
|
|
||||||
|
private fun fetchGTFSPosition(gtfsUrl: URL,api_key: String, callee: Activity) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("${gtfsUrl.protocol}://${gtfsUrl.host}${"/"}")
|
||||||
|
.header("User-Agent", "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
gtfshttpClient.newCall(request).execute().use { response ->
|
||||||
|
var message = if (response.isSuccessful) {
|
||||||
|
response.body?.string() ?: "Empty response"
|
||||||
|
} else {
|
||||||
|
"Error: ${response.code} ${response.message}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Handle response
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Post UI update to main thread
|
||||||
|
GTFSMainHandler.post {
|
||||||
|
Toast.makeText(callee, e.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the timer with a URL:
|
||||||
|
fun startGTFSTracking(url: String, api_key: String, callee: Activity) {
|
||||||
|
if (GTFSIsTracking) return
|
||||||
|
|
||||||
|
GTFSIsTracking = true
|
||||||
|
GTFSTrackingThread = Thread {
|
||||||
|
var gtfsUrl = URL(url);
|
||||||
|
try {
|
||||||
|
if(gtfsRoutes == null) {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("${gtfsUrl.protocol}://${gtfsUrl.host}${callee.getString(R.string.routes_path)}")
|
||||||
|
.header(
|
||||||
|
"User-Agent",
|
||||||
|
"${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)"
|
||||||
|
)
|
||||||
|
.header("X-Access-Token", api_key).build();
|
||||||
|
|
||||||
|
gtfshttpClient.newCall(request).execute().use { response ->
|
||||||
|
var message = if (response.isSuccessful) {
|
||||||
|
response.body?.string() ?: "Empty response"
|
||||||
|
} else {
|
||||||
|
"Error: ${response.code} ${response.message}"
|
||||||
|
}
|
||||||
|
gtfsRoutes = json.decodeFromString<GTFSRoutes>(message)
|
||||||
|
}}
|
||||||
|
if(gtfsStops == null) {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("${gtfsUrl.protocol}://${gtfsUrl.host}${callee.getString(R.string.stops_path)}")
|
||||||
|
.header(
|
||||||
|
"User-Agent",
|
||||||
|
"${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)"
|
||||||
|
)
|
||||||
|
.header("X-Access-Token", api_key).build();
|
||||||
|
|
||||||
|
gtfshttpClient.newCall(request).execute().use { response ->
|
||||||
|
var message = if (response.isSuccessful) {
|
||||||
|
response.body?.string() ?: "Empty response"
|
||||||
|
} else {
|
||||||
|
"Error: ${response.code} ${response.message}"
|
||||||
|
}
|
||||||
|
gtfsStops = json.decodeFromString<GTFSStops>(message)
|
||||||
|
|
||||||
|
// put all stops on the map
|
||||||
|
for (stop in gtfsStops!!.features) {
|
||||||
|
GTFSMainHandler.post {
|
||||||
|
mapState.addMarker(stop.properties.stop_id, x = longitudeToXNormalized(stop.geometry.coordinates[0]), y = latitudeToYNormalized(stop.geometry.coordinates[0])) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.outline_directions_bus_24),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(17.dp),
|
||||||
|
tint = Color(0xFF0000FF)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
} catch(e: Exception) {
|
||||||
|
Toast.makeText(callee, e.message ?: "Unknown error", Toast.LENGTH_SHORT).show()
|
||||||
|
GTFSMainHandler.post { stopGTFSTracking() }
|
||||||
|
return@Thread
|
||||||
|
}
|
||||||
|
while (GTFSIsTracking) {
|
||||||
|
fetchGTFSPosition(gtfsUrl,api_key, callee)
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000) // Wait 1 second between requests
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.apply { start() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopGTFSTracking() {
|
||||||
|
GTFSIsTracking = false
|
||||||
|
GTFSTrackingThread?.interrupt()
|
||||||
|
GTFSTrackingThread = null
|
||||||
|
}
|
||||||
@ -4,7 +4,6 @@ import android.app.Activity
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.ui.semantics.getOrNull
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -22,12 +21,9 @@ data class HaukResponse(
|
|||||||
val salt: String? = null
|
val salt: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
private val json = Json {
|
|
||||||
coerceInputValues = true // Coerce nulls to default values if applicable
|
|
||||||
ignoreUnknownKeys = true // Ignore fields in JSON not present in the data class
|
|
||||||
}
|
|
||||||
// OkHttpClient for making HTTP requests
|
// OkHttpClient for making HTTP requests
|
||||||
val httpClient = OkHttpClient.Builder()
|
val haukhttpClient = OkHttpClient.Builder()
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val HaukMainHandler = Handler(Looper.getMainLooper())
|
private val HaukMainHandler = Handler(Looper.getMainLooper())
|
||||||
@ -44,7 +40,7 @@ private fun fetchHaukPosition(haukUrl: String, callee: Activity) {
|
|||||||
.header("User-Agent", "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)")
|
.header("User-Agent", "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (Android)")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
httpClient.newCall(request).execute().use { response ->
|
haukhttpClient.newCall(request).execute().use { response ->
|
||||||
var message = if (response.isSuccessful) {
|
var message = if (response.isSuccessful) {
|
||||||
response.body?.string() ?: "Empty response"
|
response.body?.string() ?: "Empty response"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
package org.pupes.mhdrunpathfinder
|
package org.pupes.mhdrunpathfinder
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.ln
|
import kotlin.math.ln
|
||||||
import kotlin.math.tan
|
import kotlin.math.tan
|
||||||
|
|
||||||
|
val json = Json {
|
||||||
|
coerceInputValues = true // Coerce nulls to default values if applicable
|
||||||
|
ignoreUnknownKeys = true // Ignore fields in JSON not present in the data class
|
||||||
|
}
|
||||||
fun longitudeToXNormalized(longitude: Double): Double {
|
fun longitudeToXNormalized(longitude: Double): Double {
|
||||||
return (longitude + 180.0) / 360.0
|
return (longitude + 180.0) / 360.0
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,7 +99,7 @@ fun MainScreen(callee: Activity) {
|
|||||||
|
|
||||||
if (golemioAPIkey.isNotEmpty()) {
|
if (golemioAPIkey.isNotEmpty()) {
|
||||||
golemioState = true
|
golemioState = true
|
||||||
/*TODO: START MHD TRACKING*/
|
startGTFSTracking(callee.getString(R.string.golemio_base_url), golemioAPIkey, callee);
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
@ -142,7 +142,7 @@ fun MainScreen(callee: Activity) {
|
|||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putString("golemioAPIkey", golemioAPIkey)
|
putString("golemioAPIkey", golemioAPIkey)
|
||||||
}
|
}
|
||||||
/*TODO: START MHD TRACKING*/
|
startGTFSTracking(callee.getString(R.string.golemio_base_url), golemioAPIkey, callee);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
golemioState = golemioState,
|
golemioState = golemioState,
|
||||||
@ -153,10 +153,10 @@ fun MainScreen(callee: Activity) {
|
|||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putString("golemioAPIkey", golemioAPIkey)
|
putString("golemioAPIkey", golemioAPIkey)
|
||||||
}
|
}
|
||||||
/*TODO: START MHD TRACKING*/
|
startGTFSTracking(callee.getString(R.string.golemio_base_url), golemioAPIkey, callee);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/*TODO: STOP MHD TRACKING*/
|
stopGTFSTracking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
package org.pupes.mhdrunpathfinder
|
|
||||||
|
|
||||||
5
app/src/main/res/drawable/outline_directions_bus_24.xml
Normal file
5
app/src/main/res/drawable/outline_directions_bus_24.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M240,840Q223,840 211.5,828.5Q200,817 200,800L200,718Q182,698 171,673.5Q160,649 160,620L160,240Q160,157 237,118.5Q314,80 480,80Q652,80 726,117Q800,154 800,240L800,620Q800,649 789,673.5Q778,698 760,718L760,800Q760,817 748.5,828.5Q737,840 720,840L680,840Q663,840 651.5,828.5Q640,817 640,800L640,760L320,760L320,800Q320,817 308.5,828.5Q297,840 280,840L240,840ZM482,200Q592,200 641.5,200Q691,200 706,200L258,200Q276,200 325.5,200Q375,200 482,200ZM640,480L320,480Q287,480 263.5,480Q240,480 240,480L240,480L720,480L720,480Q720,480 696.5,480Q673,480 640,480ZM240,400L720,400L720,280L240,280L240,400ZM340,640Q365,640 382.5,622.5Q400,605 400,580Q400,555 382.5,537.5Q365,520 340,520Q315,520 297.5,537.5Q280,555 280,580Q280,605 297.5,622.5Q315,640 340,640ZM620,640Q645,640 662.5,622.5Q680,605 680,580Q680,555 662.5,537.5Q645,520 620,520Q595,520 577.5,537.5Q560,555 560,580Q560,605 577.5,622.5Q595,640 620,640ZM258,200L706,200Q691,183 641.5,171.5Q592,160 482,160Q375,160 325.5,172.5Q276,185 258,200ZM320,680L640,680Q673,680 696.5,656.5Q720,633 720,600L720,480L240,480L240,600Q240,633 263.5,656.5Q287,680 320,680Z"/>
|
||||||
|
|
||||||
|
</vector>
|
||||||
@ -1,3 +1,6 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">MHD run Pathfinder</string>
|
<string name="app_name">MHD run Pathfinder</string>
|
||||||
|
<string name="routes_path">/v2/gtfs/routes</string>
|
||||||
|
<string name="stops_path">/v2/gtfs/stops</string>
|
||||||
|
<string name="golemio_base_url">https://api.golemio.cz</string>
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
x
Reference in New Issue
Block a user