Gtfs2osm

From OpenStreetMap Wiki
Jump to navigation Jump to search

gtfs2osm is an import script developed by NeatNit for importing bus stops from a GTFS feed into OpenStreetMap. It is currently (2024-2025) being used in Israel to update bus stop information from official government sources.

The script is a re-creation of an older bus stop import script that was used up until 2020. The script is fully automatic, but currently is only run manually. In the future, it should be set up to run automatically once a day.

Source code: https://codeberg.org/NeatNit/gtfs2osm-il

Forum discussion: https://community.openstreetmap.org/t/import-bus-stops-from-gtfs/117873

Import changesets: https://www.openstreetmap.org/user/gtfs2osm-il/history

What gtfs2osm does

gtfs2osm imports bus stops into OSM using GTFS data from the Israeli government ([1]).

Stops are identified by ref=*. The ref must be exactly the number as it appears on signs at the bus stops and in navigation apps. For example, ref=32789.

Standard tags

These tags define a bus stop.

Name

Hebrew, Arabic and English names are imported. These are the official names that appear in signage, so other mappers cannot change them in OSM - gtfs2osm will always change it back to the official value.

  • name:he=* - Hebrew name, see improvement notes below
  • name:ar=* - Arabic name
  • name:en=* - English name
  • The default name name=* is set to the Hebrew name by default, but if any mapper changes it to Arabic, then the Arabic name is used instead. Mappers should change the name to Arabic in Arabic-speaking areas. Unfortunately, this cannot be automated at this time.

Names in other languages are not touched, other mappers are free to edit them.

The name is improved from the GTFS name according to these rules:

  • Repeated single-quotation marks '' are converted to a double-quotation mark ". The GTFS data likely uses '' to avoid double-quotes being interpreted as delimiting a substring in the CSV format.
  • If there is a leading single-quotation mark, then it's supposed to be a Geresh and it's moved to the end. A lot of stops have this character in the wrong place.
  • Non-breaking space is replaced with normal space.
  • Leading and trailing whitespace is removed, and repeated spaces are merged together.
  • Double-quotation marks surrounded by Hebrew letters are converted to Gershayim (U+05F4 ״).
  • Single-quotation marks preceded by a Hebrew letter are converted to Geresh (U+05F3 ׳).

Missing multilingual name

Some bus stops are missing their Arabic or English name in the GTFS data. If you find a stop with missing English/Arabic name, you can add this info. Gtfs2osm will only overwrite the name when the official name becomes available.

This is especially annoying for stops that are already using the Arabic name, but have had a name update and are now missing their Arabic name. In such cases, gtfs2osm will clear the name:ar=* tag but will leave the name=* tag with the old Arabic name.

Position

The coordinates of bus stops are updated according to the following rules:

  • If the old position in OSM is more than 200 meters away from the position in GTFS, then the bus stop is not updated at all (not even the name tags). This is to prevent accidents due to typos by human mappers, e.g. you type in the wrong number and then gtfs2osm moves the node to another city altogether.
    • Exception: if the old position was last set by a GTFS import, then there is no risk of accident so the new position is applied.
  • If there is new position data in GTFS, then it is applied in OSM.
  • Mappers are allowed to move a bus stop (within 200m) if there is a reason to do this. Gtfs2osm will not override a mapper's position unless there is new data.
  • If the position difference between OSM and GTFS is less than 3 meters, then gtfs2osm assumes it was moved in error and the mapper tried to put it back where it was. Therefore, the GTFS position is applied again. If you encounter issues with this rule, feel free to say so in the community forum thread.

In other words, mappers can move a bus stop anywhere they want within 200 meters, but not within 3 meters to the GTFS position.

If gtfs2osm wants to update a stop's position but it's part of a way, then it cancels the move to avoid damaging the rest of the map. Bus stop nodes should never be part of a way, so these cases need to be fixed manually.

Level

In principle, in multi-floor bus stations (תחנות מרכזיות), level=* is set where available. Like position, mappers are allowed to change it, and it will only be overridden if the GTFS data has a new value for it. However, mappers cannot delete this tag if it is present.

However, individual stops in central stations are not currently supported because the data for them isn't easily usable, so in practice gtfs2osm should never set level=*.

Address

The approximate address of bus stops is sometimes available in the GTFS data. However, bus stops don't have an address per se, so the more appropriate object:*=* tags are used.

The address data is only available in Hebrew. Not all stops have address data.

Other mappers can change these values. They will only be updated by gtfs2osm when the GTFS data is updated. However, mappers cannot delete these tags if they are present.

GTFS tags

The following tags are set to identify the stop in GTFS:

Mappers should never modify these tags and should not add them to any new or existing stops. They are handled exclusively by gtfs2osm.

Restoring GTFS data after mappers have changed it

If a bus stop has been moved and you wish for gtfs2osm to put it back in its official position, use the tag: restore:gtfs=position

Similarly, if you wish to restore the street name: restore:gtfs=object:street

Multiple fields can be restored at once: restore:gtfs=position;level;object:street

Or to restore everything: restore:gtfs=all

The next time gtfs2osm runs, it will restore the requested fields and delete the restore:gtfs=* tag. Unfamiliar values in the tag will be preserved - beware of typos! For example restore:gtfs=street will not work (use object:street instead of street).

Other tags

Other tags on bus stops are completely ignored by gtfs2osm and can be changed freely by mappers.

