Files
oeffimaster/lib/api_handler.dart
T
fbachus 1c0616fa86 feat(departures): make some adaptations to supp diff departure srces
with these adaptations to the api_handler and Departure.fromJson,
the fromJson-factory now works for responses from both /departureBoard
and /tripDetail

for /tripDetail a little json-copying is used, see
ApiHandler._expandDeparturesJson
2026-07-23 21:42:13 +02:00

248 lines
7.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'dart:core';
import 'dart:convert';
import 'transport_helper.dart';
class ApiHandler {
// TODO: Split next line for better Error Handling
static final String baseUrl = dotenv.get("VBB_API_URL");
static final String accessID = dotenv.get("VBB_API_KEY");
// TODO: function calls for the following API calls
// - [x] location.name: findStations
// - [ ] location.nearbystops
// - [ ] location.details
// - [ ] departureBoard
// - [ ] multiDepartureBoard
// - [ ] journeyDetails
//static Future<Map<String, dynamic>> findStations(String locationName) async {
static Future<List<Station>> findStations(
String locationName, {
int? maxStations = 5,
}) async {
String urlPath = '/api/fahrinfo/latest/location.name';
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'input': locationName,
'maxNo': maxStations.toString(),
'type': 'S', // Station/Stops only
});
//print(url);
var jsonResponse = await http.get(
url,
headers: {'Accept': 'application/json'},
);
final httpPackageJson =
json.decode(jsonResponse.body) as Map<String, dynamic>;
//print(httpPackageJson["stopLocationOrCoordLocation"]);
List<Station> stationList = httpPackageJson["stopLocationOrCoordLocation"]
.map(
(x) => Station.fromJson(x["StopLocation"]),
)
.toList()
.cast<Station>();
return stationList;
}
static Future<List<Station>> nearbyStations(
String latitude,
String longitude, {
int? maxStations = 5,
}) async {
String urlPath = '/api/fahrinfo/latest/location.nearbystops';
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'originCoordLat': latitude,
'originCoordLong': longitude,
'maxNo': maxStations.toString(),
'type': 'S', // Station/Stops only
});
var jsonResponse = await http.get(
url,
headers: {'Accept': 'application/json'},
);
final httpPackageJson =
json.decode(jsonResponse.body) as Map<String, dynamic>;
final stationList = httpPackageJson["stopLocationOrCoordLocation"]
.map(
(x) => Station.fromJson(x["StopLocation"]),
)
.toList()
.cast<Station>();
return stationList;
}
static Future<List<Departure>> departures(
String stationId, {
int? maxDepartures = -1,
int? duration = 0,
}) async {
String urlPath = '/api/fahrinfo/latest/departureBoard';
String stationString = stationId.replaceAll(' ', '%20'); // reform for URL
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'id': stationString,
////'time': /current time //time from which departures will be shown
'duration': duration.toString(), //interval time in minutes
'maxJourneys': maxDepartures.toString(),
//'type': 'S', // Station/Stops only
});
var jsonResponse = await http.get(
url,
headers: {'Accept': 'application/json'},
);
final httpPackageJson =
json.decode(jsonResponse.body) as Map<String, dynamic>;
// try {
List<Departure> departureList = httpPackageJson["Departure"]
.map(
(x) => Departure.fromJson(x),
)
.toList()
.cast<Departure>();
return departureList;
// } catch (e) {
// // TODO: logging
// // empty response
// print('Error: $e');
// // print(
// // 'Response for Station-id "$stationString" contains no field "Departure": $httpPackageJson',
// // );
// return [];
// }
}
static Future<List<Departure>> departuresAggregate(
List<String> stationList, {
int? maxDepartures = -1,
int? duration = -1,
}) async {
String urlPath = '/api/fahrinfo/latest/multiDepartureBoard';
String stationListString = stationList
.reduce((value, element) => value + '|' + element)
.replaceAll(' ', '%20'); // reform for URL
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'id': stationListString,
////'time': /current time //time from which departures will be shown
'duration': duration.toString(), //interval time in minutes
'maxJourneys': maxDepartures.toString(),
'type': 'S', // Station/Stops only
});
var jsonResponse = await http.get(
url,
headers: {'Accept': 'application/json'},
);
final httpPackageJson =
json.decode(jsonResponse.body) as Map<String, dynamic>;
List<Departure> departureList = httpPackageJson["Departure"]
.map(
(x) => Departure.fromJson(x),
)
.toList()
.cast<Departure>();
return departureList;
}
static Future<List<Segment>> trip(
String originId,
String destinationId, {
String? via,
String? viaID,
String? date,
String? time,
int? maxChange,
int? minChangeTime,
int? maxChangeTime,
bool? arrivalTime,
// int? products, // which transport modi to use - ignore for now
// String rtMode, // wherether to use real time data
// String trainFilter,
}) async {
String urlPath = '/api/fahrinfo/latest/trip';
String startId = originId.replaceAll(' ', '%20'); // reform for URL
String endId = destinationId.replaceAll(' ', '%20'); // reform for URL
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'originId': startId,
'destId': endId,
////'time': /current time //time from which departures will be shown
'time': time.toString(), //interval time in minutes
'maxChange': maxChange.toString(),
'type': 'S', // Station/Stops only
});
var jsonResponse = await http.get(
url,
headers: {'Accept': 'application/json'},
);
final httpPackageJson =
json.decode(jsonResponse.body) as Map<String, dynamic>;
// TODO: here lurk problems - do we get a list of list of segments? can't cast it
// what do we want from this?
List<Segment> SegmentList = httpPackageJson["Trip"]
.map(
(x) => x["LegList"]["Leg"].map((y) => Segment.fromJson(y)),
)
.toList();
return SegmentList;
}
static Future<List<Departure>> tripDetail(
String id, {
String? date,
String? fromId,
int? fromIdx,
String? toId,
int? toIdx,
}) async {
String urlPath = '/api/fahrinfo/latest/journeyDetail';
Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID,
'id': id,
'fromId': fromId,
'toId': toId,
});
var jsonResponse = await http.get(
url,
headers: {'Accept': 'application/json'},
);
final httpPackageJson =
json.decode(jsonResponse.body) as Map<String, dynamic>;
final DepartureList = httpPackageJson["Stops"]["Stop"]
.map(
(x) => Departure.fromJson(
// needed because I'm too lazy to write a second mostly identical
// constructor and a wrapper
_expandDeparturesJson(httpPackageJson, x, ["ref", "Product"]),
),
)
.toList()
.cast<Departure>();
return DepartureList;
}
// workaround for the fact that json from tripDetail and DepartureBoard are
// quite different from each other in their structure... but I don't wish to
// build a second constructor >:(
// so this function duplicates topLevel Maps into a lower level to be used in
// mapping functions and such
static Map<String, dynamic> _expandDeparturesJson(
Map<String, dynamic> topLevelJson,
Map<String, dynamic> subLevelJson,
List<String> topLevelKeys,
) {
for (final String entry in topLevelKeys) {
subLevelJson[entry] = topLevelJson[entry];
}
return subLevelJson;
}
}