Giter Site home page Giter Site logo

transport-validator's Introduction

Validate GTFS files

The General Transit Feed Specification (GTFS) defines a common format for public transportation schedules and associated geographic information.

This project is a validating tool for such files and can perform checks ranging from simple ones (the archive is not valid, a file is missing) to more complex ones (a vehicle is moving too fast).

Online tool

transport-validator is the tool used by the French National Access Point to validate GTFS files. If you want to use it online, you can validate your own files at this address.

Validation output

Validation output is twofold:

  • it gives useful information about the validated file, under the metadata entry
  • it lists a serie of validation items, with a corresponding severity, under the validations entry. When relevant, geographical data (GeoJSON) related to the issue is given to ease file debugging.

The output is by default formatted in json, but yaml is also available. See Options for more information.

{
    "metadata": {
        ...
    },
    "validations": {
       ...
    }
}

Metadata

Give useful information about the validated file content:

Entry format Description
start_date "YYYY-MM-DD" The starting date of the calendar information (both calendar.txt and calendar_dates.txt are taken into account).
end_date "YYYY-MM-DD" The ending date of the calendar information (both calendar.txt and calendar_dates.txt are taken into account).
networks_start_end_dates map Gives the starting and ending dates of the calendar information for each network. For example: {"agency name 1":{"start_date":"2022-08-18","end_date":"2022-10-23"}, "agency name 2":{"start_date":"2020-08-18","end_date":"2023-10-23"}}
networks list of strings A list of unique agencies names, found in agency.txt
modes list of strings A list of the route_types found in routes.txt
issues_count Object A summary of the validation issues found in the validations section. Keys of the object are the issue name, values are the number of corresponding issues found.
has_fares boolean True if a fare_attributes.txt file exists and contains information
has_shapes boolean True if a shapes.txt file exists and contains information
has_pathways boolean True if a pathways.txt file exists and contains information
some_stops_need_phone_agency boolean Some stops have a continuous_pickup or a continuous_drop_off field equal to 2.
some_stops_need_phone_driver boolean Some stops have a continuous_pickup or a continuous_drop_off field equal to 3.
validator_version string The validator version as found in the Cargo.toml

There is also a stats object inside with various statistics about the data:

Entry format Description
stops_count integer Number of stops found in the file stops.txt (for any location_type)
stop_areas_count integer Number of stop areas (location_type equal to 1) found in the file stops.txt.
stop_points_count integer Number of stops (location_type equal to 0) found in the file stops.txt.
stops_with_wheelchair_info_count integer or null Number of stops (with any location_type) with wheelchair_boarding information.
lines_count integer Number of routes found in routes.txt
routes_with_custom_color_count integer Number of routes with a custom color
routes_with_short_name_count integer Number of routes with a short name
routes_with_long_name_count integer Number of routes with a long name
trips_count integer Number of trips found in trips.txt
trips_with_bike_info_count integer Number of trips found in trips.txt with bike information provided (bikes_allowed equal to 1 or 2)
trips_with_wheelchair_info_count integer Number of trips found in trips.txt with wheelchair information provided (wheelchair_accessible equal to 1 or 2)
trips_with_shape_count integer Number of trips found in trips.txt with an attached shape
trips_with_trip_headsign_count integer Number of trips with a headsign
transfers_count integer Number of transfers
fares_attribute_count integer Number of fares attributes
fares_rules_count integer Number of fares rules

Note: For the stops_with_wheelchair_info_count, the information can be specified at the stop level (wheelchair_boarding equal to 1 or 2), or inherited from its parent station. Can be null if the GTFS contains errors preventing to compute this field.

Example

    "metadata": {
        "start_date": "2020-11-02",
        "end_date": "2022-01-31",
        "networks": [
            "carSud"
        ],
        "modes": [
            "bus", "tramway"
        ],
        "issues_count": {
            "ExcessiveSpeed": 5,
            "CloseStops": 10,
            "NullDuration": 10,
            "MissingName": 1,
            "MissingCoordinates": 1215,
            "InvalidCoordinates": 1215,
            "DuplicateStops": 49,
            "IdNotAscii": 171
        },
        "stats": {
            "stops_count": 17,
            "stop_areas_count": 2,
            "stop_points_count": 9,
            "stops_with_wheelchair_info_count": null,
            "lines_count": 5,
            "routes_with_custom_color_count": 0,
            "routes_with_short_name_count": 0,
            "routes_with_long_name_count": 4,
            "trips_count": 11,
            "trips_with_bike_info_count": 3,
            "trips_with_wheelchair_info_count": 3,
            "trips_with_shape_count": 0,
            "trips_with_trip_headsign_count": 9,
            "transfers_count": 0,
            "fares_attribute_count": 2,
            "fares_rules_count": 4
        },
        "has_fares": true,
        "has_shapes": true,
        "has_pathways": false,
        "some_stops_need_phone_agency": false,
        "some_stops_need_phone_driver": false
    }

