Esempio n. 1
0
        /// <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);
        }
Esempio n. 3
0
        /// <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()}");
            }
        }
Esempio n. 5
0
        /// <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);
            }
        }
Esempio n. 6
0
        /// <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);
            }
        }
Esempio n. 7
0
 /// <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);
     }
 }
Esempio n. 8
0
        /// <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();
            }
        }
Esempio n. 10
0
        /// <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);
        }
Esempio n. 11
0
        /// <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);
        }
Esempio n. 12
0
        /// <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);
        }
Esempio n. 13
0
        /// <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);
        }
Esempio n. 15
0
 /// <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);
 }
Esempio n. 16
0
 /// <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));
        }
Esempio n. 18
0
        /// <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;
        }
Esempio n. 19
0
 /// <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);
 }
Esempio n. 20
0
 /// <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;
 }
Esempio n. 21
0
        /// <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);
        }
Esempio n. 22
0
 /// <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;
 }
Esempio n. 23
0
 /// <summary>
 /// Filters the given feed and returns the result.
 /// </summary>
 /// <param name="feed"></param>
 /// <returns></returns>
 public abstract IGTFSFeed Filter(IGTFSFeed feed);
Esempio n. 24
0
        /// <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;
        }
Esempio n. 25
0
        /// <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));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Esempio n. 26
0
 /// <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));
 }
Esempio n. 27
0
        /// <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;
        }