Adding new stops

gtfs2osm will create a new stop in OpenStreetMap under the following conditions:

  • The stop is present in the GTFS data
  • There is no matching stop in OSM with the same ref=*
  • There is at least one bus route in the GTFS data stopping at this stop

The last rule is included because the GTFS data has a lot of false stops, old data that should have been deleted. The route data is assumed to be more reliable than the stop data, thus, if a route uses a bus stop it is assumed to be real.

However, a side effect is that newly built bus stops will not be added to OSM until a route actually uses them.

Deleting stops

gtfs2osm will delete a bus stop from OpenStreetMap under the following conditions:

  • The bus stop has the tag gtfs:stop_id:IL-MOT=*, meaning it had previously been updated by gtfs2osm, so it has authority to delete it
  • The ref=* does not match any bus stop in the GTFS data, meaning it no longer exists

Such stops will be deleted automatically.

However, a stop will not be deleted if it is part of a way, or it's a member of a relation. Bus stop nodes should never be part in a way - such cases are always mapping mistakes. As of February 2025, all such cases have been fixed in Israel. However, bus stops are often members of route relations, and there are sometimes outdated relations that include a bus stop that needs to be removed. For now, such cases can only be handled manually by a human mapper. The output log of gtfs2osm alerts the user of cases like this.

Adopting human-mapped stops

As mentioned before, bus stops are not added to OSM unless they have a route stopping at them. This means that inactive bus stops, e.g. newly built ones, are not added automatically.

If you find a bus stop that isn't mapped, you should create it as a node at the correct location with the following tags:

  • highway=bus_stop - required
  • public_transport=platform
  • bus=yes
  • ref=* - required. This must be the numeric stop number as it appears on the signs and in navigation apps. If you don't add this value, it will not be picked up by gtfs2osm.
  • All other tags associated with highway=bus_stop are optional, including name tags

Do not add gtfs:*=* tags.

If done correctly, the next time gtfs2osm runs it will "adopt" the new bus stop, add all the tags outlined above, and update the position to what's found in GTFS. If the manually-placed position is better, you can move it back.

Of course, if the GTFS data doesn't have a matching stop with the same number, then the node will not be touched. Gtfs2osm does not delete bus stops that it doesn't identify as its own, based on the tag gtfs:stop_id:IL-MOT=*.

In summary

Allowed changes

For a human mapper, the following changes are allowed.

Soft-update properties

You are free to update these properties

  • position - within 200 meters of the GTFS position, but not closer than 3 meters
    • if it's too far, gtfs2osm will stop updating the bus stop
    • if it's too close, gtfs2osm will return the stop to the GTFS position
  • level=*
  • object:city=*
  • object:street=*
  • object:housenumber=*

Arabic name

If a bus stop is in an Arabic-speaking area, change its name=* tag to the value in name:ar=*.

Missing data

These tags can be added by human mappers only if they are otherwise missing:

Extra information

Any other tags not handled by gtfs2osm are free to add or edit, in particular:

Deleting bus stops that aren't there

If you find a bus stop in OpenStreetMap that doesn't actually exist in real life, you should delete it, even if it has gtfs tags. It will not be re-created by gtfs2osm as long as no bus route actually stops there.

Adding bus stops that are missing

Create a node and use the tags:

  • highway=bus_stop - required
  • public_transport=platform
  • bus=yes
  • ref=* - required. This must be the numeric stop number as it appears on the signs and in navigation apps. If you don't add this value, it will not be picked up by gtfs2osm.
  • All other tags associated with highway=bus_stop are optional, including name tags

Do not add any gtfs:*=* tags.

Disallowed changes

The following tags are exclusively updated by gtfs2osm or are required for its operation. Never change or delete these tags for a bus stop that's updated by gtfs2osm.

FAQ

Which bus stops were imported by gtfs2osm?

All the ones that have the tag gtfs:stop_id:IL-MOT=*. Overpass Turbo query: here (scroll to a location and click Run).

Can I move a bus stop?

Yes. Generally, if you move a bus stop it will stay where you put it. However, it should be between 3m and 200m away from its position in GTFS. See Position for more details.

Can I delete a bus stop?

Yes, if it isn't serving any bus routes. Please delete bus stops that do not exist in reality if you find them. This is old data that should have been removed from GTFS, but wasn't. Please let me know about this, so I can contact the relevant authorities and have them removed from the data.

Can I add a missing bus stop?

Yes, but be sure to add ref=* with the stop number. Do not add any gtfs:* tags.

Why do some stops need to be added manually?

The data contains a lot of bus stops that no longer exist. To prevent adding them, gtfs2osm avoid adding stops if they don't serve any bus routes. Unfortunately, this means it won't add new bus stops as they are built, until they serve actual bus routes.

Can I change the language of name=*?

Yes, it can be Hebrew or Arabic. For stops in Arabic settlements, copy the value from name:ar=*.

Can I change bus stop name tags?

Generally no, apart from selecting between Hebrew and Arabic. However, if GTFS data lacks English or Arabic names, you can fill in that data.

Can I add additional tags?

Yes. You are encouraged to add any tags that can be applied to highway=bus_stop. Use StreetComplete for a fun way to add missing data!

What are the object:* tags?

See Address and object:*=*.

What is required to run the bot myself?

Python 3.12 and some packages from pip should be all that's required. There is no issue with different people running the bot on different days.