Validations

The "validations" key contains the actual validation results.

Severity

Each check is associated with a severity level.

Severity Description
Fatal Critical error, the GTFS archive couldn't be opened
Error The file does not respect the GTFS specification
Warning Not a specification error, but something is most likely wrong in the data
Information Simple information

List of checks

The validator performs a number of checks. The list of checks can be seen in the file issues.rs.

Here is a human friendly list of them :

check name Severity Description
UnusedStop Information A stop is not used.
Slow Information The speed between two stops is too low.
ExcessiveSpeed Information The speed between two stops is too high.
CloseStops Information Two stops very close to each other in the same trips
InvalidRouteType Information The type of a route is not valid.
DuplicateStops Information Two stop points or stop areas look identical. They share the same name, and are geographically very close. This check is not applied to station entrances (location_type equal to 2)
DuplicateStopSequence Error Several stop times in a trip have the same stop_sequence value. The stop_sequence values within a trip must be unique.
ExtraFile Information The file does not belong to a GTFS archive
UnusedShapeId Information A shape_id defined in shapes.txt is not used elsewhere in the GTFS
NegativeTravelTime Warning The travel duration between two stops is negative.
NegativeStopDuration Warning The departure_time at a stop is earlier than its arrival_time.
MissingName Warning An agency, a route or a stop has its name missing.
MissingCoordinates Warning A shape point or a stop is missing its coordinate(s).
NullDuration Warning The travel duration between two stops is null.
MissingLanguage Warning The publisher language code is missing.
InvalidLanguage Warning The publisher language code is not valid.
DuplicateObjectId Error The object has at least one object with the same ID.
InvalidStopLocationTypeInTrip Warning Only Stop Points are allowed to be used in a Trip
InvalidStopParent Warning The parent station of this stop is not a valid one
IdNotAscii Warning The identifier is not only ASCII characters
MissingId Error An agency, a calendar, a route, a shape point, a stop or a trip has its Id missing.
MissingUrl Error An agency or a feed publisher is missing its URL.
InvalidUrl Error The URL of an agency or a feed publisher is not valid.
InvalidCoordinates Error The coordinates of a shape point or a stop are not valid.
InvalidTimezone Error The TimeZone of an agency is not valid.
MissingPrice Error A fare is missing its price.
InvalidCurrency Error The currency of a fare is not valid
InvalidTransfers Error The number of transfers of a fare is not valid.
InvalidTransferDuration Error The transfer duration of a fare is not valid.
ImpossibleToInterpolateStopTimes Error It's impossible to interpolate the departure/arrival of some stoptimes of the trip
InvalidShapeId Error A shape_id referenced in trips.txt does not exist in shapes.txt
InvalidReference Fatal Reference not valid. For example a stop referenced by a stop time that does not exist
InvalidArchive Fatal .zip Archive not valid.
UnloadableModel Fatal A fatal error has occured by building the links in the model
MissingMandatoryFile Fatal Mandatory file missing
SubFolder Error Files were in a subfolder, which is explicitly forbidden by the specification

Geojson information

When relevant for the check, geojson information is added for each check output, making the GTFS debug process easier.

Example

Here is a validation output containing one warning, triggered by a non Ascii Stop id:

"validations": {
    "IdNotAscii" : [
        {
            "severity": "Warning",
            "issue_type": "IdNotAscii",
            "object_id": "AllBél",
            "object_type": "Stop",
            "object_name": "",
            "related_objects": [],
            "geojson": {
                "features": [
                    {
                        "geometry": null,
                        "properties": {
                            "id": "AllBél",
                            "name": ""
                        },
                        "type": "Feature"
                    }
                ],
                "type": "FeatureCollection"
            }
        }
    ]
}

