Repository navigation
Walking and cycling legs
Walking and cycling appear in the model in two roles:
- as legs of a public transport journey: the pre-transport leg from the origin to the first stop and the post-transport leg from the last stop to the destination;
- as direct trips from origin to destination: the components
WW(walking) andCC(cycling).
This page describes step by step how the model computes both. The car is described on Private transport. How the legs are joined to the public transport chains is on From chains to journeys.
- Step 1: the network
- Step 2: preparing the network
- Step 3: the pre- and post-transport caches
- Step 4: the pre- and post-transport types of the components
- Step 5: the direct walking and cycling trips
- Parameters
Walking uses the OSM network: the road segments that the OSM classification marks as pedestrian (IsPedestrian). The extract follows the analysis moment: AnalysisMoment chooses a row of ModelParameters/AnalysisMomentPresets, and its OSM_file_date is the date of the extract. The model reads it from the network cache %LocalDataProjDir%/OSM/Final_Network_<date>_r3_<hash>.mmd, which it makes from the Geofabrik shapefiles (see Private transport, OSM). Every link can be walked in both directions.
ModelParameters/Advanced/CyclingNetwork chooses the network for all cycling: cycling as pre- and post-transport, the OV-fiets, and the direct cycling trip.
With 'OSM', the default, the network consists of the OSM segments of the road types in the bike network (IsPartOfBikeNetwork, a BikeSpeed above 1 km/h), from the same extract as walking. Every link can be ridden in both directions, because the oneway of the Geofabrik shapefiles does not say whether cyclists are exempt.
With 'Fietsersbond', the network is the route network of the Fietsersbond: the network of 2024, delivered by PBL on 25 May 2025 (Fietsersbond_file_date), in %NetworkModel_Dir%/Infrastructuur/Fietsersbond_20250525. One delivery serves all analysis moments. SourceData/Infrastructure/Fietsersbond/Network_Cycling turns it into a network with the attributes of the OSM bike network:
- It keeps the links with bicycle access (
toegangnot'geen') and two known nodes: 1,449,915 of the 1,771,242 links, 160,500 km. - A link with
toegang'heen'or'terug'(10% of the links) is a one-way cycle path: it can only be ridden in its direction of travel (IsOneDirection, with the geometry in that direction). - The 217 river ferries up to
Fietsersbond_MaxFerryLength(2 km) take part, withFietsersbond_FerryExtraTime(5 min) for waiting and crossing on top of the cycling time. The 62 longer crossings (2 to 39 km) do not. No origin, destination or stop is connected to a ferry. - The road type, used for the junction penalties, is
ferry,tertiary(cars above 30 km/h),residential(cars up to 30 km/h) orcycleway(cycle paths and pedestrian areas). - The topology is built from the end points of the segments, as for OSM, not from
van_idandnaar_id. - The network covers the Netherlands only, so there are no cycling legs to stops across the border.
With the Fietsersbond network the signature of the output names the network (Impl/CyclingNetwork_FileTag, _fb20250525-veer2000m5min_r2, with the revision Impl/Fietsersbond_Revision at the end). The output folder of a component that cycles gets _fb after its name (Impl/CyclingNetwork_DirTag), for example 20251014_b2025_CC_fb_<hash>. So the output of both networks can stand side by side. WW, W_PT_W and Car do not depend on the cycling network. The pre- and post-transport caches have the network in the hash of their name (see The cache).
The two networks compare as follows (#124):
- 95% of the Fietsersbond network lies within 25 m of the OSM bike network. Most of the rest is in OSM under a road type outside the bike network (footways, pedestrian areas, tracks without a grade).
- 72% of the OSM bike network in the Netherlands lies near a Fietsersbond link; the rest is mainly
pathandservice. - At 14 km/h on every link, without junction penalties and one-way cycle paths, trips between neighbourhoods of up to 15 minutes take as long over both networks: the median ratio of the travel times is 1.000.
- In the model, with the junction penalties and the one-way cycle paths, the direct cycling trip of Y2025 in the morning rush hour reaches 7.2% fewer OD pairs within 90 minutes over OSM than over the Fietsersbond network. For the pairs that both reach, it takes 2.4% longer in the median. With cycling in the pre- or post-transport, public transport reaches 1.6 to 5.6% fewer pairs over OSM. Which of the differences between the two measurements causes this is not measured.
Every link has a constant speed: WalkingSpeed_kmhr (4.5 km/h) for walking and CyclingSpeed_kmhr (14 km/h) for cycling. An e-bike is a run with other cycling values: 20 km/h and a cost per km of its own (Cycling_CostPerKm). Its output goes to folders of its own; see An e-bike.
PublicTransport_Prep/Create_Optimised_Network_Walking and Create_Optimised_Network_Cycling (Templates/CreateNetwork_Efficient_T) prepare one network per mode, which all pre- and post-transport types of that mode share:
-
connect_neconnects the origins, the destinations and the stops to the network at the nearest connectable point. - Every junction that a route passes costs time, a junction penalty. The penalty depends on the number of road links at the node: 0 s for 2 or fewer, 2 s for 3, 5 s for 4 and 6 s for more (
Advanced/Junction_Penalties, the same for walking and cycling). A junction where every road link is a slow-traffic road costs nothing, because thewith_*_lowerSpeedLinks_*penalties are 0 s. A slow-traffic road is a road type with a car speed of at most 30 km/h, such as a residential street, a service road or a footway (Classifications/OSM/roadType/IsSlowTrafficRoad). In a residential area a walker or cyclist does not stop at every side street. Each link carries half of the penalty of each of its end nodes, so a route pays the penalty of a junction once. Only road links count, not the connection links of origins, destinations and stops. - The model removes the nodes that are not junctions and merges their links into longer links. Routes and travel times do not change (see Network optimisation).
All pre- and post-transport types are selections from four base sets. The model computes each base set once per analysis date, origin set and destination set:
| Base set | From | To | Network | Cache |
|---|---|---|---|---|
Org2Stops_Walking |
every origin | every stop | walking | Legs_<Set>toStops_Walking_<hash>.parquet |
Org2Stops_Cycling |
every origin | every stop | cycling | Legs_<Set>toStops_Cycling_<hash>.parquet |
Stops2Dest_Cycling |
every stop | every destination | cycling | Legs_Stops2<Set>_Cycling_<hash>.parquet |
Stops2Dest_Walking |
every stop | every destination | walking |
Legs_Stops2<Set>_Walking_<hash>.parquet, only when the origin and destination sets differ |
When the origin set and the destination set are the same (PublicTransport_Prep/WalkingMirror), the model reads walking from a stop to a destination as the mirror of Org2Stops_Walking. This works because the walking network has no one-way links. Cycling has a cache per direction, because the Fietsersbond network has one-way cycle paths. On the OSM network both directions give the same legs.
Per base set the model runs one Dijkstra over the whole prepared network (PublicTransport_Prep/TimeInvariantConnections/<base set>/Create_Routes, Templates/CreateNetwork_Pedestrian_Bike_T, with impedance_matrix_od64):
- from every origin (pre-transport) or every stop (post-transport);
- to every stop or every destination that is reachable;
- cut at the maximum time of the base set:
MaxWalkingTime_Org2Stops,MaxCyclingTime_Org2Stops,MaxCyclingTime_Stops2DestandMaxWalkingTime_Stops2Dest, 15 minutes each.
The time of a leg is its network time: the length of each link divided by the speed, plus the junction penalties.
Each base set is a parquet file (zstd) in %LocalDataProjDir%/IntermediateResults, so that it can also be used outside the model:
- The columns are
BuurtCode(the CBS code of the neighbourhood),stop_idandstop_name(from the GTFS feed),Traveltime(the network time in whole seconds, uint16) andTravelDist(whole metres, uint32). The model rounds them when it computes them, so that a run computes the same with and without the cache. - In the name,
<Set>is the origin or destination set (Impl/OrgSet_NameTag,DestSet_NameTag, for exampleB2025),<hash>a hash of the signature. - The signature is in
<name>.signature.txtnext to the cache. It names the base set, the network and speed (with the OSM network cache by its hash, the junction penalties and the clean-up of the network), and the maximum time of the base set. It also names the origin and destination sets with the settings that place them, the analysis date and feed, the numbers of places and stops, the format and the revision. The model reads a cache only when its signature matches the settings of the run. Otherwise it computes the base set in the run. The next write then makes a new cache, which has another hash and so stands next to the old one.
For 2025 (Buurt2025 to Buurt2025) the walking cache is 5 MB and the two cycling caches 23 and 22 MB.
With Advanced/PrePostTransportDecoupled (default TRUE) a run reads the cache of a base set when it exists, and computes the base set itself when it does not. With FALSE every run computes the base sets, and the model writes no cache. The caches are written:
- with
batch\UpdateAll.cmd legs: all base sets without a cache with the current signature, in oneGeoDmsRun, once per analysis date and neighbourhood set.batch\UpdateAll.cmd alldoes the same before the output of each public transport component, so that the portions of the output read the caches instead of each computing the base sets; - in GeoDmsGui, under
/MakeUnlinkedData/Step2_PrePostTransport: right-clickGenerate_All(all base sets without a cache), orOrg2Stops_Walking,Org2Stops_CyclingorStops2Dest_Cycling(one base set), and choose Update TreeItem; then reload the configuration (Alt+R). See Running the steps in GeoDmsGui.
The caches do not depend on the part of the day or the component. They must be computed again after a change in the network (the OSM extract or CyclingNetwork), the walking or cycling speed, the maximum time of a base set, the origins, the destinations, or the stops (the feed and the analysis date). The maximum time of another pre- or post-transport type applies when the model reads the legs (see Step 4), so it does not change these caches.
The pre- and post-transport type of a component (see Components and merging) takes the connections of its base set in PublicTransport_Prep/Legs/<type> (template Legs_T). These are PublicTransport_Prep/PrePostTransport/<base set>/Connections: the cache, or the base set computed in the run. The type keeps the legs to or from the stops of its stop set, within its own maximum time:
| Type | Code | Base set | Stops |
|---|---|---|---|
Org2Stops_Walking |
W |
Org2Stops_Walking |
all |
Org2Stops_Cycling |
C |
Org2Stops_Cycling |
all |
Org2RM_Stops_Cycling |
Crm |
Org2Stops_Cycling |
rail and metro |
Org2RMT_Stops_Cycling |
Crmt |
Org2Stops_Cycling |
rail, metro and tram |
Org2R_Stops_Cycling |
Cr |
Org2Stops_Cycling |
train stations, of any rail operator |
Stops2Dest_Walking |
W |
Stops2Dest_Walking, or the mirror of Org2Stops_Walking
|
all |
Stops2Dest_Cycling |
C |
Stops2Dest_Cycling |
all |
R_Stops2Dest_Cycling |
rC |
Stops2Dest_Cycling |
train stations, of any rail operator |
RM_Stops2Dest_Cycling |
rmC |
Stops2Dest_Cycling |
rail and metro |
RMT_Stops2Dest_Cycling |
rmtC |
Stops2Dest_Cycling |
rail, metro and tram |
OVF_Stops2Dest_Cycling |
ovfC |
Stops2Dest_Cycling |
stops near an OV-fiets location |
The stop sets follow the GTFS feed. A stop is a rail, metro or tram stop by the route type of most of its stop times (Stops/Mode_rel). The feed has no route type for light rail: RandstadRail E is metro in the feed, and the light rail lines to Zoetermeer, Nieuwegein and IJsselstein are tram, so none of them is a train station. Cr and rC take every train station, also one that only a regional or foreign operator serves, such as Arriva, Blauwnet (Keolis), Breng or DB. The model takes the modes as the feed gives them and has no stop set of its own for light rail (#152). An intercity station, where a cycling pre-transport leg gets an extra parking time (see Extra time below), is every stop within Advanced/DistanceTrainStationsSelection (350 m) of an intercity station of NS, bus stops included.
A selection gives the same legs as a Dijkstra of its own, because the route to a stop does not depend on which other stops are targets. The maximum time of a type may not exceed that of its base set (an integrity check of Legs_T).
When the model reads a leg, it adds fixed times to its network time:
- every cycling leg:
Cycling_StartTime(1 min); - a cycling pre-transport leg: also
Advanced/Cycling_PreTransport_Penalty(0 min), andCycling_ParkingTime_IC_Station(3 min) when it ends at an intercity station.
The maximum times apply to the network time, as the cut of the Dijkstra does.
- An OV-fiets leg (
ovfC) costs the rental priceOVFiets_RentalPrice, the NS tariff of the fare year (4.65 euro in 2025). - Any other leg costs its time, including the extra time, times an amount per minute of the mode. For walking that amount is a valuation of the time:
PreTransport_Walking_Time_CostsandPostTransport_Walking_Time_Costs(0.19 euro per minute). So the cheapest journey does not simply walk as far as possible. For cycling the amount stands for the costs per kilometre:PreTransport_Cycling_Time_CostsandPostTransport_Cycling_Time_Costsfollow fromCycling_CostPerKm(0.09 euro per km) as an amount in euro per minute atCyclingSpeed_kmhr. Because they apply to the extra time as well, they also charge the start and parking time.
The price of a leg always counts in Price_Augm. In Price it counts for cycling with Advanced/IncludeCyclingCostsInPrice (default TRUE) and for walking with Advanced/IncludeWalkingCostsInPrice (default FALSE). The rent of the OV-fiets is paid and always counts in Price, also with IncludeCyclingCostsInPrice off (#108).
The components WW and CC are the direct trips (PublicTransport_Prep/Direct/Walking and Direct/Cycling):
- The route is a Dijkstra from every origin to every destination over the same network as the legs (
NetworkSetup/PrivateTransport/WalkingandCycling), up toMaxWalkingTime_Org2DestandMaxCyclingTime_Org2Dest(90 min). - The duration is the network time; for cycling plus
Cycling_StartTime. - The price is the duration times
Direct_Walking_Time_Costs(a valuation of the time) orDirect_Cycling_Time_Costs(the costs per kilometre as an amount per minute) whenIncludeWalkingCostsInPriceorIncludeCyclingCostsInPriceis set, else 0. - The direct trips do not depend on the departure moment or on the part of the day (
Classifications/Components/TimeInvariant). SoWWandCCwrite one file per origin block,tt_alle_<n>of<N>, which holds for every departure moment of every part of the day. Their output folder carries the origin set instead ofOutputLabel(Impl/OrgSet_PathTag, for example20251014_b2025_WW_<hash>), so the runs of every part of the day and every label share it. The output step ofbatch\UpdateAll.cmdcomputes them for the componentsWWandCC. There is no intermediate cache.
| Parameter | Default | Description |
|---|---|---|
WalkingSpeed_kmhr, CyclingSpeed_kmhr
|
4.5, 14 km/h | Speed on every link; in the signature of the pre- and post-transport caches. |
MaxWalkingTime_Org2Stops, MaxWalkingTime_Stops2Dest
|
15 min | Maximum walking pre- and post-transport time; in the signature of the pre- and post-transport caches. |
MaxCyclingTime_Org2Stops, MaxCyclingTime_Stops2Dest
|
15 min | Maximum cycling pre- and post-transport time of the base sets; in the signature of the pre- and post-transport caches. |
MaxCyclingTime_Org2RM_Stops, _Org2RMT_Stops, _Org2R_Stops, _RM_Stops2Dest, _RMT_Stops2Dest, _R_Stops2Dest, _OVF_Stops2Dest
|
15 min | Maximum time of the derived cycling types; at most that of the base set. They apply when the legs are read, so they are in the signature of the output, not in that of the pre- and post-transport caches. |
MaxWalkingTime_Org2Dest, MaxCyclingTime_Org2Dest
|
90 min | Maximum duration of a direct walking or cycling trip. |
Cycling_StartTime |
1 min | Added to every cycling leg and direct cycling trip. |
Cycling_ParkingTime_IC_Station, Advanced/Cycling_PreTransport_Penalty
|
3, 0 min | Extra time at the end of a cycling pre-transport leg at an intercity station, and at the end of every cycling pre-transport leg. |
OVFiets_RentalPrice |
NS tariff of the fare year (OVFiets_Tariffs), 4.65 euro in 2025 |
Price of an OV-fiets leg; it always counts in Price. |
Cycling_CostPerKm |
0.09 euro/km | Costs per kilometre of cycling on an own bike, from which the three *_Cycling_Time_Costs follow. |
PreTransport_Walking_Time_Costs, PostTransport_Walking_Time_Costs, Direct_Walking_Time_Costs
|
0.19 euro/min | Amount per minute of a walking leg or the direct walking trip: a valuation of the time. |
PreTransport_Cycling_Time_Costs, PostTransport_Cycling_Time_Costs, Direct_Cycling_Time_Costs
|
Cycling_CostPerKm at CyclingSpeed_kmhr, in euro/min |
Amount per minute of a cycling leg on an own bike or the direct cycling trip: the costs per kilometre as an amount per minute. |
Advanced/IncludeWalkingCostsInPrice, Advanced/IncludeCyclingCostsInPrice
|
FALSE, TRUE
|
Do the costs of walking (a valuation of the time) or of cycling on an own bike (the costs per kilometre) count in Price? They always count in Price_Augm. The rent of the OV-fiets always counts in Price. |
Advanced/CyclingNetwork |
'OSM' |
'OSM' or 'Fietsersbond'. |
Fietsersbond_file_date, Advanced/Fietsersbond_MaxFerryLength, Advanced/Fietsersbond_FerryExtraTime
|
'20250525', 2 km, 5 min |
The Fietsersbond delivery and its ferries. |
NetworkModel PBL documentation