7 Commits
8 changed files with 186 additions and 44 deletions
+1
View File
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:label="oeffimaster" android:label="oeffimaster"
android:name="${applicationName}" android:name="${applicationName}"
+64
View File
@@ -0,0 +1,64 @@
erDiagram
journey||--|{routesegments: "consists of"
routesegments||--||segment: "sorts"
segment||--|{departure: "consists of"
departure}|--|{station: "originates from"
departure}|--||vehicle: uses
vehicle||..|{line: "shared data"
segment}|--||vehicle: uses
stationlines}|..||station: "general connections"
stationlines}|..||line: groups
journey {
int id PK
string startstation
string endstation
}
routesegments {
int id PK
int journey FK
int segment FK
int segmentorder
}
segment {
int id PK
string ref
int startid FK
int endid FK
string vehicleid FK
}
departure {
int id PK
string ref
string direction
string vehicle FK
string vehiclename
string station FK
string stationname
string arrivaltime
string departuretime
}
station {
string id PK
string name
}
stationlines {
int id PK
string stationid FK
string lineid FK
}
vehicle {
string vehicleid PK
string name
string line FK
}
line {
string lineid PK
string name
string mode
string destination
string direction
string fgcolor
string bgcolor
}
+8
View File
@@ -0,0 +1,8 @@
flowchart TD
Start-->stat[Search Station]-->selstat[Select station]
selstat-->seldep["`Select departure:
vehicle, direction & time`"]
seldep-->selarr["`Select arrival station:
when & where to exit`"] --> sw{switch?} --yes-->seldep
sw--"no"-->fin([journey details])
+19
View File
@@ -0,0 +1,19 @@
sequenceDiagram
participant api@{ "type": "entity"} as public transport API
participant ah as api_handler
participant ui as UI
participant th as transport_helper
participant db@{ "type":"database"} as database
ui-->>ah: find Station
ah-->>api: GET request w/ stationname
api-->>ah: return json
ah-->>th: provide json Map
th-->>db: store object
db-->>th: return id
th-->>ah: return object
ah-->>ui: display data
+10 -10
View File
@@ -1,10 +1,10 @@
- running mobile app - [x] running mobile app
- users should be able to - [ ] users should be able to
- plan public transport in the VBB transit network - [~] plan public transport in the VBB transit network
- create and customise routes quickly and on-the-fly - [~] create and customise routes quickly and on-the-fly
- edit routes from anywhere, where respective route information can be seen - [ ] edit routes from anywhere, where respective route information can be seen
- compare routes easily, especially between different endpoints - [ ] compare routes easily, especially between different endpoints
- the app should offer up the information most relevant to current route planning - [~] the app should offer up the information most relevant to current route planning
- between route steps, there should be no more than 3 taps required, optimally even just one(1), scrolling excluded - [x] between route steps, there should be no more than 3 taps required, optimally even just one(1), scrolling excluded
- e.g. only lines that connect to a station should be shown - [x] e.g. only lines that connect to a station should be shown
- stations that a line goes through should be available as next node/ step for planning - [x] stations that a line goes through should be available as next node/ step for planning
+12 -9
View File
@@ -6,19 +6,19 @@ import 'dart:convert';
import 'transport_helper.dart'; import 'transport_helper.dart';
String timeOf(DateTime fullDT) {
// doing it this way so we always get two digits on the hour
String full = fullDT.toString();
List<String> splits = full.split(" ")[1].split(":");
String time = '${splits[0]}:${splits[1]}';
return time;
}
class ApiHandler { class ApiHandler {
// TODO: Split next line for better Error Handling // TODO: Split next line for better Error Handling
static final String baseUrl = dotenv.get("VBB_API_URL"); static final String baseUrl = dotenv.get("VBB_API_URL");
static final String accessID = dotenv.get("VBB_API_KEY"); 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<Map<String, dynamic>> findStations(String locationName) async {
static Future<List<Station>> findStations( static Future<List<Station>> findStations(
String locationName, { String locationName, {
@@ -79,6 +79,7 @@ class ApiHandler {
static Future<List<Departure>> departures( static Future<List<Departure>> departures(
String stationId, { String stationId, {
int? maxDepartures = -1, int? maxDepartures = -1,
DateTime? time,
int? duration = 0, int? duration = 0,
}) async { }) async {
String urlPath = '/api/fahrinfo/latest/departureBoard'; String urlPath = '/api/fahrinfo/latest/departureBoard';
@@ -87,7 +88,9 @@ class ApiHandler {
Uri url = Uri.https(baseUrl, urlPath, { Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID, 'accessId': accessID,
'id': stationString, 'id': stationString,
////'time': /current time //time from which departures will be shown 'time': timeOf(
time ?? DateTime.now(),
), //time from which departures will be shown
'duration': duration.toString(), //interval time in minutes 'duration': duration.toString(), //interval time in minutes
'maxJourneys': maxDepartures.toString(), 'maxJourneys': maxDepartures.toString(),
//'type': 'S', // Station/Stops only //'type': 'S', // Station/Stops only
+66 -17
View File
@@ -273,9 +273,11 @@ class StationList extends StatelessWidget {
class ChooseDeparturePage extends StatefulWidget { class ChooseDeparturePage extends StatefulWidget {
final Station startStation; final Station startStation;
final int? journeyId; final int? journeyId;
final Departure? arrival;
const ChooseDeparturePage({ const ChooseDeparturePage({
super.key, super.key,
this.journeyId, this.journeyId,
this.arrival,
required this.startStation, required this.startStation,
}); });
@@ -288,7 +290,10 @@ class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
Journey? singleJourney; Journey? singleJourney;
void loadDepartures(Station startStation) async { void loadDepartures(Station startStation) async {
departureList = await ApiHandler.departures(startStation.id); departureList = await ApiHandler.departures(
startStation.id,
time: widget.arrival?.arrivalTime ?? DateTime.now(),
);
setState(() {}); setState(() {});
} }
@@ -330,16 +335,25 @@ class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
), ),
), ),
), ),
ElevatedButton( // ElevatedButton(
// onPressed: () {
// if (widget.journeyId != null) {
// loadJourney(widget.journeyId as int);
// }
// },
// child: const Text('Finish Route'),
// ),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () {
if (widget.journeyId != null) { if (widget.journeyId != null) {
loadJourney(widget.journeyId as int); loadJourney(widget.journeyId as int);
} }
}, },
child: const Text('Finish Route'), tooltip: 'Finish route',
), child: const Icon(Icons.check),
],
),
), ),
); );
} }
@@ -470,6 +484,7 @@ class _ChooseArrivalPageState extends State<ChooseArrivalPage> {
builder: (context) => ChooseDeparturePage( builder: (context) => ChooseDeparturePage(
startStation: startStation, startStation: startStation,
journeyId: journeyId, journeyId: journeyId,
arrival: arrival,
), ),
), ),
); );
@@ -528,8 +543,32 @@ class _JourneyDetailPageState extends State<JourneyDetailPage> {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Connection Details \n$duration')), appBar: AppBar(title: Text('Connection Details \n$duration')),
body: Center( body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: SizedBox(
height: 400,
child: SegmentList(journey: widget.singleJourney), child: SegmentList(journey: widget.singleJourney),
), ),
),
Container(
margin: const EdgeInsets.all(20.0),
child: ElevatedButton(
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(
builder: (context) => JourneyOverviewPage(),
),
);
},
child: const Text('View all Journeys'),
),
),
],
),
),
); );
} }
} }
@@ -596,6 +635,16 @@ class _JourneyOverviewPageState extends State<JourneyOverviewPage> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container(
padding: EdgeInsets.only(left: 30, top: 50),
width: 500.0,
height: 100.0,
child: FittedBox(
fit: BoxFit.contain,
alignment: Alignment.centerLeft,
child: Text("Your Journeys"),
),
),
FutureBuilder( FutureBuilder(
future: loadJourneys(), future: loadJourneys(),
builder: (context, snapshot) { builder: (context, snapshot) {
@@ -648,17 +697,17 @@ class _JourneyOverviewPageState extends State<JourneyOverviewPage> {
); );
}, },
), ),
ElevatedButton( // ElevatedButton(
onPressed: () { // onPressed: () {
Navigator.push( // Navigator.push(
context, // context,
MaterialPageRoute( // MaterialPageRoute(
builder: (context) => ChooseStationPage(), // builder: (context) => ChooseStationPage(),
), // ),
); // );
}, // },
child: const Text('Start a route'), // child: const Text('Start a route'),
), // ),
], ],
), ),
), ),
+1 -3
View File
@@ -187,9 +187,7 @@ class Journey {
database.execute("""CREATE TABLE IF NOT EXISTS '$tableName'( database.execute("""CREATE TABLE IF NOT EXISTS '$tableName'(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
startstation TEXT, startstation TEXT,
endstation TEXT, endstation TEXT
FOREIGN KEY (startstation) REFERENCES station(name)
FOREIGN KEY (endstation) REFERENCES station(name)
); );
"""); """);
} }