Another example showing the GeoJSON information for an information about two stops too close:

    "validations": {
        "CloseStops": [
            {
                "severity": "Information",
                "issue_type": "CloseStops",
                "object_id": "PH00320P",
                "object_type": "Stop",
                "object_name": "Baril Les Hauts",
                "related_objects": [
                    {
                        "id": "PH00320C",
                        "object_type": "Stop",
                        "name": "Baril Les Hauts"
                    },
                    {
                        "id": "MAGM",
                        "object_type": "Route",
                        "name": "MAGM-MagmaBus Navette Centre Ville St Philippe"
                    }
                ],
                "details": "distance between the stops is 0 meter(s)",
                "geojson": {
                    "features": [
                        {
                            "geometry": {
                                "coordinates": [
                                    55.71866572404381,
                                    -21.356751531407003
                                ],
                                "type": "Point"
                            },
                            "properties": {
                                "id": "PH00320P",
                                "name": "Baril Les Hauts"
                            },
                            "type": "Feature"
                        },
                        {
                            "geometry": {
                                "coordinates": [
                                    55.71866572404381,
                                    -21.356751531407003
                                ],
                                "type": "Point"
                            },
                            "properties": {
                                "id": "PH00320C",
                                "name": "Baril Les Hauts"
                            },
                            "type": "Feature"
                        },
                        {
                            "geometry": null,
                            "properties": {
                                "details": "distance between the stops is 0 meter(s)"
                            },
                            "type": "Feature"
                        }
                    ],
                    "type": "FeatureCollection"
                }
            }
        ]
    }

Installation

  1. This project is written in Rust. You need to first install Rust on your machine.

  2. Clone the project:

git clone https://github.com/etalab/transport-validator/
cd transport-validator

Run the validator

Run from a local directory

The release version can be run as:

cargo run --release -- --input test_data/unused_stop

It is the fastest and recommanded version to use when validating files.

If you are developping on the validator, you can also run a debug build:

cargo run -- --input test_data/unused_stop

The validator can read a zip file, or an url:

cargo run --release -- -i some_gtfs.zip
cargo run --release -- -i https://example.com/network.gfts

If you do not intend to run the validator as a dæmon, it can be compiled without dæmon support, saving on compile time and binary size:

cargo run --release --no-default-features -- -i some_gtfs.zip

Run as a dæmon

The validator can run as a HTTP dæmon to validate any file from a url.

For now the call is synchronous. Be aware that if the file is large, the time required to download the GTFS zip, the request might time out.

The command to launch the dæmon is:

cargo run --release

You can then ask for a validation:

curl http://localhost:7878/validate?url=https://example.com/gtfs.zip

Options

  • --input or -i: Path (can be a directory or a zip file) or HTTP URL (file will be downloaded) of the GTFS file.
  • --max-issues or -m: The maxium number of issues per type. Defaults to 1000.
  • --output-format or -f: Output format (when using the validator in command line). Value by default is json, but yaml is also available.
  • --custom-rules or -c: Path to a YAML file containing custom values to use during the validation.

Custom rules

Some values used during the validations can be customized by using the --custom-rules option and providing a path to a YAML file.

Available customizations are:

Field Description
max_tramway_speed Maximum speed in km/h on a route_type tramway before triggering an ExcessiveSpeed warning
max_subway_speed Maximum speed in km/h on a route_type subway before triggering an ExcessiveSpeed warning
max_rail_speed Maximum speed in km/h on a route_type rail before triggering an ExcessiveSpeed warning
max_bus_speed Maximum speed in km/h on a route_type bus before triggering an ExcessiveSpeed warning
max_ferry_speed Maximum speed in km/h on a route_type ferry before triggering an ExcessiveSpeed warning
max_cable_car_speed Maximum speed in km/h on a route_type cable car before triggering an ExcessiveSpeed warning
max_gondola_speed Maximum speed in km/h on a route_type gondola before triggering an ExcessiveSpeed warning
max_funicular_speed Maximum speed in km/h on a route_type funicular before triggering an ExcessiveSpeed warning
max_coach_speed Maximum speed in km/h on a route_type coach before triggering an ExcessiveSpeed warning
max_air_speed Maximum speed in km/h on a route_type air before triggering an ExcessiveSpeed warning
max_taxi_speed Maximum speed in km/h on a route_type taxi before triggering an ExcessiveSpeed warning
max_other_speed Maximum speed in km/h on a route_type other before triggering an ExcessiveSpeed warning

Example

max_bus_speed: 120
max_gondola_speed: 50

If you need to customize other values, please let us know.

Lint

To lint our code we use rustfmt

Install it running:

rustup component add rustfmt-preview

Lint your code running:

cargo fmt --all -- --write-mode=diff

Contact

Questions? Comments? Get in touch with the transport.data.gouv.fr's technical team: [email protected]

Alternatives

transport-validator's People

Contributors

antoine-de avatar antoineaugusti avatar baptisteb-a avatar dependabot[bot] avatar fchabouis avatar l-vincent-l avatar manu1400 avatar patochectp avatar stuebinm avatar thbar avatar tristramg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

transport-validator's Issues

Add a warning when the calendar is empty

We saw that it's possible to have no start/end dates for a dataset when the GTFS does not contain trips/calendar dates.

It seems strange to publish such a file on the NAP.

