From 529a4cc3bdd15092f6ffd8603adb18fbc11f4eb9 Mon Sep 17 00:00:00 2001 From: Aeonoi <143847987+Aeonoi@users.noreply.github.com> Date: Fri, 22 Sep 2023 16:49:31 -0400 Subject: [PATCH 1/2] Added byRequest property --- Public/routes-data/fall23.gpx | 14 ++++++++++++++ Sources/App/Migrations/CreateStops.swift | 1 + Sources/App/Models/Stop.swift | 6 +++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Public/routes-data/fall23.gpx b/Public/routes-data/fall23.gpx index 8a0d4b7..9e1f973 100644 --- a/Public/routes-data/fall23.gpx +++ b/Public/routes-data/fall23.gpx @@ -13,45 +13,59 @@ Student Union + No Request Academy Hall + Request Only Tibbits Avenue + Request Only Polytechnic Residence Commons + No Request City Station + No Request Blitman Residence Commons + No Request West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only B-Lot + Request Only Stacwyck + No Request Bryckwyck + No Request RAHP-B + No Request Colonie + No Request West Route diff --git a/Sources/App/Migrations/CreateStops.swift b/Sources/App/Migrations/CreateStops.swift index b24f647..eaf62dc 100644 --- a/Sources/App/Migrations/CreateStops.swift +++ b/Sources/App/Migrations/CreateStops.swift @@ -17,6 +17,7 @@ struct CreateStops: AsyncMigration { .field("name", .string, .required) .field("coordinate", .dictionary, .required) .field("schedule", .dictionary, .required) + .field("isByRequest", .string, .required) .create() } diff --git a/Sources/App/Models/Stop.swift b/Sources/App/Models/Stop.swift index 90c28a0..d118793 100644 --- a/Sources/App/Models/Stop.swift +++ b/Sources/App/Models/Stop.swift @@ -28,6 +28,9 @@ final class Stop: Equatable, Hashable, Model, Content { @Field(key: "schedule") var schedule: MapSchedule + + @Field(key: "isByRequest") + var isByRequest: Bool init() { } @@ -37,12 +40,13 @@ final class Stop: Equatable, Hashable, Model, Content { /// - schedule: The schedule for when the stop will be active. /// - Note: This initializer fails and returns `nil` if the provided GPX waypoint doesn’t contain sufficient information to create a stop object. init?(from gpxWaypoint: any GPXWaypointProtocol, withSchedule schedule: MapSchedule) { - guard let name = gpxWaypoint.name, let coordinate = Coordinate(from: gpxWaypoint) else { + guard let name = gpxWaypoint.name, let coordinate = Coordinate(from: gpxWaypoint), let isByRequest: String? = gpxWaypoint.desc else { return nil } self.name = name self.coordinate = coordinate self.schedule = schedule + self.isByRequest = (isByRequest == "Request Only") } func hash(into hasher: inout Hasher) { From 1f072f9e0cee7540cb4cc438616bb258d87f2f8f Mon Sep 17 00:00:00 2001 From: Aeonoi <143847987+Aeonoi@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:42:17 -0400 Subject: [PATCH 2/2] Associated Stops With Routes --- Public/routes-data/fall22-event.gpx | 15 ++++++++++++ Public/routes-data/fall22.gpx | 16 +++++++++++++ Public/routes-data/spring22.gpx | 15 ++++++++++++ Public/routes-data/spring23.gpx | 16 +++++++++++++ .../routes-data/summer22-weekday-testing.gpx | 7 ++++++ Public/routes-data/summer22-weekday.gpx | 6 +++++ Public/routes-data/summer22-weekend.gpx | 7 ++++++ Sources/App/Jobs/GPXImportingJob.swift | 13 ++++++---- Sources/App/Migrations/CreateStops.swift | 3 ++- Sources/App/Models/Route.swift | 21 +++++++++++++++- Sources/App/Models/Stop.swift | 24 ++++++++++++++----- Sources/App/Utilities.swift | 2 ++ 12 files changed, 132 insertions(+), 13 deletions(-) diff --git a/Public/routes-data/fall22-event.gpx b/Public/routes-data/fall22-event.gpx index 592410d..a42aeb8 100644 --- a/Public/routes-data/fall22-event.gpx +++ b/Public/routes-data/fall22-event.gpx @@ -13,48 +13,63 @@ Student Union + No Request Academy Hall + Request Only Tibbits Avenue + Request Only Polytechnic Residence Commons + No Request City Station + No Request Blitman Residence Commons + No Request Heffner Alumni House + No Request Barton Hall + No Request B-Lot + Request Only E-Lot + No Request Stacwyck + No Request Bryckwyck + No Request RAHP-B + No Request Georgian Court + Request Only Colonie + No Request Special Event West Route diff --git a/Public/routes-data/fall22.gpx b/Public/routes-data/fall22.gpx index 6954dac..999cb2c 100644 --- a/Public/routes-data/fall22.gpx +++ b/Public/routes-data/fall22.gpx @@ -13,51 +13,67 @@ Student Union + No Request Academy Hall + Request Only Tibbits Avenue + Request Only Polytechnic Residence Commons + No Request City Station + No Request Blitman Residence Commons + No Request West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only B-Lot + Request Only E-Lot + No Request Stacwyck + No Request Bryckwyck + No Request RAHP-B + No Request Georgian Court + Request Only Colonie + No Request West Route diff --git a/Public/routes-data/spring22.gpx b/Public/routes-data/spring22.gpx index c2fb5c7..f8e6d27 100644 --- a/Public/routes-data/spring22.gpx +++ b/Public/routes-data/spring22.gpx @@ -13,48 +13,63 @@ Student Union + No Request Academy Hall + Request Only Tibbits Avenue + Request Only Polytechnic Residence Commons + No Request City Station + No Request Blitman Residence Commons + No Request West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only B-Lot + Request Only ECAV Arena + No Request Stacwyck + No Request Bryckwyck + No Request Beman Lane + No Request The Armory + No Request Route diff --git a/Public/routes-data/spring23.gpx b/Public/routes-data/spring23.gpx index a1c3306..65ee78b 100644 --- a/Public/routes-data/spring23.gpx +++ b/Public/routes-data/spring23.gpx @@ -13,51 +13,67 @@ Student Union + No Request Academy Hall + Request Only Tibbits Avenue + Request Only Polytechnic Residence Commons + No Request City Station + No Request Blitman Residence Commons + No Request West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only B-Lot + Request Only E-Lot + Request Only Stacwyck + No Request Bryckwyck + No Request RAHP-B + No Request Georgian Court + Request Only Colonie + No Request West Route diff --git a/Public/routes-data/summer22-weekday-testing.gpx b/Public/routes-data/summer22-weekday-testing.gpx index a47febc..330e532 100644 --- a/Public/routes-data/summer22-weekday-testing.gpx +++ b/Public/routes-data/summer22-weekday-testing.gpx @@ -13,24 +13,31 @@ Student Union + No Request Academy Hall + Request Only West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only East Campus Athletic Village + No Request Burdett Avenue + No Request Weekday Testing Route diff --git a/Public/routes-data/summer22-weekday.gpx b/Public/routes-data/summer22-weekday.gpx index 2ad5093..cd406a7 100644 --- a/Public/routes-data/summer22-weekday.gpx +++ b/Public/routes-data/summer22-weekday.gpx @@ -13,21 +13,27 @@ Student Union + No Request Academy Hall + Request Only West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only Burdett Avenue + No Request Weekday Route diff --git a/Public/routes-data/summer22-weekend.gpx b/Public/routes-data/summer22-weekend.gpx index b1ec5a9..3cb186a 100644 --- a/Public/routes-data/summer22-weekend.gpx +++ b/Public/routes-data/summer22-weekend.gpx @@ -13,24 +13,31 @@ Student Union + No Request Academy Hall + Request Only Blitman Residence Commons + No Request West Hall + No Request ’87 Gymnasium + No Request Barton Hall + Request Only Burdett Avenue + No Request Weekend Route diff --git a/Sources/App/Jobs/GPXImportingJob.swift b/Sources/App/Jobs/GPXImportingJob.swift index 67b2353..5f312cb 100644 --- a/Sources/App/Jobs/GPXImportingJob.swift +++ b/Sources/App/Jobs/GPXImportingJob.swift @@ -55,18 +55,21 @@ struct GPXImportingJob: AsyncScheduledJob { errorPrint("Couldn’t parse GPX file “\(routesFileURL.lastPathComponent)”") continue } + var allRoutes: Set = [] for gpxRoute in gpx.routes { do { - try await Route(from: gpxRoute, schedule: schedule) + let route = Route(from: gpxRoute, schedule: schedule) + allRoutes.insert(route) + try await route .save(on: context.application.db(.sqlite)) - for gpxWaypoint in gpx.waypoints { - try await Stop(from: gpxWaypoint, withSchedule: schedule)! - .save(on: context.application.db(.sqlite)) - } } catch { errorPrint("Couldn’t import GPX route from file “\(routesFileURL.lastPathComponent)”: \(error)") } } + for gpxWaypoint in gpx.waypoints { + try await Stop(from: gpxWaypoint, withSchedule: schedule, selectingRoutesFrom: allRoutes)! + .save(on: context.application.db(.sqlite)) + } } } diff --git a/Sources/App/Migrations/CreateStops.swift b/Sources/App/Migrations/CreateStops.swift index eaf62dc..0f4d2a3 100644 --- a/Sources/App/Migrations/CreateStops.swift +++ b/Sources/App/Migrations/CreateStops.swift @@ -17,7 +17,8 @@ struct CreateStops: AsyncMigration { .field("name", .string, .required) .field("coordinate", .dictionary, .required) .field("schedule", .dictionary, .required) - .field("isByRequest", .string, .required) + .field("is_by_request", .string, .required) + .field("route_id", .array(of: .uuid), .required) .create() } diff --git a/Sources/App/Models/Route.swift b/Sources/App/Models/Route.swift index 2f79dee..33fafc2 100644 --- a/Sources/App/Models/Route.swift +++ b/Sources/App/Models/Route.swift @@ -14,7 +14,7 @@ import Vapor /// A representation of a shuttle route. /// /// A route is represented as a sequence of geospatial coordinates. -final class Route: Model, Content, Collection { +final class Route: Equatable, Hashable, Model, Content, Collection { static let schema = "routes" @@ -118,6 +118,14 @@ final class Route: Model, Content, Collection { return self.coordinates[index] } + func hash(into hasher: inout Hasher) { + hasher.combine(self.id) + } + + static func == (lhs: Route, rhs: Route) -> Bool { + return lhs.id == rhs.id && lhs.name == rhs.name + } + func index(after oldIndex: Int) -> Int { return oldIndex + 1 } @@ -135,5 +143,16 @@ final class Route: Model, Content, Collection { } return distance < Constants.isOnRouteThreshold } + + func checkStopIsOnRoute(Coordinate: Coordinate) -> Bool { + let distance = LineString(self.coordinates) + .closestCoordinate(to: Coordinate)? + .coordinate + .distance(to: Coordinate) + guard let distance else { + return false + } + return distance < Constants.isStopOnRouteThreshold + } } diff --git a/Sources/App/Models/Stop.swift b/Sources/App/Models/Stop.swift index d118793..13329cb 100644 --- a/Sources/App/Models/Stop.swift +++ b/Sources/App/Models/Stop.swift @@ -29,26 +29,39 @@ final class Stop: Equatable, Hashable, Model, Content { @Field(key: "schedule") var schedule: MapSchedule - @Field(key: "isByRequest") + @Field(key: "is_by_request") var isByRequest: Bool - + + @Field(key: "route_id") + var routeID: Set + init() { } /// Creates a stop object from a GPX waypoint. /// - Parameters: /// - gpxWaypoint: The GPX waypoint from which to create a stop object. /// - schedule: The schedule for when the stop will be active. + /// - route: The set of routes to associate each stop object with a routeID /// - Note: This initializer fails and returns `nil` if the provided GPX waypoint doesn’t contain sufficient information to create a stop object. - init?(from gpxWaypoint: any GPXWaypointProtocol, withSchedule schedule: MapSchedule) { - guard let name = gpxWaypoint.name, let coordinate = Coordinate(from: gpxWaypoint), let isByRequest: String? = gpxWaypoint.desc else { + init?(from gpxWaypoint: any GPXWaypointProtocol, withSchedule schedule: MapSchedule, selectingRoutesFrom allRoutes: Set) { + guard let name = gpxWaypoint.name, let coordinate = Coordinate(from: gpxWaypoint), let isByRequest = gpxWaypoint.desc + else { return nil } self.name = name self.coordinate = coordinate self.schedule = schedule self.isByRequest = (isByRequest == "Request Only") + self.routeID = [] + for route in allRoutes { + if let id = route.id{ + if (route.checkStopIsOnRoute(Coordinate: self.coordinate)) { + self.routeID.insert(id) + } + } + } } - + func hash(into hasher: inout Hasher) { hasher.combine(self.name) // Hashing the ID could potentially violate Hashable’s invariants when the ID is determined by the database, so we hash the name instead. This means that stop names must be globally unique, which doesn’t seem to be too far-fetched as an assumption/requirement. } @@ -56,5 +69,4 @@ final class Stop: Equatable, Hashable, Model, Content { static func == (lhs: Stop, rhs: Stop) -> Bool { return lhs.name == rhs.name && lhs.coordinate == rhs.coordinate } - } diff --git a/Sources/App/Utilities.swift b/Sources/App/Utilities.swift index d89428f..e248ac8 100644 --- a/Sources/App/Utilities.swift +++ b/Sources/App/Utilities.swift @@ -121,6 +121,8 @@ enum Constants { /// The maximum perpendicular distance, in meters, away from a route at which a coordinate is considered to be “on” that route. static let isOnRouteThreshold: Double = 5 + static let isStopOnRouteThreshold: Double = 20 + static let apnsTopic = "com.gerzer.shuttletracker" }