Skip to content
31,486 changes: 31,027 additions & 459 deletions Data/trip.json

Large diffs are not rendered by default.

54 changes: 46 additions & 8 deletions Haltestellenmonitor1-DD.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,7 @@ struct DepartureView: View {
self.stopEvents = stopEventContainer.stopEvents ?? []
self.isLoaded = true
}
}
catch {
} catch {
if let jsonString = String(data: content, encoding: .utf8) {
print("DepartureMonitor JSON DECODE error: \(error)\n\n\(jsonString)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
//

struct Location: Hashable, Codable {
struct LocationParent: Hashable, Codable {
var name: String
}

var id: String?
// var isGlobalId: Bool?
var name: String
var name: String?
var disassembledName: String?
var type: String
var coord: [Int]?
var coord: [Coordinate]?
var properties: Stop_Property?
// var parent: Location?
var parent: LocationParent?
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,22 @@ struct Transportation: Hashable, Codable {
// var origin: Place?
var properties: T_Properties
var destination: Place



// prevent optional
enum CodingKeys: String, CodingKey {
case id, number, product, properties, destination
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

id = try container.decode(String.self, forKey: .id)
number = try container.decodeIfPresent(String.self, forKey: .number) ?? "N/A"
product = try container.decode(Product.self, forKey: .product)
properties = try container.decode(T_Properties.self, forKey: .properties)
destination = try container.decode(Place.self, forKey: .destination)
}

// Manual initializer for previews and manual creation
init(id: String,
number: String = "N/A",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct StopSequenceItem: Hashable, Codable {
// var disassembledName: String?
// var type: String
// var pointType: String?
// var coord: [Int]?
// var coord: [Coordinate]?
// var niveau: Int
var parent: Location
// var productClasses: [Int]
Expand Down Expand Up @@ -53,6 +53,20 @@ struct StopSequenceItem: Hashable, Codable {
return ""
}

func getArrivalTime() -> String {
if arrivalTimePlanned == nil && arrivalTimeEstimated == nil {
return ""
}
return getTimeStamp(date: getISO8601Date(dateString: self.arrivalTimePlanned))
}

func getRealArrivalTime() -> String {
if arrivalTimePlanned == nil && arrivalTimeEstimated == nil {
return ""
}
return getTimeStamp(date: getISO8601Date(dateString: self.arrivalTimeEstimated ?? self.arrivalTimePlanned))
}

func getScheduledTime() -> String {
let date = getISO8601Date(dateString: self.getTimetabledTime())

Expand Down Expand Up @@ -84,6 +98,29 @@ struct StopSequenceItem: Hashable, Codable {
return calendar.dateComponents([.minute], from: scheduledTimeComponents, to: realtimeComponents).minute!
}

func getTimeDifferenceArrival() -> Int {
if self.getEstimatedTime() == "" {
return 0
}
if arrivalTimePlanned == nil && arrivalTimeEstimated == nil {
return 0
}
let realtimeDate = getISO8601Date(dateString: arrivalTimeEstimated)
let scheduledTimeDate = getISO8601Date(dateString: arrivalTimePlanned)

let calendar = Calendar.current

let realtimeComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: realtimeDate)
let scheduledTimeComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: scheduledTimeDate)

return calendar.dateComponents([.minute], from: scheduledTimeComponents, to: realtimeComponents).minute!
}

func getName() -> String {
let location = parent.parent?.name ?? parent.name ?? ""
return "\(self.parent.disassembledName ?? self.parent.name ?? "")\(location == "Dresden" || location.isEmpty ? "" : ", \(location)")"
}

func getPlatform() -> String {
if self.properties.plannedPlatformName == nil && self.properties.platfromName == nil {
return ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,14 @@ struct SingleTripView: View {
do {
let stopSequenceContainer = try JSONDecoder().decode(StopSequenceContainer.self, from: content)
let stopEvents = stopSequenceContainer.leg.stopSequence ?? []

await MainActor.run {
if stopEvents.count > 0 {
self.stopSequence = stopEvents
}
self.isLoaded = true
}
}
catch {
} catch {
if let jsonString = String(data: content, encoding: .utf8) {
print("SingleTrip JSON DECODE error: \(error)\n\n\(jsonString)")

Expand Down
25 changes: 25 additions & 0 deletions Haltestellenmonitor1-DD/Shared/Models/Coordinate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Coordinate.swift
// Haltestellenmonitor1-DD
//
// Created by Tom Braune on 06.08.25.
//

struct Coordinate: Hashable, Codable {
let value: Double

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let double = try? container.decode(Double.self) {
self.value = double
} else if let string = try? container.decode(String.self), let double = Double(string) {
self.value = double
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode as Double or String convertible to Double")
}
}

init (_ value: Double) {
self.value = value
}
}
8 changes: 7 additions & 1 deletion Haltestellenmonitor1-DD/Shared/Models/MOTTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func getAccessibilityLabelStandard(motType: MOTType) -> String {
case .Down:
return "Treppe abwärts"
case .Walking:
return "Zu Fuß"
return "Fußweg"
}
}

Expand All @@ -48,6 +48,8 @@ func getAccessibilityLabelEFA(iconId: Int) -> String {
return getAccessibilityLabelStandard(motType: .CableCar)
case 10:
return getAccessibilityLabelStandard(motType: .Boat)
case 100:
return getAccessibilityLabelStandard(motType: .Walking)
default:
return getAccessibilityLabelStandard(motType: .Default)
}
Expand Down Expand Up @@ -113,6 +115,8 @@ func getIconEFA(iconId: Int) -> String {
return getIconStandard(motType: .CableCar)
case 10:
return getIconStandard(motType: .Boat)
case 100:
return getIconStandard(motType: .Walking)
default:
return getIconStandard(motType: .Default)
}
Expand Down Expand Up @@ -175,6 +179,8 @@ func getColorEFA(iconId: Int) -> Color {
return getColorStandard(motType: .CableCar)
case 10:
return getColorStandard(motType: .Boat)
case 100:
return getColorStandard(motType: .Walking)
default:
return getColorStandard(motType: .Default)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct ConnectionStop: Hashable, Codable {
return "0"
}

// TODO EFA?
let url = URL(string: "https://webapi.vvo-online.de/tr/pointfinder?limit=0&assignedstops=false&stopsOnly=false&provider=dvb&showlines=false&query=coord%3A\(Int(coordinate!.x))%3A\(Int(coordinate!.y))&format=json")!
let request = URLRequest(url: url, timeoutInterval: 20)

Expand Down
68 changes: 68 additions & 0 deletions Haltestellenmonitor1-DD/TripFinder/Models/Journey.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// Journey.swift
// Haltestellenmonitor1-DD
//
// Created by Tom Braune on 25.07.25.
//
import Foundation
struct Journey: Hashable, Codable {
struct TripFare: Hashable, Codable {
struct Ticket: Hashable, Codable {
let name: String
let currency: String
let priceBrutto: Double
let priceLevel: String
let priceLevelUnit: String
let fromLeg: Int
let toLeg: Int
let person: String
}

let tickets: [Ticket]
}
let interchanges: Int
let legs: [TripLeg]
let fare: TripFare

var tripFareString: String {
return "\(fare.tickets.first!.priceBrutto) \(fare.tickets.first!.currency)"
}

var tripFareDetails: String {
return "\(fare.tickets.first!.name), \(fare.tickets.first!.priceLevelUnit) \(fare.tickets.first!.priceLevel)"
}

var tripFareIsForFullTrip: Bool {
return (fare.tickets.first!.fromLeg == 0 && fare.tickets.first!.toLeg == legs.count - 1)
}

/// Duration in Minutes
var duration: Int {
var totalDuration: Int = 0
for leg in legs {
totalDuration += leg.duration
}
return totalDuration / 60
}

func getStartTimeString() -> String {
if legs.isEmpty {
return ""
}

let date = getISO8601Date(dateString: legs.first!.origin.departureTimeEstimated ?? legs.first!.origin.departureTimePlanned)

return getTimeStamp(date: date)
}

func getEndTimeString() -> String {
if legs.isEmpty {
return ""
}

// Include Possible Walking Distances etc
let date = legs.last!.getEndTimeWithInterchange() ?? getISO8601Date(dateString: legs.last!.destination.arrivalTimeEstimated ?? legs.last!.destination.arrivalTimePlanned)

return getTimeStamp(date: date)
}
}
3 changes: 1 addition & 2 deletions Haltestellenmonitor1-DD/TripFinder/Models/Trip.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
import Foundation

struct Trip: Hashable, Codable {
var SessionId: String
var Routes: [Route]
var journeys: [Journey]
}
Loading