Adding a warning may be useful to make this information visible when validating or displaying data.

Implement datatools validators

  • ReferencesTripValidator
  • TimeZoneValidator
  • ServiceValidator
  • MisplacedStopValidator
  • DuplicateStopsValidator
  • NamesValidator
  • TripValidator
  • FeedValidator
  • SpeedTripValidator
  • OverlappingTripValidator
  • ReversedTripValidator
  • PatternFinderValidator
  • NewTripTimesValidator

Memory efficient validation

The IDFM GTFS starts to be quite big. Today, 2023-08-16, the ZIP archive is 103 Mo.

Line counts by file:

      85 agency.txt
    1151 calendar.txt
    2736 calendar_dates.txt
    4434 pathways.txt
    1761 routes.txt
  230555 stop_extensions.txt
 12970342 stop_times.txt
   51052 stops.txt
  189757 transfers.txt
  567651 trips.txt
 14019524 total

Validating the file requires > 4 Go of RAM.

  • Would it be possible to lower this amount easily or would it be a big project?
  • Which steps require high RAM usage?

Metadata: extract feed_start_date and feed_end_date from feed_info

Documentation: https://developers.google.com/transit/gtfs/reference#feed_infotxt

These fields would be useful for some GTFS files where service is only running for some months, for example during winter time. Our current tooling treats those GTFS as outdated when the end_date is in the past but we could "know" that it's still valid until a specific date if those fields are present and are in the future.

Example for a network operating only during winter (say in the Alps)

  • start_date: 2021-12-01
  • end_date: 2021-04-15
  • feed_start_date: 2021-11-01
  • feed_end_date: 2022-11-01

and the publisher would need to publish a new version of the GTFS before 2022-11-01.

Move this repository to rust-transit?

A long time ago, we created a github organization, https://github.com/rust-transit, to host gtfs-structures hoping to host some transit tools made in rust.
I don't really remember why we did not put this project in it, I feel it would be more natural for it to be in the same place as gtfs-structures.

I'm thinking about this as @Tristramg will be giving a talk about rust-transit at fosdem.

I don't have high hopes on this, but maybe we can attract some contributors (like gtfs-structures that have 15 contributors).

What are you thoughts on this @thbar @AntoineAugusti ?

We would obviously need to rewrite the readme a bit to tell that it's founded by transport.data.gouv.fr and beta.gouv.fr

Extract feed_info.feed_contact_email as a metadata

Email address for communication regarding the GTFS dataset and data publishing practices. feed_contact_email is a technical contact for GTFS-consuming applications. Provide customer service contact information through agency.txt.

This metadata is useful to send technical notifications, like expiry reminders.

It is already public if the GTFS is published online so it shouldn't be a problem to expose it in the metadata.

Metadata: add accessibility features

Report statistics on trips.wheelchair_accessible and stops.wheelchair_boarding to know if the file has some accessibility information baked in.

Unable to call validator on Linux command line

Hi folks,
Thank you very much for creating and maintaining this repository. Please accept my appreciation!

According to the readme.md I should invoke the following instructions on a Linux command line terminal to run the validator.

git clone https://github.com/etalab/transport-validator/
cd transport-validator
cargo run --release -- -i some_gtfs.zip

However, there is no file in this repository called cargo or I have not found it yet. Please point me in the right direction. Any hint is appreciated. Cheers!

Review error levels for duplicates

From the GTFS specification, ["Field types" section] for ID.

An ID field value is an internal ID, not intended to be shown to riders, and is a sequence of any UTF-8 characters. Using only printable ASCII characters is recommended. An ID is labeled "unique ID" when it must be unique within a file.

When the type is marked as Unique ID I think it should be an ERROR severity instead of a WARNING.

The relevant code path seems to be here.

check_duplicates(&raw_gtfs.stops, Severity::Warning)
.into_iter()
.chain(check_duplicates(&raw_gtfs.routes, Severity::Warning).into_iter())
.chain(check_duplicates(&raw_gtfs.trips, Severity::Warning).into_iter())
.chain(
check_duplicates(
raw_gtfs.calendar.as_ref().unwrap_or(&Ok(vec![])),
Severity::Error,
)
.into_iter(),
)
.chain(
check_duplicates(
raw_gtfs.fare_attributes.as_ref().unwrap_or(&Ok(vec![])),
Severity::Warning,
)
.into_iter(),
)

a duplicate ID for stops.id should be an ERROR. Other fields should be reviewed, we could check for more duplicates as well.

IDs should be unique

trips.id, stops.id etc should be unique but I think that the validator does not enforce the uniqueness at the moment.

Relevant for every field having a type set to ID in the spec.

Wrong color format is not detected

