/// <summary> /// Adds a new feed. /// </summary> /// <param name="feed">The feed to add.</param> /// <returns>The id of the new feed.</returns> public int AddFeed(IGTFSFeed feed) { int newId = _feeds.Count; _feeds.Add(feed); return(newId); }
/// <summary> /// Adds the given feed. /// </summary> /// <param name="feed"></param> /// <returns></returns> public int AddFeed(IGTFSFeed feed) { int newId = this.AddFeed(); feed.CopyTo(this.GetFeed(newId)); return(newId); }
/// <summary> /// Compares two feeds. /// </summary> /// <param name="actual"></param> /// <param name="expected"></param> public static void AreEqual(IGTFSFeed actual, IGTFSFeed expected) { // first compare feed info. GTFSAssert.AreEqual(actual.GetFeedInfo(), expected.GetFeedInfo()); // compare agencies. GTFSAssert.AreEqual <Agency>(actual.Agencies, expected.Agencies, (x, y) => x.Id == y.Id, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <CalendarDate>(actual.CalendarDates, expected.CalendarDates, (x, y) => x.ServiceId == y.ServiceId && x.Date == y.Date && x.ExceptionType == y.ExceptionType, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Calendar>(actual.Calendars, expected.Calendars, (x, y) => x.ToString() == y.ToString(), (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <FareAttribute>(actual.FareAttributes, expected.FareAttributes, (x, y) => x.FareId == y.FareId, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <FareRule>(actual.FareRules, expected.FareRules, (x, y) => x.ContainsId == y.ContainsId && x.DestinationId == y.DestinationId && x.FareId == y.FareId && x.OriginId == y.OriginId && x.RouteId == y.RouteId, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Frequency>(actual.Frequencies, expected.Frequencies, (x, y) => x.TripId == y.TripId && x.StartTime == y.StartTime, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Route>(actual.Routes, expected.Routes, (x, y) => x.Id == y.Id, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Shape>(actual.Shapes, expected.Shapes, (x, y) => x.Id == y.Id && x.Sequence == y.Sequence, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Stop>(actual.Stops, expected.Stops, (x, y) => x.Id == y.Id, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <StopTime>(actual.StopTimes, expected.StopTimes, (x, y) => x.TripId == y.TripId && x.StopId == y.StopId && x.StopSequence == y.StopSequence, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Transfer>(actual.Transfers, expected.Transfers, (x, y) => x.FromStopId == y.FromStopId && x.ToStopId == y.ToStopId && x.TransferType == y.TransferType, (x, y) => GTFSAssert.AreEqual(x, y)); GTFSAssert.AreEqual <Trip>(actual.Trips, expected.Trips, (x, y) => x.Id == y.Id, (x, y) => GTFSAssert.AreEqual(x, y)); }
private static void StopTest(IGTFSFeed feed) { var stops = feed.Stops.GroupBy(x => x.LocationType); foreach (var x in stops) { Console.WriteLine($"Location type {x.Key}: {x.Count()}"); } }
/// <summary> /// Merges the content of the given feed with this feed by adding or replacing entities. /// </summary> public static void Merge(this IGTFSFeed thisFeed, IGTFSFeed feed) { var feedInfo = feed.GetFeedInfo(); if (feedInfo != null) { thisFeed.SetFeedInfo(feedInfo); } foreach (var entity in feed.Agencies) { thisFeed.Agencies.AddOrReplace(entity, x => x.Id); } foreach (var entity in feed.CalendarDates) { thisFeed.CalendarDates.AddOrReplace(entity); } foreach (var entity in feed.Calendars) { thisFeed.Calendars.AddOrReplace(entity); } foreach (var entity in feed.FareAttributes) { thisFeed.FareAttributes.AddOrReplace(entity); } foreach (var entity in feed.FareRules) { thisFeed.FareRules.AddOrReplace(entity, x => x.FareId); } foreach (var entity in feed.Frequencies) { thisFeed.Frequencies.AddOrReplace(entity); } foreach (var entity in feed.Routes) { thisFeed.Routes.AddOrReplace(entity, x => x.Id); } foreach (var entity in feed.Shapes) { thisFeed.Shapes.AddOrReplace(entity); } foreach (var entity in feed.Stops) { thisFeed.Stops.AddOrReplace(entity, x => x.Id); } foreach (var entity in feed.StopTimes) { thisFeed.StopTimes.AddOrReplace(entity); } foreach (var entity in feed.Transfers) { thisFeed.Transfers.AddOrReplace(entity); } foreach (var entity in feed.Trips) { thisFeed.Trips.AddOrReplace(entity, x => x.Id); } }
/// <summary> /// Copies this feed to the given feed. /// </summary> public static void CopyTo(this IGTFSFeed thisFeed, IGTFSFeed feed) { var feedInfo = thisFeed.GetFeedInfo(); if (feedInfo != null) { feed.SetFeedInfo(feedInfo); } foreach (var entity in thisFeed.Agencies) { feed.Agencies.Add(entity); } foreach (var entity in thisFeed.CalendarDates) { feed.CalendarDates.Add(entity); } foreach (var entity in thisFeed.Calendars) { feed.Calendars.Add(entity); } foreach (var entity in thisFeed.FareAttributes) { feed.FareAttributes.Add(entity); } foreach (var entity in thisFeed.FareRules) { feed.FareRules.Add(entity); } foreach (var entity in thisFeed.Frequencies) { feed.Frequencies.Add(entity); } foreach (var entity in thisFeed.Routes) { feed.Routes.Add(entity); } foreach (var entity in thisFeed.Shapes) { feed.Shapes.Add(entity); } foreach (var entity in thisFeed.Stops) { feed.Stops.Add(entity); } foreach (var entity in thisFeed.StopTimes) { feed.StopTimes.Add(entity); } foreach (var entity in thisFeed.Transfers) { feed.Transfers.Add(entity); } foreach (var entity in thisFeed.Trips) { feed.Trips.Add(entity); } }
/// <summary> /// Copies this feed to the given feed. /// </summary> /// <param name="thisFeed"></param> /// <param name="feed"></param> public static void CopyTo(this IGTFSFeed thisFeed, IGTFSFeed feed) { var feedInfo = thisFeed.GetFeedInfo(); if(feedInfo != null) { feed.SetFeedInfo(feedInfo); } foreach(var entity in thisFeed.Agencies) { feed.Agencies.Add(entity); } foreach (var entity in thisFeed.CalendarDates) { feed.CalendarDates.Add(entity); } foreach (var entity in thisFeed.Calendars) { feed.Calendars.Add(entity); } foreach (var entity in thisFeed.FareAttributes) { feed.FareAttributes.Add(entity); } foreach (var entity in thisFeed.FareRules) { feed.FareRules.Add(entity); } foreach (var entity in thisFeed.Frequencies) { feed.Frequencies.Add(entity); } foreach (var entity in thisFeed.Routes) { feed.Routes.Add(entity); } foreach (var entity in thisFeed.Shapes) { feed.Shapes.Add(entity); } foreach (var entity in thisFeed.Stops) { feed.Stops.Add(entity); } foreach (var entity in thisFeed.StopTimes) { feed.StopTimes.Add(entity); } foreach (var entity in thisFeed.Transfers) { feed.Transfers.Add(entity); } foreach (var entity in thisFeed.Trips) { feed.Trips.Add(entity); } }
/// <summary> /// Returns all stops that statisfy the given filter. /// </summary> /// <returns></returns> public static HashSet <string> GetStopsFor(this IGTFSFeed feed, Func <Stop, bool> stopFilter) { // collect stopids. var stopIds = new HashSet <string>(); foreach (var stop in feed.Stops) { if (stopFilter.Invoke(stop)) { // stop has to be included. stopIds.Add(stop.Id); } } return(stopIds); }
private static void CalendarTest(IGTFSFeed feed) { LocalDatePattern ptn = LocalDatePattern.CreateWithInvariantCulture("ddd uuuu-MM-dd"); foreach ((Calendar Cal, IEnumerable <CalendarDate> CalDates)cal in feed.Calendars) { Console.WriteLine($"Calendar: {cal.Cal.ID}"); Console.WriteLine($"Provides service on: {DayMasks.Get(cal.Cal.Mask)}"); Console.WriteLine($"Active {ptn.Format(cal.Cal.StartDate)} through {ptn.Format(cal.Cal.EndDate)}"); Console.WriteLine("Exceptions:"); foreach (CalendarDate date in cal.CalDates) { Console.WriteLine($" {ptn.Format(date.Date)}: {date.ExceptionType}"); } Console.WriteLine(); } }
/// <summary> /// Returns all trips along the stops that statisfy the given filter. /// </summary> /// <returns></returns> public static HashSet <string> GetTripsFor(this IGTFSFeed feed, Func <Stop, bool> stopFilter) { // collect stopids. var stopIds = feed.GetStopsFor(stopFilter); // collect tripid's. var tripIds = new HashSet <string>(); foreach (var stopTime in feed.StopTimes) { if (stopIds.Contains(stopTime.StopId)) { // save the trip id's to keep. tripIds.Add(stopTime.TripId); } } return(tripIds); }
/// <summary> /// Returns all routes along the stops that statisfy the given filter. /// </summary> /// <returns></returns> public static HashSet <string> GetRoutesFor(this IGTFSFeed feed, Func <Stop, bool> stopFilter) { // collect tripid's. var tripIds = feed.GetTripsFor(stopFilter); // collect routeid's. var routeIds = new HashSet <string>(); foreach (var trip in feed.Trips) { if (tripIds.Contains(trip.Id)) { routeIds.Add(trip.RouteId); } } return(routeIds); }
/// <summary> /// Copies this feed to the given feed. /// </summary> public static void CopyTo(this IGTFSFeed thisFeed, IGTFSFeed feed) { var feedInfo = thisFeed.GetFeedInfo(); if (feedInfo != null) { feed.SetFeedInfo(feedInfo); } feed.Agencies.AddRange(thisFeed.Agencies); feed.CalendarDates.AddRange(thisFeed.CalendarDates); feed.Calendars.AddRange(thisFeed.Calendars); feed.FareAttributes.AddRange(thisFeed.FareAttributes); feed.FareRules.AddRange(thisFeed.FareRules); feed.Frequencies.AddRange(thisFeed.Frequencies); feed.Routes.AddRange(thisFeed.Routes); feed.Shapes.AddRange(thisFeed.Shapes); feed.Stops.AddRange(thisFeed.Stops); feed.StopTimes.AddRange(thisFeed.StopTimes); feed.Transfers.AddRange(thisFeed.Transfers); feed.Trips.AddRange(thisFeed.Trips); feed.Levels.AddRange(thisFeed.Levels); feed.Pathways.AddRange(thisFeed.Pathways); }
/// <summary> /// Validates a GTFS feed. /// </summary> /// <param name="feed"></param> /// <param name="messages"></param> /// <returns></returns> public static bool Validate(IGTFSFeed feed, out string messages) { // check agencies. var agencyIds = new HashSet<string>(); foreach (var agency in feed.Agencies) { if (agencyIds.Contains(agency.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate agency id found: {0}", agency.Id); return false; } agencyIds.Add(agency.Id); } // check stops. var stopIds = new HashSet<string>(); foreach(var stop in feed.Stops) { if(stopIds.Contains(stop.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate stop id found: {0}", stop.Id); return false; } stopIds.Add(stop.Id); } // check routes. var routeIds = new HashSet<string>(); foreach(var route in feed.Routes) { if (routeIds.Contains(route.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate route id found: {0}", route.Id); return false; } routeIds.Add(route.Id); if (!agencyIds.Contains(route.AgencyId)) {// oeps, unknown id. messages = string.Format("Unknown agency found in route {0}: {1}", route.Id, route.AgencyId); return false; } } // check trips for routes. var tripIds = new HashSet<string>(); foreach(var trip in feed.Trips) { if (tripIds.Contains(trip.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate trip id found: {0}", trip.Id); return false; } tripIds.Add(trip.Id); if (!routeIds.Contains(trip.RouteId)) { // oeps, unknown id. messages = string.Format("Unknown route found in trip {0}: {1}", trip.Id, trip.RouteId); return false; } } // check stop times. var stopTimesIndex = new Dictionary<string, List<StopTime>>(); var stopTimes = new HashSet<Tuple<string, uint>>(); foreach(var stopTime in feed.StopTimes) { var stopTimeId = new Tuple<string, uint>(stopTime.TripId, stopTime.StopSequence); if(stopTimes.Contains(stopTimeId)) { // oeps, duplicate id. messages = string.Format("Duplicate stop_time entry found: {0}", stopTime.TripId, stopTime.StopSequence); return false; } stopTimes.Add(stopTimeId); List<StopTime> stopTimeList; if (!stopTimesIndex.TryGetValue(stopTime.TripId, out stopTimeList)) { stopTimeList = new List<StopTime>(); stopTimesIndex.Add(stopTime.TripId, stopTimeList); } stopTimeList.Add(stopTime); if (!stopIds.Contains(stopTime.StopId)) { // oeps, unknown id. messages = string.Format("Unknown stop found in stop_time {0}: {1}", stopTime.StopId, stopTime.StopId); return false; } if (!tripIds.Contains(stopTime.TripId)) { // oeps, unknown id. messages = string.Format("Unknown trip found in stop_time {0}: {1}", stopTime.StopId, stopTime.TripId); return false; } } // check all sequences. var sequences = new HashSet<uint>(); foreach(var stopTimesPair in stopTimesIndex) { uint min = uint.MaxValue; uint max = uint.MinValue; foreach(var stopTime in stopTimesPair.Value) { if (stopTime.StopSequence < min) { min = stopTime.StopSequence; } if(stopTime.StopSequence > max) { max = stopTime.StopSequence; } sequences.Add(stopTime.StopSequence); } if(min != 0 && max != sequences.Count) { // oeps, unknown id. messages = string.Format("Missing sequence found in stop_times for trip id {0}.", stopTimesPair.Key); return false; } sequences.Clear(); } messages = string.Empty; return true; }
/// <summary> /// Validates a GTFS feed. /// </summary> /// <param name="feed"></param> /// <param name="messages"></param> /// <returns></returns> public static bool Validate(IGTFSFeed feed, out string messages) { // check agencies. var agencyIds = new HashSet <string>(); foreach (var agency in feed.Agencies) { if (agencyIds.Contains(agency.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate agency id found: {0}", agency.Id); return(false); } agencyIds.Add(agency.Id); } // check stops. var stopIds = new HashSet <string>(); foreach (var stop in feed.Stops) { if (stopIds.Contains(stop.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate stop id found: {0}", stop.Id); return(false); } stopIds.Add(stop.Id); } // check routes. var routeIds = new HashSet <string>(); foreach (var route in feed.Routes) { if (routeIds.Contains(route.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate route id found: {0}", route.Id); return(false); } routeIds.Add(route.Id); if (!agencyIds.Contains(route.AgencyId)) {// oeps, unknown id. messages = string.Format("Unknown agency found in route {0}: {1}", route.Id, route.AgencyId); return(false); } } // check trips for routes. var tripIds = new HashSet <string>(); foreach (var trip in feed.Trips) { if (tripIds.Contains(trip.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate trip id found: {0}", trip.Id); return(false); } tripIds.Add(trip.Id); if (!routeIds.Contains(trip.RouteId)) { // oeps, unknown id. messages = string.Format("Unknown route found in trip {0}: {1}", trip.Id, trip.RouteId); return(false); } } // check stop times. var stopTimesIndex = new Dictionary <string, List <StopTime> >(); var stopTimes = new HashSet <Tuple <string, uint> >(); foreach (var stopTime in feed.StopTimes) { var stopTimeId = new Tuple <string, uint>(stopTime.TripId, stopTime.StopSequence); if (stopTimes.Contains(stopTimeId)) { // oeps, duplicate id. messages = string.Format("Duplicate stop_time entry found: {0}", stopTime.TripId, stopTime.StopSequence); return(false); } stopTimes.Add(stopTimeId); List <StopTime> stopTimeList; if (!stopTimesIndex.TryGetValue(stopTime.TripId, out stopTimeList)) { stopTimeList = new List <StopTime>(); stopTimesIndex.Add(stopTime.TripId, stopTimeList); } stopTimeList.Add(stopTime); if (!stopIds.Contains(stopTime.StopId)) { // oeps, unknown id. messages = string.Format("Unknown stop found in stop_time {0}: {1}", stopTime.StopId, stopTime.StopId); return(false); } if (!tripIds.Contains(stopTime.TripId)) { // oeps, unknown id. messages = string.Format("Unknown trip found in stop_time {0}: {1}", stopTime.StopId, stopTime.TripId); return(false); } } // check all sequences. foreach (var stopTimesPair in stopTimesIndex) { uint current = 0, previous; foreach (var stopTime in stopTimesPair.Value) { previous = current; current = stopTime.StopSequence; if (previous != 0) { if (previous >= current) { messages = string.Format("Stop sequences values shall increase and be unique in stop_times file for trip id {0}.", stopTimesPair.Key); return(false); } } } } messages = string.Empty; return(true); }
/// <summary> /// Validates a GTFS feed. /// </summary> /// <param name="feed"></param> /// <returns></returns> public static bool Validate(IGTFSFeed feed) { var messages = string.Empty; return GTFSFeedValidation.Validate(feed, out messages); }
/// <summary> /// Filters the given feed and returns the result. /// </summary> /// <param name="feed"></param> /// <returns></returns> public abstract IGTFSFeed Filter(IGTFSFeed feed);
/// <summary> /// Validates a GTFS feed. /// </summary> /// <param name="feed"></param> /// <returns></returns> public static bool Validate(IGTFSFeed feed) { var messages = string.Empty; return(GTFSFeedValidation.Validate(feed, out messages)); }
/// <summary> /// Finds all stops-at-shapes for the given trip. /// </summary> /// <param name="feed">The feed to search.</param> /// <param name="tripId">The trip to search.</param> /// <param name="maxTolerance">The maximum distance.</param> /// <returns></returns> public List<StopAtShape> Find(IGTFSFeed feed, string tripId, double maxTolerance) { var minTolerance = 1; if (string.IsNullOrWhiteSpace(tripId)) { throw new ArgumentNullException("tripId"); } var trip = feed.Trips.Get(tripId); if (trip == null) { throw new Exception(string.Format("Trip with id {0} not found.", tripId)); } var stopsAtShape = new List<StopAtShape>(); var shapeId = trip.ShapeId; if(!string.IsNullOrWhiteSpace(shapeId)) { // there is an id. var shapes = feed.Shapes.Get(shapeId).ToList(); shapes.Sort((x, y) => { return x.Sequence.CompareTo(y.Sequence); }); var stops = feed.StopTimes.GetForTrip(tripId).ToList(); stops.Sort((x, y) => { return x.StopSequence.CompareTo(y.StopSequence); }); int stopIdx = 0; int lastFoundShape = -1; while (stopIdx < stops.Count) { // find a shape entry for the current stop. var stop = feed.Stops.Get(stops[stopIdx].StopId); var shapeFoundIdx = -1; var shapeDistanceTolerance = minTolerance; // when shape point < minTolerance; while (shapeDistanceTolerance < maxTolerance) { // keep searching until tolerance reaches var shapeDistance = double.MaxValue; // only search from the last found shape on. for (int shapeIdx = lastFoundShape + 1; shapeIdx < shapes.Count; shapeIdx++) { var localDistance = Extensions.DistanceInMeter(stop.Latitude, stop.Longitude, shapes[shapeIdx].Latitude, shapes[shapeIdx].Longitude); if (localDistance < shapeDistanceTolerance) { // is within tolerance. if(shapeDistance != double.MaxValue && localDistance > shapeDistance) { // another within tolerance, but higher, stop search. break; } if(localDistance < shapeDistance) { // ok, distance is better! shapeDistance = localDistance; shapeFoundIdx = shapeIdx; } } else { // distance is bigger. if(shapeDistance != double.MaxValue) { // a point was already found. break; } } } if(shapeDistance != double.MaxValue) { // a point was found. break; } shapeDistanceTolerance = shapeDistanceTolerance * 2; } if(shapeFoundIdx == -1) { // no shape-candidate was found. throw new Exception(string.Format("No shape was found for stop {0} with tolerlance of [{1}-{2}[m.", stop.ToString(), 0, maxTolerance)); } // add to result. lastFoundShape = shapeFoundIdx; stopsAtShape.Add(new StopAtShape() { ShapePointSequence = shapes[shapeFoundIdx].Sequence, StopId = stop.Id, StopOffset = 0, TripId = tripId }); stopIdx++; } } return stopsAtShape; }
/// <summary> /// Finds all stops-at-shapes for the given trip with tolerance [0-20[m. /// </summary> /// <param name="feed">The feed to search.</param> /// <param name="tripId">The trip to search.</param> /// <returns></returns> public List<StopAtShape> Find(IGTFSFeed feed, string tripId) { return this.Find(feed, tripId, 20); }
/// <summary> /// Adds a new feed. /// </summary> /// <param name="feed">The feed to add.</param> /// <returns>The id of the new feed.</returns> public int AddFeed(IGTFSFeed feed) { int newId = _feeds.Count; _feeds.Add(feed); return newId; }
/// <summary> /// Finds all stops-at-shapes for the given trip. /// </summary> /// <param name="feed">The feed to search.</param> /// <param name="tripId">The trip to search.</param> /// <param name="maxTolerance">The maximum distance.</param> /// <returns></returns> public List <StopAtShape> Find(IGTFSFeed feed, string tripId, double maxTolerance) { var minTolerance = 1; if (string.IsNullOrWhiteSpace(tripId)) { throw new ArgumentNullException("tripId"); } var trip = feed.Trips.Get(tripId); if (trip == null) { throw new Exception(string.Format("Trip with id {0} not found.", tripId)); } var stopsAtShape = new List <StopAtShape>(); var shapeId = trip.ShapeId; if (!string.IsNullOrWhiteSpace(shapeId)) { // there is an id. var shapes = feed.Shapes.Get(shapeId).ToList(); shapes.Sort((x, y) => { return(x.Sequence.CompareTo(y.Sequence)); }); var stops = feed.StopTimes.GetForTrip(tripId).ToList(); stops.Sort((x, y) => { return(x.StopSequence.CompareTo(y.StopSequence)); }); int stopIdx = 0; int lastFoundShape = -1; while (stopIdx < stops.Count) { // find a shape entry for the current stop. var stop = feed.Stops.Get(stops[stopIdx].StopId); var shapeFoundIdx = -1; var shapeDistanceTolerance = minTolerance; // when shape point < minTolerance; while (shapeDistanceTolerance < maxTolerance) { // keep searching until tolerance reaches var shapeDistance = double.MaxValue; // only search from the last found shape on. for (int shapeIdx = lastFoundShape + 1; shapeIdx < shapes.Count; shapeIdx++) { var localDistance = Extensions.DistanceInMeter(stop.Latitude, stop.Longitude, shapes[shapeIdx].Latitude, shapes[shapeIdx].Longitude); if (localDistance < shapeDistanceTolerance) { // is within tolerance. if (shapeDistance != double.MaxValue && localDistance > shapeDistance) { // another within tolerance, but higher, stop search. break; } if (localDistance < shapeDistance) { // ok, distance is better! shapeDistance = localDistance; shapeFoundIdx = shapeIdx; } } else { // distance is bigger. if (shapeDistance != double.MaxValue) { // a point was already found. break; } } } if (shapeDistance != double.MaxValue) { // a point was found. break; } shapeDistanceTolerance = shapeDistanceTolerance * 2; } if (shapeFoundIdx == -1) { // no shape-candidate was found. throw new Exception(string.Format("No shape was found for stop {0} with tolerlance of [{1}-{2}[m.", stop.ToString(), 0, maxTolerance)); } // add to result. lastFoundShape = shapeFoundIdx; stopsAtShape.Add(new StopAtShape() { ShapePointSequence = shapes[shapeFoundIdx].Sequence, StopId = stop.Id, StopOffset = 0, TripId = tripId }); stopIdx++; } } return(stopsAtShape); }
/// <summary> /// Adds the given feed. /// </summary> /// <param name="feed"></param> /// <returns></returns> public int AddFeed(IGTFSFeed feed) { int newId = this.AddFeed(); feed.CopyTo(this.GetFeed(newId)); return newId; }
/// <summary> /// Filters the given feed and returns a filtered version. /// </summary> /// <param name="feed"></param> /// <returns></returns> public override IGTFSFeed Filter(IGTFSFeed feed) { var filteredFeed = new GTFSFeed(); // filter routes. var routeIds = new HashSet<string>(); var agencyIds = new HashSet<string>(); foreach (var route in feed.Routes) { if (_routesFilter.Invoke(route)) { // keep this route. filteredFeed.Routes.Add(route); routeIds.Add(route.Id); // keep agency ids. agencyIds.Add(route.AgencyId); } } // filter trips. var serviceIds = new HashSet<string>(); var tripIds = new HashSet<string>(); var shapeIds = new HashSet<string>(); foreach (var trip in feed.Trips) { if (routeIds.Contains(trip.RouteId)) { // keep this trip, it is related to at least one stop-time. filteredFeed.Trips.Add(trip); tripIds.Add(trip.Id); // keep serviceId, routeId and shapeId. serviceIds.Add(trip.ServiceId); if (!string.IsNullOrWhiteSpace(trip.ShapeId)) { shapeIds.Add(trip.ShapeId); } } } // filter stop-times. var stopIds = new HashSet<string>(); foreach (var stopTime in feed.StopTimes) { if (tripIds.Contains(stopTime.TripId)) { // stop is included, keep this stopTime. filteredFeed.StopTimes.Add(stopTime); // save the trip id's to keep. stopIds.Add(stopTime.StopId); } } // filter stops. foreach (var stop in feed.Stops) { if (stopIds.Contains(stop.Id)) { // stop has to be included. stopIds.Add(stop.Id); filteredFeed.Stops.Add(stop); } } // filter agencies. foreach (var agency in feed.Agencies) { if (agencyIds.Contains(agency.Id)) { // keep this agency. filteredFeed.Agencies.Add(agency); } } // filter calendars. foreach (var calendar in feed.Calendars) { if (serviceIds.Contains(calendar.ServiceId)) { // keep this calendar. filteredFeed.Calendars.Add(calendar); } } // filter calendar-dates. foreach (var calendarDate in feed.CalendarDates) { if (serviceIds.Contains(calendarDate.ServiceId)) { // keep this calendarDate. filteredFeed.CalendarDates.Add(calendarDate); } } // filter fare rules. var fareIds = new HashSet<string>(); foreach (var fareRule in feed.FareRules) { if (routeIds.Contains(fareRule.RouteId)) { // keep this fare rule. filteredFeed.FareRules.Add(fareRule); // keep fare ids. fareIds.Add(fareRule.FareId); } } // filter fare attributes. foreach (var fareAttribute in feed.FareAttributes) { if (fareIds.Contains(fareAttribute.FareId)) { // keep this fare attribute. filteredFeed.FareAttributes.Add(fareAttribute); } } // filter frequencies. foreach (var frequency in feed.Frequencies) { if (tripIds.Contains(frequency.TripId)) { // keep this frequency. filteredFeed.Frequencies.Add(frequency); } } foreach (var transfer in feed.Transfers) { if (stopIds.Contains(transfer.FromStopId) && stopIds.Contains(transfer.ToStopId)) { filteredFeed.Transfers.Add(transfer); } } return filteredFeed; }
/// <summary> /// Loads data from a gtfs feed. /// </summary> public static void LoadFrom(this TransitDb db, IGTFSFeed feed) { if (db.StopsCount > 0 || db.TripsCount > 0) { // the database is not empty, cannot load this new GTFS-feed. throw new InvalidOperationException("Cannot load a GTFS-feed into a non-empty transit db."); } // load agencies. var agenciesIndex = new Dictionary <string, uint>(); for (var i = 0; i < feed.Agencies.Count; i++) { var agency = feed.Agencies.Get(i); agenciesIndex[agency.Id.ToStringEmptyWhenNull()] = db.AddAgency(agency); } // load schedules. var calendarTag = 1; var schedulesIndex = new Dictionary <string, uint>(); var calendarDates = new List <CalendarDate>(feed.CalendarDates); foreach (var calendar in feed.Calendars) { for (var day = calendar.StartDate; day <= calendar.EndDate; day = day.AddDays(1)) { if (calendar.CoversDate(day)) { calendarDates.Add(new CalendarDate() { Date = day, ExceptionType = global::GTFS.Entities.Enumerations.ExceptionType.Added, ServiceId = calendar.ServiceId, Tag = calendarTag }); } } } calendarDates.Sort((x, y) => { var c = x.ServiceId.CompareTo(y.ServiceId); if (c == 0) { return(x.Date.CompareTo(y.Date)); } return(c); }); // merge/remove dates. for (var i = 0; i < calendarDates.Count - 1; i++) { if (calendarDates[i].ServiceId == calendarDates[i + 1].ServiceId && calendarDates[i].Date == calendarDates[i + 1].Date) { if (calendarDates[i].ExceptionType == calendarDates[i + 1].ExceptionType) { calendarDates.RemoveAt(i + 1); } else if (calendarDates[i].Tag == null && calendarDates[i + 1].Tag != null) { calendarDates.RemoveAt(i + 1); } else if (calendarDates[i].Tag != null && calendarDates[i + 1].Tag == null) { calendarDates.RemoveAt(i); i--; } } } // convert to calendar objects again. var currentServiceId = string.Empty; var currentCalendars = new List <Calendar>(); var scheduleIds = new Dictionary <string, uint>(); for (var i = 0; i < calendarDates.Count; i++) { var current = calendarDates[i]; if (currentServiceId != current.ServiceId) { // start new calendars. if (currentCalendars.Count > 0) { // add previous calendars. currentCalendars[currentCalendars.Count - 1].TrimEndDate(); var newScheduleId = db.AddCalendars(currentCalendars); scheduleIds.Add(currentServiceId, newScheduleId); currentCalendars.Clear(); } // start working on the next one. currentServiceId = current.ServiceId; if (current.ExceptionType == global::GTFS.Entities.Enumerations.ExceptionType.Added) { // ok, use this as the first new date. var calendarForCurrent = current.Date.CreateCalendar( current.ServiceId); calendarForCurrent.ExpandWeek(); calendarForCurrent.TrimStartDate(); currentCalendars.Add(calendarForCurrent); } else { // not Add so don't create a week yet, go for the next one. currentServiceId = string.Empty; } } else { // add to existing calendars. var existing = currentCalendars[currentCalendars.Count - 1]; if (existing.EndDate >= current.Date) { // should be part of the last calendar. existing.Set(current.Date, current.ExceptionType == global::GTFS.Entities.Enumerations.ExceptionType.Added); } else if (current.ExceptionType == global::GTFS.Entities.Enumerations.ExceptionType.Added) { // add new calendar. var calendarForCurrent = current.Date.CreateCalendar( current.ServiceId); calendarForCurrent.ExpandWeek(); calendarForCurrent.TrimStartDate(); currentCalendars.Add(calendarForCurrent); } } } if (currentCalendars.Count > 0) { // add last calendars. currentCalendars[currentCalendars.Count - 1].TrimEndDate(); var newScheduleId = db.AddCalendars(currentCalendars); scheduleIds.Add(currentServiceId, newScheduleId); } // load routes. var routes = new Dictionary <string, global::GTFS.Entities.Route>(); for (var i = 0; i < feed.Routes.Count; i++) { var route = feed.Routes.Get(i); routes[route.Id] = route; } // load trips. var tripsIndex = new Dictionary <string, uint>(); var tripServiceIds = new Dictionary <string, string>(); for (var i = 0; i < feed.Trips.Count; i++) { var trip = feed.Trips.Get(i); tripServiceIds[trip.Id] = trip.ServiceId; var route = feed.Routes.Get(trip.RouteId); uint agencyId, scheduleId; global::GTFS.Entities.Route gtfsRoute; if (agenciesIndex.TryGetValue(route.AgencyId.ToStringEmptyWhenNull(), out agencyId) && scheduleIds.TryGetValue(trip.ServiceId, out scheduleId) && routes.TryGetValue(trip.RouteId, out gtfsRoute)) { tripsIndex[trip.Id] = db.AddTrip(trip, gtfsRoute, agencyId, scheduleId); } } // load stops. var stopsReverseIndex = new Extensions.LinkedListNode <string> [feed.Stops.Count]; var stopsIndex = new Dictionary <string, uint>(); for (var i = 0; i < feed.Stops.Count; i++) { var stop = feed.Stops.Get(i); if (string.IsNullOrWhiteSpace(stop.ParentStation)) { // only add stops that have no parent station. var stopId = db.AddStop(stop); stopsReverseIndex[stopId] = new Extensions.LinkedListNode <string>(stop.Id); stopsIndex[stop.Id] = stopId; } } for (var i = 0; i < feed.Stops.Count; i++) { var stop = feed.Stops.Get(i); if (!string.IsNullOrWhiteSpace(stop.ParentStation)) { // now add the stops that have parent stations. uint stopId; if (!stopsIndex.TryGetValue(stop.ParentStation, out stopId)) { // oeps, parent station not found. throw new Exception("A station was found with a parent station that has a parent station of it's own. Only one level of station hierarchy is supported."); } var node = stopsReverseIndex[stopId]; while (node.Next != null) { node = node.Next; } node.Next = new Extensions.LinkedListNode <string>(stop.Id); } } // sort stops. db.SortStops((i, j) => { var temp = stopsReverseIndex[i]; stopsReverseIndex[i] = stopsReverseIndex[j]; stopsReverseIndex[j] = temp; }); // re-index stops. for (uint i = 0; i < stopsReverseIndex.Length; i++) { var node = stopsReverseIndex[i]; while (node != null) { stopsIndex[node.Value] = i; node = node.Next; } } // index the shapes. var shapeIndex = new Dictionary <string, ShapePoint[]>(); if (feed.Shapes != null) { var originalShapeIndex = new Dictionary <string, List <Shape> >(); List <Shape> shape = null; foreach (var shapePoint in feed.Shapes) { if (!originalShapeIndex.TryGetValue(shapePoint.Id, out shape)) { shape = new List <Shape>(); originalShapeIndex.Add(shapePoint.Id, shape); } shape.Add(shapePoint); } foreach (var pair in originalShapeIndex) { pair.Value.Sort((x, y) => { if (x.Id == y.Id) { return(x.Sequence.CompareTo(y.Sequence)); } return(x.Id.CompareTo(y.Id)); }); var shapePoints = new ShapePoint[pair.Value.Count]; for (var i = 0; i < shapePoints.Length; i++) { float distanceTravelled = 0; if (pair.Value[i].DistanceTravelled.HasValue) { distanceTravelled = (float)pair.Value[i].DistanceTravelled.Value; } else { if (i > 0) { distanceTravelled = Coordinate.DistanceEstimateInMeter( (float)pair.Value[i].Latitude, (float)pair.Value[i].Longitude, shapePoints[i - 1].Latitude, shapePoints[i - 1].Longitude) + shapePoints[i - 1].DistanceTravelled; } } shapePoints[i] = new ShapePoint() { Latitude = (float)pair.Value[i].Latitude, Longitude = (float)pair.Value[i].Longitude, DistanceTravelled = distanceTravelled }; } shapeIndex[pair.Key] = shapePoints; } } // load connections. var stopTimes = new List <StopTime>(feed.StopTimes); stopTimes.Sort((x, y) => { var c = x.TripId.CompareTo(y.TripId); if (c == 0) { return(x.StopSequence.CompareTo(y.StopSequence)); } return(c); }); var currentTripId = string.Empty; var currentShapeId = string.Empty; uint collisionOffset = 1; for (var i = 0; i < stopTimes.Count; i++) { var stopTime = stopTimes[i]; var stopTimeServiceId = string.Empty; uint tripId; if (tripServiceIds.TryGetValue(stopTime.TripId, out stopTimeServiceId) && tripsIndex.TryGetValue(stopTime.TripId, out tripId)) { if (currentTripId != stopTime.TripId) { // start a new sequence. currentTripId = stopTime.TripId; var trip = feed.Trips.Get(currentTripId); currentShapeId = trip.ShapeId; collisionOffset = 1; } else { // the previous stop time has the same id, add them as a connection. var previousStopTime = stopTimes[i - 1]; uint stop1, stop2; if (stopsIndex.TryGetValue(previousStopTime.StopId, out stop1) && stopsIndex.TryGetValue(stopTime.StopId, out stop2)) { if (stopTime.ArrivalTime.TotalSeconds - previousStopTime.DepartureTime.TotalSeconds < collisionOffset) { // make sure arrival and departure time differ at least one second. db.AddConnection(stop1, stop2, tripId, (uint)previousStopTime.DepartureTime.TotalSeconds + (collisionOffset - 1), (uint)stopTime.ArrivalTime.TotalSeconds + collisionOffset); collisionOffset++; } else if (collisionOffset > 1) { // the previous time was offsetted, also offset this departure time. db.AddConnection(stop1, stop2, tripId, (uint)previousStopTime.DepartureTime.TotalSeconds + (collisionOffset - 1), (uint)stopTime.ArrivalTime.TotalSeconds); collisionOffset = 1; } else { // arrival and departure time differ already. db.AddConnection(stop1, stop2, tripId, (uint)previousStopTime.DepartureTime.TotalSeconds, (uint)stopTime.ArrivalTime.TotalSeconds); collisionOffset = 1; } if (previousStopTime.ShapeDistTravelled.HasValue && stopTime.ShapeDistTravelled.HasValue) { var shape = db.ShapesDb.Get(stop1, stop2); if (shape == null) { ShapePoint[] shapePoints = null; if (shapeIndex.TryGetValue(currentShapeId, out shapePoints)) { var shapeBetweenStops = ShapePoint.ExtractShape( shapePoints, (float)previousStopTime.ShapeDistTravelled.Value, (float)stopTime.ShapeDistTravelled.Value); db.ShapesDb.Add(stop1, stop2, new Graphs.Geometric.Shapes.ShapeEnumerable(shapeBetweenStops)); } } } } } } } }
/// <summary> /// Finds all stops-at-shapes for the given trip with tolerance [0-20[m. /// </summary> /// <param name="feed">The feed to search.</param> /// <param name="tripId">The trip to search.</param> /// <returns></returns> public List <StopAtShape> Find(IGTFSFeed feed, string tripId) { return(this.Find(feed, tripId, 20)); }
/// <summary> /// Filters the given feed and returns a filtered version. /// </summary> /// <param name="feed"></param> /// <returns></returns> public override IGTFSFeed Filter(IGTFSFeed feed) { var filteredFeed = new GTFSFeed(); // filter routes. var routeIds = new HashSet <string>(); var agencyIds = new HashSet <string>(); foreach (var route in feed.Routes) { if (_routesFilter.Invoke(route)) { // keep this route. filteredFeed.Routes.Add(route); routeIds.Add(route.Id); // keep agency ids. agencyIds.Add(route.AgencyId); } } // filter trips. var serviceIds = new HashSet <string>(); var tripIds = new HashSet <string>(); var shapeIds = new HashSet <string>(); foreach (var trip in feed.Trips) { if (routeIds.Contains(trip.RouteId)) { // keep this trip, it is related to at least one stop-time. filteredFeed.Trips.Add(trip); tripIds.Add(trip.Id); // keep serviceId, routeId and shapeId. serviceIds.Add(trip.ServiceId); if (!string.IsNullOrWhiteSpace(trip.ShapeId)) { shapeIds.Add(trip.ShapeId); } } } // filter stop-times. var stopIds = new HashSet <string>(); foreach (var stopTime in feed.StopTimes) { if (tripIds.Contains(stopTime.TripId)) { // stop is included, keep this stopTime. filteredFeed.StopTimes.Add(stopTime); // save the trip id's to keep. stopIds.Add(stopTime.StopId); } } // filter stops. foreach (var stop in feed.Stops) { if (stopIds.Contains(stop.Id)) { // stop has to be included. stopIds.Add(stop.Id); filteredFeed.Stops.Add(stop); } } // filter agencies. foreach (var agency in feed.Agencies) { if (agencyIds.Contains(agency.Id)) { // keep this agency. filteredFeed.Agencies.Add(agency); } } // filter calendars. foreach (var calendar in feed.Calendars) { if (serviceIds.Contains(calendar.ServiceId)) { // keep this calendar. filteredFeed.Calendars.Add(calendar); } } // filter calendar-dates. foreach (var calendarDate in feed.CalendarDates) { if (serviceIds.Contains(calendarDate.ServiceId)) { // keep this calendarDate. filteredFeed.CalendarDates.Add(calendarDate); } } // filter fare rules. var fareIds = new HashSet <string>(); foreach (var fareRule in feed.FareRules) { if (routeIds.Contains(fareRule.RouteId)) { // keep this fare rule. filteredFeed.FareRules.Add(fareRule); // keep fare ids. fareIds.Add(fareRule.FareId); } } // filter fare attributes. foreach (var fareAttribute in feed.FareAttributes) { if (fareIds.Contains(fareAttribute.FareId)) { // keep this fare attribute. filteredFeed.FareAttributes.Add(fareAttribute); } } // filter frequencies. foreach (var frequency in feed.Frequencies) { if (tripIds.Contains(frequency.TripId)) { // keep this frequency. filteredFeed.Frequencies.Add(frequency); } } foreach (var transfer in feed.Transfers) { if (stopIds.Contains(transfer.FromStopId) && stopIds.Contains(transfer.ToStopId)) { filteredFeed.Transfers.Add(transfer); } } return(filteredFeed); }
/// <summary> /// Validates a GTFS feed. /// </summary> /// <param name="feed"></param> /// <param name="messages"></param> /// <returns></returns> public static bool Validate(IGTFSFeed feed, out string messages) { // check agencies. var agencyIds = new HashSet<string>(); foreach (var agency in feed.Agencies) { if (agencyIds.Contains(agency.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate agency id found: {0}", agency.Id); return false; } agencyIds.Add(agency.Id); } // check stops. var stopIds = new HashSet<string>(); foreach(var stop in feed.Stops) { if(stopIds.Contains(stop.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate stop id found: {0}", stop.Id); return false; } stopIds.Add(stop.Id); } // check routes. var routeIds = new HashSet<string>(); foreach(var route in feed.Routes) { if (routeIds.Contains(route.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate route id found: {0}", route.Id); return false; } routeIds.Add(route.Id); if (!agencyIds.Contains(route.AgencyId)) {// oeps, unknown id. messages = string.Format("Unknown agency found in route {0}: {1}", route.Id, route.AgencyId); return false; } } // check trips for routes. var tripIds = new HashSet<string>(); foreach(var trip in feed.Trips) { if (tripIds.Contains(trip.Id)) { // oeps, duplicate id. messages = string.Format("Duplicate trip id found: {0}", trip.Id); return false; } tripIds.Add(trip.Id); if (!routeIds.Contains(trip.RouteId)) { // oeps, unknown id. messages = string.Format("Unknown route found in trip {0}: {1}", trip.Id, trip.RouteId); return false; } } // check stop times. var stopTimesIndex = new Dictionary<string, List<StopTime>>(); var stopTimes = new HashSet<Tuple<string, uint>>(); foreach(var stopTime in feed.StopTimes) { var stopTimeId = new Tuple<string, uint>(stopTime.TripId, stopTime.StopSequence); if(stopTimes.Contains(stopTimeId)) { // oeps, duplicate id. messages = string.Format("Duplicate stop_time entry found: {0}", stopTime.TripId, stopTime.StopSequence); return false; } stopTimes.Add(stopTimeId); List<StopTime> stopTimeList; if (!stopTimesIndex.TryGetValue(stopTime.TripId, out stopTimeList)) { stopTimeList = new List<StopTime>(); stopTimesIndex.Add(stopTime.TripId, stopTimeList); } stopTimeList.Add(stopTime); if (!stopIds.Contains(stopTime.StopId)) { // oeps, unknown id. messages = string.Format("Unknown stop found in stop_time {0}: {1}", stopTime.StopId, stopTime.StopId); return false; } if (!tripIds.Contains(stopTime.TripId)) { // oeps, unknown id. messages = string.Format("Unknown trip found in stop_time {0}: {1}", stopTime.StopId, stopTime.TripId); return false; } } // check all sequences. foreach (var stopTimesPair in stopTimesIndex) { uint current = 0, previous; foreach (var stopTime in stopTimesPair.Value) { previous = current; current = stopTime.StopSequence; if (previous != 0) { if (previous >= current) { messages = string.Format("Stop sequences values shall increase and be unique in stop_times file for trip id {0}.", stopTimesPair.Key); return false; } } } } messages = string.Empty; return true; }