Note
Go to the end to download the full example code.
Building a model from GMNS#
In this example we create a model for Marshalltown, Iowa, from GMNS data generated from OSM data.
To create a network file from OSM, you can either use AequilibraE or OSM2GMNS
Imports#
from pathlib import Path
from tempfile import mkdtemp
import pandas as pd
# Figure out where our polaris directory is (so we can get some test data)
from polaris import __file__ as polaris_dir
from polaris.network.create.triggers import create_network_triggers, delete_network_triggers
from polaris.network.network import Network
from polaris.utils.database.db_utils import commit_and_close
polaris_dir = Path(polaris_dir).parent.parent
2025-10-30 06:36:07 UTC+0000 - No pre-existing parameter file exists for this project. Will use default
Creates a new supply file with the chosen SRID sphinx_gallery_thumbnail_path = ‘../../examples/model_building/marshalltown.png’
data_folder = polaris_dir / "tests" / "data" / "docs" / "marshalltown"
srid = 3857
project_dir = mkdtemp(prefix="polaris_marshalltown_from_gmns_")
Network.create(Path(project_dir) / "Marshalltown-Supply.sqlite", srid=srid, jumpstart=True)
2025-10-30 06:36:08 UTC+0000 - Creating empty db: /tmp/polaris_marshalltown_from_gmns_kx3esh4g/Marshalltown-Supply.sqlite (jumpstart=True, with_defaults=True)
2025-10-30 06:36:08 UTC+0000 - Creating file at /tmp/polaris_marshalltown_from_gmns_kx3esh4g/Marshalltown-Supply.sqlite
2025-10-30 06:36:08 UTC+0000 - Adding Spatialite infrastructure to the database
2025-10-30 06:36:08 UTC+0000 - Creating Tables
2025-10-30 06:36:17 UTC+0000 - Creating Tables: Done in 0:00:08.642050 seconds
2025-10-30 06:36:17 UTC+0000 - Creating empty db: /tmp/polaris_marshalltown_from_gmns_kx3esh4g/Marshalltown-Supply.sqlite (jumpstart=True, with_defaults=True): Done in 0:00:08.701127 seconds
2025-10-30 06:36:17 UTC+0000 - Creating triggers for version 99999999
Opens the newly-created network and import the GMNS network into it
network = Network()
network.open(Path(project_dir) / "Marshalltown-Supply.sqlite")
network.ie.from_gmns(str(data_folder), "epsg:4326")
2025-10-30 06:36:17 UTC+0000 - Working with file on /tmp/polaris_marshalltown_from_gmns_kx3esh4g/Marshalltown-Supply.sqlite
2025-10-30 06:36:17 UTC+0000 - Importing Nodes
2025-10-30 06:36:37 UTC+0000 - Importing Links
2025-10-30 06:36:40 UTC+0000 - Importing Zones
2025-10-30 06:36:40 UTC+0000 - Importing Locations
2025-10-30 06:36:40 UTC+0000 - No location file to import from GMNS
2025-10-30 06:36:40 UTC+0000 - Finished GMNS import
Unfortunately, Polaris still does not support arbitrary link types due to hard coded parameters in the simulator, so we will have to map them manually.
crosswalk = {
"WALK": "WALKWAY",
"TRANSIT": "BUSWAY",
"RESIDENTIAL": "LOCAL",
"SERVICE": "OTHER",
"SECONDARY": "MINOR",
"CYCLEWAY": "BIKEWAY", # In Polaris, we do not have a type for both bike and walk
"TERTIARY": "COLLECTOR",
"PATH": "WALKWAY",
"UNCLASSIFIED": "OTHER",
"PRIMARY": "MAJOR",
"TRUNK": "PRINCIPAL",
"MOTORWAY": "EXPRESSWAY",
"TRACK": "WALKWAY",
}
with commit_and_close(network.path_to_file, spatial=True) as conn:
for k, v in crosswalk.items():
conn.execute("""UPDATE Link SET "type"=? WHERE "type"=?;""", [v, k])
conn.commit()
conn.execute("DELETE FROM Link_type WHERE link_type=?;", [k])
We also did not did the necessary data imputation into this GMNS network before importing into Polaris, so let’s do it now. This adds a free-flow speed of 10 m/s and 1 lane per direction for most links, which is a TERRIBLE data imputation practice, but suffices for the purpose of an example
sqls = [
"""UPDATE Link SET lanes_ab=min(1, lanes_ab) where "type" !='EXPRESSWAY';""",
"""UPDATE Link SET lanes_ba=min(1, lanes_ba) where "type" !='EXPRESSWAY';""",
"""UPDATE Link SET fspd_ab=min(10, fspd_ab) where lanes_ab>0;""",
"""UPDATE Link SET fspd_ba=min(10, fspd_ba) where lanes_ba>0;""",
]
with commit_and_close(network.path_to_file, spatial=True) as conn:
delete_network_triggers(conn)
for sql in sqls:
conn.execute(sql)
conn.commit()
create_network_triggers(conn)
2025-10-30 06:36:40 UTC+0000 - Deleting triggers
2025-10-30 06:36:40 UTC+0000 - Creating triggers for version 99999999
transit = network.transit
transit.purge()
feed = transit.new_gtfs(
file_path=data_folder / "marshalltownmunicipaltransit-ia-us.zip",
description="Marshalltown impressive PT system",
agency="MTPT",
)
# You can set map-matching on to get all routes following the network you have in your model
# This task is time-consuming, however
feed.set_allow_map_match(False)
feed.set_date("2019-10-08") # We should be a pick date very carefully
feed.path_store.threshold = 5000 # Let's assume we are on a machine with little memory
feed.set_do_raw_shapes(True)
transit.fix_connections_table()
2025-10-30 06:36:41 UTC+0000 - Clearing transit tables
2025-10-30 06:36:41 UTC+0000 - Table "TRANSIT_RAW_SHAPES" does not exist in the network
2025-10-30 06:36:41 UTC+0000 - Creating GTFS feed object for /home/gitlab-runner/builds/polaris/code/polarislib/tests/data/docs/marshalltown/marshalltownmunicipaltransit-ia-us.zip
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
We can also set a maximum speed to fix possible GTFS nonsense
max_speeds = pd.read_csv(data_folder / "transit_max_speeds.csv")
feed.set_maximum_speeds(max_speeds)
max_speeds.head()
We do the transit import
feed.doWork()
2025-10-30 06:36:41 UTC+0000 - Loading data for 2019-10-08 from the MTPT GTFS feed. This may take some time
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/gtfs_loader.py:438: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.defchararray.
key = np.core.defchararray.add(stoptimes["trip_id"].astype(str), np.array(["##"] * stoptimes.shape[0]))
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/gtfs_loader.py:439: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.defchararray.
key = np.core.defchararray.add(key, stoptimes["stop_sequence"].astype(str))
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
2025-10-30 06:36:41 UTC+0000 - Starting deconflict_stop_times
2025-10-30 06:36:41 UTC+0000 - There were a total of 301 segments that were too fast and were corrected
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
/home/gitlab-runner/builds/polaris/code/polarislib/polaris/network/transit/parse_csv.py:41: DeprecationWarning: numpy.core is deprecated and has been renamed to numpy._core. The numpy._core namespace contains private NumPy internals and its use is discouraged, as NumPy internals can change without warning in any release. In practice, most real-world usage of numpy.core is to access functionality in the public NumPy API. If that is the case, use the public NumPy API. If not, you are using NumPy internals. If you would still like to access an internal attribute, use numpy._core.records.
data = np.core.records.fromrecords(tot, names=[x.lower() for x in titles]) # type: ignore
2025-10-30 06:36:41 UTC+0000 - Building data structures
2025-10-30 06:36:41 UTC+0000 - Importing feed for agency MTPT on 2019-10-08
2025-10-30 06:36:41 UTC+0000 - Creating transit raw shapes for agency ID: 2
2025-10-30 06:36:42 UTC+0000 - Finished creating raw shapes
You also MUST rebuild network.active.build()
The network still doesn’t have zones, locations or other things, so we won’t bother about running consistency checks before we close it
network.close()
Total running time of the script: (0 minutes 35.454 seconds)