If a route has a wrong color value, an error is raised by gtfs_structures but the validator does not pick it up it seems.

The GTFS file is not well formated.: CSVError { file_name: "routes.txt", source: Error(Deserialize { pos: Some(Position { byte: 14991, line: 213, record: 213 }), err: DeserializeError { field: None, kind: Message("'' is not a valid color; RRGGBB format is expected, without a leading `#`") } }), line_in_error: Some(LineError { headers: ["route_id", "agency_id", "route_short_name", "route_long_name", "route_desc", "route_type", "route_url", "route_color", "route_text_color"], values: ["SNCF:1000", "SNCF", "", "Rennes - Brest", "", "2", "", "", ""] }) }'

Wondering if it should be handled explicitly here?

https://github.com/rust-transit/gtfs-structure/blob/348052aa229f21a2452971078d7c8d721ea9352b/src/error.rs#L28-L30

routes.agency_id: add check

This condition is not enforced at the moment.

Agency for the specified route. This field is required when the dataset provides data for routes from more than one agency in agency.txt, otherwise it is optional.

From routes.txt

non-zero exit code if checking failed when using --input

It would be nice if the validator returned a non-zero exit code when used on the cli with the --input option, either optionally behind a cli flag (e.g. --exit-code) or simply by default if --input is used. Perhaps it would even be possible to use different exit codes depending on severity of warnings or errors found (e.g. code 1 for fatal errors, 2 if there were no fatal errors but some warnings, etc.).

My motivation for this is that it'd make it much easier to use the tool in scripts or CI pipelines; currently the easiest way to answer "did validation fail?" i can find is to parse the json output with jq and check the validations (or issues_count) keys directly.

How to evaluate validation result?

Hi folks,
How are you? Thank you very much for creating and maintaining this repository. I am running the validator the first time on a GTFS archive. You see my call and result below. I am wondering, how do you usually process this kind of result. It does not look very friendly to be processed by human eyes. I appreciate any hint about evaluating the validators result!

Call:

cargo run --release -- -i ~/gtfs/vrn/vrn_gtfs_11.12.2020_0.zip

Here is a snippet from the result, as the entire result humongous:

