Bimba.git

ref: f7001336c32940112f2229735bd2b5135826b8ba

app/src/main/java/xyz/apiote/bimba/czwek/api/transitousJourney.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.api

import android.content.Context
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.api.transitous.api.RoutingApi
import xyz.apiote.bimba.czwek.api.transitous.model.PedestrianProfile
import xyz.apiote.bimba.czwek.repo.Colour
import xyz.apiote.bimba.czwek.repo.CongestionLevel
import xyz.apiote.bimba.czwek.repo.Event
import xyz.apiote.bimba.czwek.repo.Journey
import xyz.apiote.bimba.czwek.repo.JourneyParams
import xyz.apiote.bimba.czwek.repo.Leg
import xyz.apiote.bimba.czwek.repo.LineStub
import xyz.apiote.bimba.czwek.repo.LineType
import xyz.apiote.bimba.czwek.repo.OccupancyStatus
import xyz.apiote.bimba.czwek.repo.Place
import xyz.apiote.bimba.czwek.repo.Position
import xyz.apiote.bimba.czwek.repo.TimeReference
import xyz.apiote.bimba.czwek.repo.TrafficResponseException
import xyz.apiote.bimba.czwek.repo.Vehicle
import xyz.apiote.bimba.czwek.units.Metre
import xyz.apiote.bimba.czwek.units.Mps
import xyz.apiote.bimba.czwek.units.Second
import java.time.Duration
import java.time.ZoneId
import java.time.ZonedDateTime

suspend fun getJourney(
	from: Place,
	to: Place,
	params: JourneyParams,
	context: Context
): List<Journey> {
	if (!isNetworkAvailable(context)) {
		throw TrafficResponseException(0, "", Error(0, R.string.error_offline, R.drawable.error_net))
	}

	return withContext(Dispatchers.IO) {
		Log.d("Journeys", "from: ${from.planString()}")
		Log.d("Journeys", "to: ${to.planString()}")
		Log.d(
			"Journeys", "params: $params; time: ${
				ZonedDateTime.of(params.getSafeDate(), params.getSafeTime(), ZoneId.systemDefault())
					.toOffsetDateTime()
			}"
		)
		val client = OkHttpClient.Builder()
			.callTimeout(Duration.ofSeconds(60))
			.readTimeout(Duration.ofSeconds(60))
			.writeTimeout(Duration.ofSeconds(60))
			.connectTimeout(Duration.ofSeconds(60)).build()
		val response = RoutingApi(client = client).plan(
			from.planString(),
			to.planString(),
			maxTransfers = null,
			maxTravelTime = null,
			time = ZonedDateTime.of(params.getSafeDate(), params.getSafeTime(), ZoneId.systemDefault())
				.toOffsetDateTime(),
			arriveBy = params.timeReference == TimeReference.ARRIVE_BY,
			requireBikeTransport = params.bicycle,
			pedestrianProfile = if (params.wheelchairAccessible) PedestrianProfile.WHEELCHAIR else PedestrianProfile.FOOT,
		)
		Log.d("Journeys", "${response.itineraries}")
		response.itineraries.map {
			val legs: List<Leg> = it.legs.map {
				Leg(
					Event(
						it.tripId ?: "",
						null,
						Time.fromOffsetTime(it.startTime, ZoneId.systemDefault()),
						0u,
						it.realTime,
						Vehicle(
							it.tripId ?: "",
							Position(0.0, 0.0),
							0u,
							Mps(0),
							LineStub(
								it.routeShortName ?: "", // TODO is it nullable, yes e.g. for transit
								LineType.fromTransitous2(it.mode),
								Colour.fromHex(it.routeColor)
							),
							it.headsign ?: "",  // TODO is it nullable, yes e.g. for transit
							CongestionLevel.UNKNOWN,
							OccupancyStatus.UNKNOWN
						),
						boarding = 0xffu,
						alerts = emptyList(),
						exact = true,
						terminusArrival = false
					),
					Event(
						it.tripId ?: "",
						Time.fromOffsetTime(it.endTime, ZoneId.systemDefault()),
						null,
						0u,
						it.realTime,
						Vehicle(
							it.tripId ?: "",
							Position(0.0, 0.0),
							0u,
							Mps(0),
							LineStub(
								it.routeShortName ?: "", // TODO is it nullable, yes e.g. for transit
								LineType.fromTransitous2(it.mode),
								Colour.fromHex(it.routeColor)
							),
							it.headsign ?: "",  // TODO is it nullable, yes e.g. for transit
							CongestionLevel.UNKNOWN,
							OccupancyStatus.UNKNOWN
						),
						boarding = 0xffu,
						alerts = emptyList(),
						exact = true,
						terminusArrival = false
					),
					Place(it.from),
					Place(it.to),
					it.agencyName,
					it.distance?.toDouble()?.let { Metre(it) },
					Second(it.duration),
					(it.intermediateStops ?: emptyList()).map { Place(it) },
					decode(it.legGeometry.points)
					/*it.rental,
					it.steps,*/
				)
			}
			Journey(
				it.startTime.atZoneSameInstant(ZoneId.systemDefault()), it.endTime.atZoneSameInstant(
					ZoneId.systemDefault()
				), legs
			)
		}
	}
}

/*
The following piece of code © Google, Apache License, Version 2.0
from https://github.com/googlemaps/android-maps-utils/blob/main/library/src/main/java/com/google/maps/android/PolyUtil.java
with changes to decode with precision 7
*/
fun decode(encodedPath: String): MutableList<Position> {
	val len = encodedPath.length

	val path: MutableList<Position> = ArrayList<Position>()
	var index = 0
	var lat = 0
	var lng = 0

	while (index < len) {
		var result = 1
		var shift = 0
		var b: Int
		do {
			b = encodedPath[index++].code - 63 - 1
			result += b shl shift
			shift += 5
		} while (b >= 0x1f)
		lat += if ((result and 1) != 0) (result shr 1).inv() else (result shr 1)

		result = 1
		shift = 0
		do {
			b = encodedPath[index++].code - 63 - 1
			result += b shl shift
			shift += 5
		} while (b >= 0x1f)
		lng += if ((result and 1) != 0) (result shr 1).inv() else (result shr 1)

		path.add(Position(lat * 1e-7, lng * 1e-7))
	}

	return path
}