[...],{"id":"vrn-40-698-1","object_type":"Route","name":"Fürth - Rimbach - Heppenheim - Bensheim"}],"details":"travel duration is null, but there are 2117 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06431:419:0:Ri O","object_type":"Stop","object_name":"Bürstadt, Beethovenplatz","related_objects":[{"id":"de:06431:421:0:1","object_type":"Stop","name":"Bürstadt, Erich-Kästner-Schule"},{"id":"vrn-40-645-1","object_type":"Route","name":"Schulverkehr Groß-Rohrheim - Biblis - Bürstadt - Lampertheim"}],"details":"travel duration is null, but there are 649 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:08128:12022:0:Bus1","object_type":"Stop","object_name":"Bad Mgh., Herrenwiesen/Diesel.","related_objects":[{"id":"de:08128:12007:0:Bus1","object_type":"Stop","name":"Bad Mergentheim, Bembe"},{"id":"vrn-49-945-1","object_type":"Route","name":"Bad Mergentheim - Königshofen - Lauda"}],"details":"travel duration is null, but there are 599 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07340:95210:0:RiO","object_type":"Stop","object_name":"Ruppertsweiler, Semler","related_objects":[{"id":"de:07340:95206:0:RiO","object_type":"Stop","name":"Ruppertsweiler, Ortsmitte"},{"id":"vrn-50-256-1","object_type":"Route","name":"Pirmasens - Lemberg - Glashütte / Münchweiler - Leimen"}],"details":"travel duration is null, but there are 548 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07332:470:2:Bus","object_type":"Stop","object_name":"Deidesheim, Bahnhof","related_objects":[{"id":"de:07332:747","object_type":"Stop","name":"Forst, Waage"},{"id":"vrn-22-512-1","object_type":"Route","name":"Forst - Deidesheim - Königsbach - Haardt - Neustadt"}],"details":"travel duration is null, but there are 1464 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07340:3784:0:RiN","object_type":"Stop","object_name":"Fischbach/Dahn, Grundschule","related_objects":[{"id":"de:07340:5978:0:RiW","object_type":"Stop","name":"Fischbach/Dahn, Feuerwehrhaus"},{"id":"vrn-50-251-1","object_type":"Route","name":"(Hinterweidenthal) - Dahn - Bundenthal - Fischbach/Dahn - Ludwigswinkel"}],"details":"travel duration is null, but there are 603 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06437:23328","object_type":"Stop","object_name":"Ober-Sensbach, Gasthaus Pflug","related_objects":[{"id":"de:06437:23330","object_type":"Stop","name":"Ober-Sensbach, Das Tolle Rott"},{"id":"vrn-48-53t-1","object_type":"Route","name":"(Hesselbach) - Gaimühle - Sensbachtal - Hetzbach - Beeerfelden"}],"details":"travel duration is null, but there are 1766 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07317:92168:0:1","object_type":"Stop","object_name":"Pirmasens, Mississippi Ave. N.","related_objects":[{"id":"de:07317:92014:0:1","object_type":"Stop","name":"Pirmasens, Zweibrücker Straße 148"},{"id":"vrn-82-11-1","object_type":"Route","name":"Ruftaxi Pirmasens Hbf-Exerzierplatz-Sommerwald-Husterhöhe-Fehrbach-Hengsberg-Hbf"}],"details":"travel duration is null, but there are 790 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07333:4823","object_type":"Stop","object_name":"Morschheim, Römerhof","related_objects":[{"id":"de:07333:4894","object_type":"Stop","name":"Oberwiesen, Ort"},{"id":"vrn-80-902-1","object_type":"Route","name":"Gaugrehweiler - Kriegsfeld - Kirchheimbolanden - Eisenberg"}],"details":"travel duration is null, but there are 4930 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06437:16146","object_type":"Stop","object_name":"Ober-Ostern, Kindergarten","related_objects":[{"id":"de:06437:23997","object_type":"Stop","name":"Ober-Ostern, Oberdorf"},{"id":"vrn-48-14t-1","object_type":"Route","name":"Reichelsheim - Ostern - Weschnitz - Rohrbach - Reichelsheim"},{"id":"vrn-48-14b-1","object_type":"Route","name":"Reichelsheim - Ostern - Weschnitz - Rohrbach - Reichelsheim"}],"details":"travel duration is null, but there are 646 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:08225:4795:0:RiS","object_type":"Stop","object_name":"Waldkatzenbach, Feriendorf","related_objects":[{"id":"de:08225:4770:0:RiN","object_type":"Stop","name":"Waldkatzenbach, Meisentalsiedl"},{"id":"vrn-33-821-1","object_type":"Route","name":"Buchen - Mudau - Eberbach"}],"details":"travel duration is null, but there are 519 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07320:93003:0:RiS","object_type":"Stop","object_name":"Mittelbach, Altheimer Straße","related_objects":[{"id":"de:07320:93048:0:1","object_type":"Stop","name":"Mittelbach, Hengstbach"},{"id":"vrn-83-222b-1","object_type":"Route","name":"Europaring (Ernstweiler) - Stadtmitte - Mittelbach"}],"details":"travel duration is null, but there are 670 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:08225:15178","object_type":"Stop","object_name":"Haßmersheim, Steg","related_objects":[{"id":"de:08225:53:0:RiW","object_type":"Stop","name":"Neckarzimmern, Schleuse"},{"id":"vrn-33-828-1","object_type":"Route","name":"Neckarmühlbach - Haßmersheim/Hüffenhardt - Mosbach"}],"details":"travel duration is null, but there are 1920 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06437:23242","object_type":"Stop","object_name":"Ober-Mossau, Friedrich","related_objects":[{"id":"de:06437:23244","object_type":"Stop","name":"Ober-Mossau, Oberdorf"},{"id":"vrn-48-45t-1","object_type":"Route","name":"Hiltersklingen - Mossautal - Steinbuch - Michelstadt - Erbach"},{"id":"vrn-48-45b-1","object_type":"Route","name":"Hiltersklingen - Mossautal - Steinbuch - Michelstadt - Erbach"}],"details":"travel duration is null, but there are 824 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07337:2728:0:RiO","object_type":"Stop","object_name":"Annweiler, Heller","related_objects":[{"id":"de:07337:8054:1:RiO","object_type":"Stop","name":"Annweiler, Bahnhof"},{"id":"vrn-50-525-1","object_type":"Route","name":"Annweiler - Wernersberg - Lug - Vorderweidenthal - (Bad Bergzabern)"}],"details":"travel duration is null, but there are 826 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:08225:2705:0:RiS","object_type":"Stop","object_name":"Mosbach, Duale Hochschule","related_objects":[{"id":"de:08225:6039:0:RiO","object_type":"Stop","name":"Mosbach, Schorre"},{"id":"vrn-33-835-1","object_type":"Route","name":"Mosbach - Billigheim - Oberschefflenz"}],"details":"travel duration is null, but there are 538 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06437:23231","object_type":"Stop","object_name":"Hiltersklingen, Mitte","related_objects":[{"id":"de:06437:23232","object_type":"Stop","name":"Hiltersklingen, Oberdorf"},{"id":"vrn-48-45t-1","object_type":"Route","name":"Hiltersklingen - Mossautal - Steinbuch - Michelstadt - Erbach"}],"details":"travel duration is null, but there are 1109 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07316:3131","object_type":"Stop","object_name":"Lachen, Linde","related_objects":[{"id":"de:07316:55557","object_type":"Stop","name":"Lachen, Bonhoefferstraße"},{"id":"vrn-32-507-1","object_type":"Route","name":"Neustadt - Geinsheim - Weingarten - Harthausen - Speyer"}],"details":"travel duration is null, but there are 566 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06437:23951","object_type":"Stop","object_name":"Ober-Mossau, Brau. Schmucker","related_objects":[{"id":"de:06437:23950","object_type":"Stop","name":"Unter-Mossau, Am Rehholz"},{"id":"vrn-48-45b-1","object_type":"Route","name":"Hiltersklingen - Mossautal - Steinbuch - Michelstadt - Erbach"}],"details":"travel duration is null, but there are 582 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:08222:5698","object_type":"Stop","object_name":"Rheinau, Dortmunder Straße","related_objects":[{"id":"de:08222:2349","object_type":"Stop","name":"Rheinau, Riedwiesen"},{"id":"vrn-17-48-1","object_type":"Route","name":"MA Rheinau Bahnhof - Am Rheinauer See - MA Rheinauhafen"}],"details":"travel duration is null, but there are 558 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:06437:23032","object_type":"Stop","object_name":"Airlenbach, Eiche","related_objects":[{"id":"de:06437:23034","object_type":"Stop","name":"Airlenbach, Liederbach"},{"id":"vrn-48-54t-1","object_type":"Route","name":"Beerfelden - Finkenbach/Rothenberg - Hirschhorn"}],"details":"travel duration is null, but there are 1284 meters between the stops"},{"severity":"Warning","issue_type":"NullDuration","object_id":"de:07317:92037:0:1","object_type":"Stop","object_name":"Pirmasens, Christiansgasse","related_objects":[{"id":"de:07317:92049:0:1","object_type":"Stop","name":"Pirmasens, Parkwaldsiedlung"},{"id":"vrn-82-12-1","object_type":"Route","name":"Ruftaxi Pirmasens Hbf-Exerzierpl.-Horeb-Kirchberg-Niedersimten-Ruhbank-Erlenbr."}],"details":"travel duration is null, but there are 1554 meters between the stops"}],"MissingCoordinates":[{"severity":"Warning","issue_type":"MissingCoordinates","object_id":"de:07311:775","object_type":"Stop","object_name":"775","related_objects":[],"details":"Latitude and longitude are missing"},{"severity":"Warning","issue_type":"MissingCoordinates","object_id":"de:08221:1700","object_type":"Stop","object_name":"1700","related_objects":[],"details":"Latitude and longitude are missing"}],"DuplicateStops":[{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08226:566:0:1","object_type":"Stop","object_name":"Eberbach, Verbindungsweg","related_objects":[{"id":"de:08226:566:0:2","object_type":"Stop","name":"Eberbach, Verbindungsweg"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:07319:4387:0:2","object_type":"Stop","object_name":"Rheindürkheim, Rhenania","related_objects":[{"id":"de:07319:4387:0:1","object_type":"Stop","name":"Rheindürkheim, Rhenania"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08215:32529:0:1","object_type":"Stop","object_name":"Östringen, Mitte","related_objects":[{"id":"de:08215:32529:0:2","object_type":"Stop","name":"Östringen, Mitte"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:07336:96261:0:2","object_type":"Stop","object_name":"Konken, Ortsmitte","related_objects":[{"id":"de:07336:96261:0:1","object_type":"Stop","name":"Konken, Ortsmitte"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:07336:96261:0:2","object_type":"Stop","object_name":"Konken, Ortsmitte","related_objects":[{"id":"de:07336:96261:0:3","object_type":"Stop","name":"Konken, Ortsmitte"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:07336:96261:0:1","object_type":"Stop","object_name":"Konken, Ortsmitte","related_objects":[{"id":"de:07336:96261:0:3","object_type":"Stop","name":"Konken, Ortsmitte"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1196:2:BRiW","object_type":"Stop","object_name":"Heidelberg, Altes Hallenbad","related_objects":[{"id":"de:08221:1196:1:RiW","object_type":"Stop","name":"Heidelberg, Altes Hallenbad"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1168:1:TrRiN","object_type":"Stop","object_name":"Heidelberg, Seegarten","related_objects":[{"id":"de:08221:1168:2:BuRiN","object_type":"Stop","name":"Heidelberg, Seegarten"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08222:5548:1:TrRiS","object_type":"Stop","object_name":"Mannheim, Alte Feuerwache","related_objects":[{"id":"de:08222:5548:2:BuRiS","object_type":"Stop","name":"Mannheim, Alte Feuerwache"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1168:1:TrRiW","object_type":"Stop","object_name":"Heidelberg, Seegarten","related_objects":[{"id":"de:08221:1168:2:BuRiW","object_type":"Stop","name":"Heidelberg, Seegarten"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:07331:8007:2:Bus","object_type":"Stop","object_name":"Saulheim, Bahnhof","related_objects":[{"id":"de:07331:8007:2","object_type":"Stop","name":"Saulheim, Bahnhof"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08222:2451:2:NBus","object_type":"Stop","object_name":"Mannheim, Paradeplatz","related_objects":[{"id":"de:08222:2451:2:A","object_type":"Stop","name":"Mannheim, Paradeplatz"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08226:4149:1:RiW","object_type":"Stop","object_name":"Weinheim, Wormser Straße","related_objects":[{"id":"de:08226:4149:2","object_type":"Stop","name":"Weinheim, Wormser Straße"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1196:1:RiO","object_type":"Stop","object_name":"Heidelberg, Altes Hallenbad","related_objects":[{"id":"de:08221:1196:2:BRiO","object_type":"Stop","name":"Heidelberg, Altes Hallenbad"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1193:1:TrRiO","object_type":"Stop","object_name":"Heidelberg, Stadtbücherei","related_objects":[{"id":"de:08221:1193:2:Bus O","object_type":"Stop","name":"Heidelberg, Stadtbücherei"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08226:2971:0:Ri S","object_type":"Stop","object_name":"Dilsberg, Blumenstrich","related_objects":[{"id":"de:08226:2971:0:Ri N","object_type":"Stop","name":"Dilsberg, Blumenstrich"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:06431:1590:2:2","object_type":"Stop","object_name":"Hirschhorn, Bahnhof","related_objects":[{"id":"de:06431:1590:2:1","object_type":"Stop","name":"Hirschhorn, Bahnhof"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1185:1:RiN","object_type":"Stop","object_name":"Heidelberg, Rheinstraße","related_objects":[{"id":"de:08221:1185:2:RiN","object_type":"Stop","name":"Heidelberg, Rheinstraße"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08221:1246:0:RiW","object_type":"Stop","object_name":"Neuenh., Med. Kl./Chirurg./Zoo","related_objects":[{"id":"de:08221:1246","object_type":"Stop","name":"Neuenh., Med. Kl./Chirurg./Zoo"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08222:2520:0:NBus","object_type":"Stop","object_name":"Neuostheim, Maimarkt","related_objects":[{"id":"de:08222:2520:0:RiN","object_type":"Stop","name":"Neuostheim, Maimarkt"}]},{"severity":"Information","issue_type":"DuplicateStops","object_id":"de:08222:2410:0:TrRiN","object_type":"Stop","object_name":"Mannheim, Carl-Benz-Straße","related_objects":[{"id":"de:08222:2410:0:BuRiN","object_type":"Stop","name":"Mannheim, Carl-Benz-Straße"}]}]}}

Cheers!

Raise an error when files are in a subfolder

While doing a pass of data quality on https://transport.data.gouv.fr, I noticed a number of GTFS are considered valid despite containing a subfolder:

I have opened this issue to discuss this at the spec level:

Which raised a return so far:

We get lots of support requests for exactly this topic in the OpenTripPlanner chat room.
I would welcome the spec being more clear on this.

The crate used to read GTFS in the validator is smoothly handling that, but other tools may not etc, causing them trouble to ingest the data.

I would personally be happy to consider a file invalid on https://transport.data.gouv.fr if there is a subfolder, since it complicates the ingestion.

Given how the crate works, would it be complicated to return an error in that case (i.e. detect the fact that while data is valid, it wasn't stored at the root)?

To be discussed with the team (@fchabouis @AntoineAugusti), just raising the point with concrete data.

Ajouter le nom du fichier contenant des erreurs/avertissements dans le rapport d'erreur

Le rapport d'erreur du validateur GTFS ne mentionne le fichier qui contient des erreurs que quand il y a des erreurs irrécupérables comme ici :
image

ça pourrait être utile aux producteurs et réutilisateurs d'avoir cette précision pour les avertissements également.
Dans cet exemple, on sait qu'il y a une URL invalide dans le fichier mais on ne sait pas dans quel fichier :
image

Sans cette précision, le producteur pensait que c'était l'URL du GTFS même (GTFS moissonné) qui était invalide.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.