-
Notifications
You must be signed in to change notification settings - Fork 0
GraphBuilder
Trip planning operates on an object called a graph, which specifies all of the locations in your region, and how to get from one to another. The GraphBuilder is a command line tool for configuring and building the trip planner graph and any other artifacts necessary for trip planning. The tool is controlled by an XML configuration file that describes what features will be included in the graph and where the graph will be saved.
Make sure that your GraphBuilder is built from an up-to-date copy of the source tree. If you want to build in Eclipse, see GettingStartedEclipse.
If you're having performance problems, please read the Performance section.
If you are interested in the internal details of the graph, see .
The GraphBuilder application is contained in the opentripplanner-graph-builder
module.
To run the module in Eclipse, select the opentripplanner-graph-builder module in Eclipse and then choose Run -> Run Configurations. You'll get the run configuration window. Select Java Application from the left pane and then click the New launch configuration button (the little document icon with a +). This will create a new configuration, which should have project already filled in as "opentripplanner-graph-builder" (if it doesn't, you can click browse to set it manually). Click Search next to the field for the main class; this should automatically find the GraphBuilderMain class for you.
The last thing you'll want to do is click on the Arguments tab and put the full path to your XML config file under Program arguments and put -Xmx2048m
under VM arguments (that sets the max RAM for the graph building process to 2GB; you can set this to be more or less depending on how much RAM you have and how big of a graph you're building). Additional VM arguments may improve performance - see JVMPerformance for more details.
To run the module in Netbeans, select the opentripplanner-graph-builder module in Netbeans, right-click and then choose Properties. Under the Run node you'll see various run options used when the project is executed.
Click Browse next to the Main Class: field, and it should automatically select the org.opentripplanner.graph_builder.GraphBuilderMain
class. Click Select main class.
The last thing you'll want to do is put the full path to your XML config file in the Arguments field and put -Xmx2048m
in the VM Options field (that sets the max RAM for the graph building process to 2GB; you can set this to be more or less depending on how much RAM you have and how big of a graph you're building). Additional VM arguments may improve performance - see JVMPerformance for more details.
You can also run GraphBuilder from the command line. To do this, just run the following inside your OpenTripPlanner checkout:
mvn package -DskipTests
cd opentripplanner-graph-builder/target
java -Xmx2048M -jar graph-builder.jar /path/to/my/graph-config.xml
Where /path/to/my/graph-config.xml
is the path to your config file (see below for details).
The graph builder configuration file describes what data and settings are used to build the graph.
The trip planner uses data from many sources: transit data from GTFS, street data from OpenStreetMap or shapefiles, and elevation data from the National Elevation Dataset. Each data source has its own configuration options.
For street data, whether OSM or shapefile, there are some common properties: permissions, bicycle safety, slope override. Each importer will have its own way of handling these properties, discussed under the section for that importer. This terminology provides a common background.
Street segments: Streets are broken into segments. A segment is a portion of a street between two consecutive intersections (or between a dead end and the first intersection thereafter) -- for example, Lexington Avenue between 27th St and 28th St is one street segment. This is complicated by the way that street networks are stored. For OSM, consecutive street segments that share attributes can be stored together as one "way". For example, if 8th Ave has the same name, street type, bike lane, etc between W 12th and W 14th St, it may be stored as one way. This is not a problem -- it's just worth knowing. Conversely, in shapefiles, a street segment may be broken at an overpass even if there is no intersection. This does require special handling, since we want to ensure that, even though two streets meet at the same point, they don't intersect if one passes over the other.
Permissions: What types of users can traverse a given street segment. For instance, an attribute can be used to make certain street segments traversable by bikes only in one direction. Alternatively, a street type attribute could be used to make certain segments (e.g., highways) inaccessible to pedestrians. Street traversal permissions are defined in org.opentripplanner.routing.edgetype.StreetTraversalPermission
(Javadoc) and are:
NONE
ALL
PEDESTRIAN
BICYCLE
PEDESTRIAN_AND_BICYCLE
PEDESTRIAN_AND_CAR
BICYCLE_AND_CAR
CAR
Bicycle safety: The relative safety of street segments for bicycling. A standard street has a safety of 1. Safer streets have safeties between 0 and 1; more dangerous streets have safeties higher than 1. Safety is per unit distance traveled, so a very short path along a dangerous street could still be safer than a longer path along a safe street. The bicycle safety value only affects the relative weights given to street segments and does not affect the street segments' traversal permissions. That is, even if you say that all streets marked as "DANGEROUS" are 100 times less safe than a regular street, it is still possible that the trip planner will recommend a segment marked "DANGEROUS." If you want to guarantee that certain streets are never used by certain modes of transportation, you should do so using the permissions.
Slope Override: Slope data comes from a source that is not aware of the street network. It reflects the slope of the ground, even if the road is a bridge above the ground, or a tunnel underneath it. The slope override can be used to set the slope to 0 on street segments that do not follow the ground.
Notes: You can create notes on certain streets, which are shown when a user's path uses those streets. For example, for a road with a mud surface, you could add the note, "Caution: muddy!"
GraphBuilder expects a path to an XML file that defines the recipes for building the graph. Let's start with a simple example:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- Configure the GraphBuilderTask -->
<bean id="graphBuilderTask" class="org.opentripplanner.graph_builder.GraphBuilderTask">
<!-- The path where the graph bundle will be written -->
<property name="path" value="/tmp/graph-bundle" />
<property name="graphBuilders">
<list>
<!-- Include an OpenStreetMap graph builder plugin -->
<bean class="org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl">
<property name="provider">
<!-- Use an OSM provider that will automatically download OSM data in a specified region -->
<bean class="org.opentripplanner.openstreetmap.impl.RegionBasedOpenStreetMapProviderImpl">
<property name="regionsSource">
<!-- The region to collect OSM data -->
<bean class="org.opentripplanner.openstreetmap.impl.FixedRegionSourceImpl">
<property name="latFrom" value="47.64231867751335" />
<property name="lonFrom" value="-122.32950210571289" />
<property name="latTo" value="47.66839305008159" />
<property name="lonTo" value="-122.29001998901367" />
</bean>
</property>
<!-- Specify where downloaded OSM data will be cached -->
<property name="cacheDirectory" value="/tmp/osm-cache" />
</bean>
</property>
<property name="defaultWayPropertySetSource">
<bean class="org.opentripplanner.graph_builder.impl.osm.DefaultWayPropertySetSource" />
</property>
</bean>
</list>
</property>
</bean>
</beans>
If the configuration file looks a lot like a Spring IoC Container configuration file, you're right. We're using Spring to configure the build process, so you can do anything you'd normally do in Spring to manage your configuration.
So what's going on here? We've configured one top-level GraphBuilderTask bean (an object, roughly). Its "path" property determines the directory where the graph will be written. The bulk of the configuration takes place via the graphBuilders property. Here, we specify a list of "inner beans" implementing org.opentripplanner.graph_builder.services.GraphBuilder
.
OpenTripPlanner includes a number of useful builders by default. In the example above, we've configured an OpenStreetMap graph builder. This builder will automatically download OSM data for a configurable region and construct a street network from the resulting data. The OSM builder has more configurable options, which are described below in the documentation for key graph builder plugins.
When you execute the GraphBuilderMain application, it should pick up your configuration and run each of the graph builder plugins in-turn and then write the resulting graph to the graph bundle output directory you specified.
A graph builder plugin is any class that implements org.opentripplanner.graph_builder.services.GraphBuilder
. Find documentation for some useful builders below:
Graph builder class: org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl
This builder can construct a street network from OpenStreetMap data. The plugin needs an OSM data provider. The current source tree includes three by default:
- org.opentripplanner.openstreetmap.impl.AnyFileBasedOpenStreetMapProviderImpl
- org.opentripplanner.openstreetmap.impl.RegionBasedOpenStreetMapProviderImpl
AnyFileBasedOpenStreetMapProviderImpl
has a path
property that can be used to specify a previously downloaded *.osm
, *.osm.bz2
, *.osm.gz
, or *.osm.pbf
file to provide OSM data.
An example for building the graph from an OSM file would include a provider with the following declaration:
<property name="providers">
<list>
<!-- Use an OSM provider that reads a file-->
<bean class="org.opentripplanner.openstreetmap.impl.AnyFileBasedOpenStreetMapProviderImpl">
<property name="path" value="/path/to/osm/file.osm" />
</bean>
<!-- you could have more than one of these beans,
if you have multiple OSM files -->
</list>
</property>
The RegionBasedOpenStreetMapProviderImpl
expects a regionsSource
property that will include specification of regions where OSM data will automatically be downloaded using the public OSM REST API.
Each way in OSM (which roughly corresponds to a street segment) has certain tags associated with it. The tags are of the form key=value. For example, highway=footway means that a path is for pedestrians only.
From OSM tags, we can get permissions, slope overrides, and generated street names (in case the street name is not specified). The central structure is an ordered list of specifiers, where each specifier consists of one or more tag-value pairs. For example:
- highway=motorway
- highway=motorway;cycleway=*
- highway=motorway;oneway=true
- highway=motorway;cycleway=lane
In #3, * means wildcard -- so, that entry will match any tag which has anything at all in the cycleway tag (but not ways that don't have it). A way is matched against this list by the first-appearing most-specific tag set. Wildcard matches are used only to break ties. That means that if a way has only the tags highway=motorway and oneway=true, it will match the 3rd entry. If it has only highway=motorway and cycleway=route, it will match #2. And if it has highway=motorway, cycleway=lane, and oneway=true, it will match #3 because #3 appears before #4, and because #2 is only a wildcard match.
You can use the "defaultWayPropertySetSource" property to specify a
WayPropertiesSource
that provides a set of properties. We've
collected a good default set in
org.opentripplanner.graph_builder.impl.osm.DefaultWayPropertySetSource
:
<property name="defaultWayPropertySetSource">
<bean class="org.opentripplanner.graph_builder.impl.osm.DefaultWayPropertySetSource" />
</property>
The default way property source specifies permissions and safety, but you can also specify your own permissions to replace them. To set the permissions for OpenStreetMap data, you can use the wayProperties
property. Here's an example:
<property name="wayPropertySet">
<bean class="org.opentripplanner.graph_builder.impl.osm.WayPropertySet">
<property name="wayProperties">
<list>
<bean class="org.opentripplanner.graph_builder.impl.osm.WayPropertyPicker">
<property name="specifier" value="highway=motorway" />
<property name="properties">
<bean class="org.opentripplanner.graph_builder.impl.osm.WayProperties">
<property name="permission" value="CAR" />
</bean>
</property>
</bean>
<bean class="org.opentripplanner.graph_builder.impl.osm.WayPropertyPicker">
<property name="specifier" value="highway=motorway;cycleway=lane" />
<property name="properties">
<bean class="org.opentripplanner.graph_builder.impl.osm.WayProperties">
<property name="safetyFeatures" value="1.5,1.5" />
<property name="permission" value="BICYCLE_AND_CAR" />
</bean>
</property>
</bean>
<bean class="org.opentripplanner.graph_builder.impl.osm.WayPropertyPicker">
<property name="specifier" value="surface=gravel" />
<property name="properties">
<bean class="org.opentripplanner.graph_builder.impl.osm.WayProperties">
<property name="safetyFeatures" value="2.0,2.0" />
<property name="safetyMixin" value="true" />
</bean>
</property>
</bean>
...
</list>
</property>
...
</bean>
</property>
In this example, ways with the tag highway=motorway (and no cycleway tags) can only be traversed by car. In this example, ways with the tags highway=motorway can only be traversed by car. Ways with the tags highway=motorway and cycleway=lane are traversable by bicycles and cars, but are considered to be less safe (in both directions) per unit distance.
One way streets are handled automatically. Also handled automatically are cases where a street has different bicycle safety features on each side as specified by cycleway:left and cycleway:right.
The third WayPropertyPicker specifies a safety mixin. Safety mixins are selected independently of other WayPropertyPickers; any whose tags apply are used. Their safetyFeatures values are multiplied by whichever set of WayProperties are selected by the first-appearing most-specific rule. For example, a way with tags highway=motorway, cycleway=lane and surface=gravel would have bike safety 1.5 * 2.0 = 3.0.
By default, only ways with the highway tag, or railway=platform, are traversable at all.
Correct permissions vary by country. See
the OpenStreetMap wiki for some guidance.
The OSM Wiki has more information about possible cycleway values.
When computing the slopes of streets, we use the values from the National Elevation Dataset. This sometimes reflects the value of the underlying terrain rather than the streets. So in the case of bridges (and certain other structures), slope should be ignored.
<bean id="osmBuilder" class="org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl">
<property name="wayPropertySet">
<bean class="org.opentripplanner.graph_builder.impl.osm.WayPropertySet">
...
<property name="slopeOverrides">
<list>
<bean class="org.opentripplanner.graph_builder.impl.osm.SlopeOverridePicker">
<property name="specifier" value="bridge=yes" />
<property name="override" value="true" />
</bean>
...
</list>
</property>
</bean>
</property>
</bean>
Some OSM ways don't have names. Sometimes this is because nobody has entered the official names yet, or sometimes there are no official names (as in the case of walking paths through parks). The default name generated for these is sort of ugly: "way 11513514 from 3." You can instead generate a more generic name, which will lead to prettier directions.
Note the "{surface}" in the second namer. This is replaced by the value of the surface tag for each way that it is applied to.
<bean id="osmBuilder" class="org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl">
<property name="wayPropertySet">
<bean class="org.opentripplanner.graph_builder.impl.osm.WayPropertySet">
<property name="creativeNamers">
<list>
<bean class="org.opentripplanner.graph_builder.impl.osm.CreativeNamerPicker">
<property name="specifier" value="highway=cycleway" />
<property name="namer" value="Bike path" />
</bean>
<bean class="org.opentripplanner.graph_builder.impl.osm.CreativeNamerPicker">
<property name="specifier" value="highway=path" />
<property name="namer" value="Path with surface {surface}" />
</bean>
</list>
...
</property>
...
</bean>
</property>
</bean>
In general, choosing street names from simple OSM tags will be good enough. But in some cases, you need more advanced naming. OpenStreetMapGraphBuilderImpl includes a customNamer property. In Portland, we have used this to rename sidewalks which have the same name as their corresponding street, as well as cases where streets suddenly become paths (for instance NW 29th Ave at Wilson St).
<bean id="osmBuilder" class="org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl">
...
<property name="customNamer">
<bean class="org.opentripplanner.graph_builder.impl.osm.PortlandCustomNamer" />
</property>
</bean>
Notes work much like creative naming. You add notes by specifying a notes property on WayPropertySet, and then putting NotePicker beans in it, like so:
<property name="notes">
<list>
<bean class="org.opentripplanner.graph_builder.impl.osm.NotePicker">
<property name="specifier" value="highway=primary" />
<property name="noteProperties" value="{name} is a large road" />
</bean>
</list>
</property>
By default, OTP downloads the OSM data from the OSM API server. In some very densely mapped regions, this may fail because the API server has a limit on how much data it will return per call. In this case, you may run a local API server and point OTP to it. You might also choose a different API server if the main server is down or slow. See the OSM server status page for a list of servers.
To choose another server, edit your graph builder config as follows:
<bean class="org.opentripplanner.openstreetmap.impl.RegionBasedOpenStreetMapProviderImpl">
<!-- all of the usual stuff -->
<property name="downloader">
<bean class="org.opentripplanner.openstreetmap.impl.OSMDownloader">
<property name="apiBaseUrl" value="http://osmxapi.hypercube.telascience.org/api/0.6/" />
</bean>
</property>
...
</bean>
Graph builder class: org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetGraphBuilderImpl
This builder can construct a street network from shapefile data. The plugin needs two pieces to function:
- A shapefile datasource
- A schema that tells us how to map shapefile-specific field values into street names and travel permissions
A shapefile must have at least one feature per street segment; OTP will not attempt to compute intersections. It's OK if a street segment is broken into multiple consecutive features, as sometimes happens due to non-standard address numbering.
Let's jump right into an example to show you how to wire things up. The following example is for a Portland, OR street shapefile:
<bean id="shapefileStreetBuilder" class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetGraphBuilderImpl">
<!-- Shapefile data source -->
<property name="featureSourceFactory">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileFeatureSourceFactoryImpl">
<property name="path" value="path/to/your/shapes/Streets_pdx.shp" />
</bean>
</property>
<!-- Schema -->
<property name="schema">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetSchema">
<property name="idAttribute" value="LOCALID" />
<property name="nameAttribute" value="FULL_NAME" />
</bean>
</property>
</bean>
In the above example, we read shapefile data from a local path. The schema mapping is very simple. We specify that we read the street id and name from the "LOCALID" and "FULL_NAME" columns of the shapefile.
By default, the ShapefileStreetSchema allows travel in both directions of a street for pedestrians, bicycles, and cars. Let's look at an example with more complex rules. The following example is for a NYC street shapefile:
<bean id="shapefileStreetBuilder" class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetGraphBuilderImpl">
<property name="featureSourceFactory">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileFeatureSourceFactoryImpl">
<property name="path" value="path/to/nyc/streets/streets.shp" />
</bean>
</property>
<property name="schema">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetSchema">
<property name="idAttribute" value="StreetCode" />
<property name="nameAttribute" value="Street" />
<property name="permissionConverter">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.CaseBasedTraversalPermissionConverter">
<property name="attributeName" value="TrafDir" />
<property name="defaultPermission" value="PEDESTRIAN_AND_BICYCLE" />
<property name="permissions">
<map>
<entry key="W" value="ALL,PEDESTRIAN" />
<entry key="A" value="PEDESTRIAN,ALL" />
<entry key="T" value="ALL,ALL" />
</map>
</property>
</bean>
</property>
</bean>
</property>
</bean>
We specify the shapefile path as in the first example. In the second example, we specify different column names for street id and name ("StreetCode" and "Street" respectively). The new addition is the specification of a permissionsConverter
. This is how street directions are handled. Though you can plug-in your own implementation, we went with the CaseBasedTraversalPermissionConverter
, which allows us to set traversal permissions using a simple case-based ruleset from an attribute value. Specifically, we are using the value of the "TrafDir" column to tell us how traffic can proceed along both directions of a given street. By default, we only allow transit by pedestrians and bicyclists. However, based on the value of "TrafDir" we can specify both the forward and reverse permission. So, in this example, a one-way street is one with TrafDir W or A. The first element in the pair of permissions refers to travel along the direction of the street's geometry, the second against. So, on a street from longitude, latitude (-74, 40) to (-74,40.1), the first element refers to travel North, and the second refers to travel South.
Note the comma separating the travel permissions in the case map.
nameAttribute (required): The attribute (e.g., "FULL_NAME") in the shapefile whose values should be used to name the streets (e.g., "Degraw St.").
nameConverter (optional): A converter to use to extract the street name attribute from the shapefile. Specifying a nameConverter
is optional, however, if you do specify one, you should not specify a nameAttribute
. The most common use of a nameConverterer
is to handle data sets that have unnamed streets for which you want to provide a default value. For example, the following configuration would name streets using the values in the FULL_NAME attribute. If FULL_NAME is missing for a street segment, it will name the segment "Unnamed street."
<property name="nameConverter">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.StringAttributeFeatureConverter">
<property name="attributeName" value="FULL_NAME" />
<property name="defaultValue" value="Unnamed street" />
</bean>
</property>
idAttribute (required): The attribute (e.g., "LOCALID") in the shapefile that uniquely identifies the feature.
idConverter (optional): The converter to use to extract the ID attribute from the shapefile. See the description for nameConverter
above. If you specify an idConverter
, you should not specify a separate idAttribute
.
permissionConverter (optional): The converter to use when setting the traversal permissions for street segments. The simplest thing to do is to use a CaseBasedTraversalPermissionConverter
. If you want to set the traversal permissions based on a combination of different attributes in the shapefile (e.g., street type and street direction), you can use a CompositeConverter
along with multiple CaseBasedTraversalPermissionConverter
s.
bicycleSafetyConverter (optional): The converter to use when setting the relative safety of street segments for bicycling.
*slopeOverrideConverter' (optional): Converter to use to override the slope associated with certain streets segments.
Sometimes your bike lane data will be in a separate shapefile from your streets. In this case, you want to join the files. Each street segment will have some key, which will appear in both files (perhaps under different names).
In this case, use the JoinedFeatureConverter
(Javadoc).
<property name="bicycleSafetyConverter">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.JoinedFeatureConverter">
<property name="mainKey" value="SEG_ID" />
<property name="joinedKey" value="SEG_ID" />
<property name="converter">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.CaseBasedBicycleSafetyFeatureConverter">
...
</bean>
</property>
<property name="joinedSourceFactory">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.ShapefileFeatureSourceFactoryImpl">
<property name="path" value="/home/novalis/otp/opentripplanner/philly/Philadelphia_BikeNetwork2005.shp" />
</bean>
</property>
</bean>
</property>
<property name="noteConverter">
<bean class="org.opentripplanner.graph_builder.impl.shapefile.AttributeFeatureConverter">
<property name="attributeName" value="notes" />
</bean>
</property>
Unlike OSM notes, shapefile notes do not support templates.
Graph builder class: org.opentripplanner.graph_builder.impl.GtfsGraphBuilderImpl
(Javadoc)
This builder constructs a graph from a list of one or more GTFS bundles. Each bundle includes either a path or url property that specifies the location of the GTFS data. When using multiple GTFS bundles, you must also specify a defaultAgencyId, as shown in Example 2 below.
To reduce memory usage (and perhaps increase speed), it is possible optimize the GTFS using OneBusAway's GTFS Transformer.
By default, bikes may be taken along on all transit trips. The GTFS data (https://groups.google.com/forum/?fromgroups#! topic/gtfs-changes/QqaGO]uNmG7o) can specify whether bikes are allowed on specific trips. If no data is available, and bikes are disallowed, defaultBikesAllowed can be set to false:
<bean class="org.opentripplanner.graph_builder.model.GtfsBundle">
<property name="url" value="http://developer.trimet.org/schedule/GTFS/20100117/google_transit.zip" />
<property name="defaultBikesAllowed" value="false" />
</bean>
The following configures a GTFS builder using a single remote GTFS file.
<bean id="gtfsBuilder" class="org.opentripplanner.graph_builder.impl.GtfsGraphBuilderImpl">
<property name="gtfsBundles">
<bean id="gtfsBundles" class="org.opentripplanner.graph_builder.model.GtfsBundles">
<property name="bundles">
<list>
<bean class="org.opentripplanner.graph_builder.model.GtfsBundle">
<property name="url" value="http://developer.trimet.org/schedule/GTFS/20100117/google_transit.zip" />
</bean>
</list>
</property>
</bean>
</property>
</bean>
If you want to build a graph for a region that is served by multiple transit agencies, you will likely need to use multiple GTFS files. The snippet below demonstrates how to build a graph using multiple local GTFS files.
<bean id="gtfsBuilder" class="org.opentripplanner.graph_builder.impl.GtfsGraphBuilderImpl">
<property name="gtfsBundles">
<bean id="gtfsBundles" class="org.opentripplanner.graph_builder.model.GtfsBundles">
<property name="bundles">
<list>
<bean class="org.opentripplanner.graph_builder.model.GtfsBundle">
<property name="path" value="/Users/nicholasbs/dev/gtfs/MTA/nyct_subway_091213.zip" />
<property name="defaultAgencyId" value="MTA NYCT" />
</bean>
<bean class="org.opentripplanner.graph_builder.model.GtfsBundle">
<property name="path" value="/Users/nicholasbs/dev/gtfs/MTA/lirr_100113.zip" />
<property name="defaultAgencyId" value="LI" />
</bean>
<!-- add additional GtfsBundles here -->
</list>
</property>
</bean>
</property>
</bean>
Note that when using multiple bundles it is necessary to include the defaultAgencyId property.
By default, all transfers either go through the street network, or are defined using the pathways.txt proposal for GTFS. However, some systems do not yet have pathways.txt, but need to have in-station transfers (i.e. the New York City subway system). The fix for this is to use transfers.txt to define in-station transfers, with:
<bean class="org.opentripplanner.graph_builder.model.GtfsBundle">
<property name="path" value="/home/novalis/otp/opentripplanner/google_transit_nyct_subway.zip" />
<property name="defaultAgencyId" value="MTA NYCT" />
<property name="transfersTxtDefinesStationPaths" value="true" />
<property name="defaultStreetToStopTime" value="60" />
</bean>
The defaultStreetToStopTime property is used to allow some time to get from the street to off-street (underground or elevated) stops, and may be used independently of transfers.txt.
Graph builder class: org.opentripplanner.graph_builder.impl.TransitToStreetNetworkGraphBuilderImpl
(Javadoc)
The following shows an advanced example that builds a graph from a transit GTFS feed and uses the set of transit stops in that feed to automatically download OSM data to provide street coverage for ~ 1 mile around each transit stop.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="gtfsBuilder" class="org.opentripplanner.graph_builder.impl.GtfsGraphBuilderImpl">
<property name="gtfsBundles">
<bean id="gtfsBundles" class="org.opentripplanner.graph_builder.model.GtfsBundles">
<property name="bundles">
<list>
<bean class="org.opentripplanner.graph_builder.model.GtfsBundle">
<property name="path" value="/path/to/gtfs.zip" />
</bean>
</list>
</property>
</bean>
</property>
</bean>
<bean id="osmBuilder" class="org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl">
<property name="provider">
<bean class="org.opentripplanner.openstreetmap.impl.RegionBasedOpenStreetMapProviderImpl">
<property name="regionsSource">
<bean class="org.opentripplanner.graph_builder.impl.TransitStopsRegionsSourceImpl" />
</property>
<property name="cacheDirectory" value="/tmp/osm" />
</bean>
</property>
</bean>
<bean id="transitStreetLink" class="org.opentripplanner.graph_builder.impl.TransitToStreetNetworkGraphBuilderImpl" />
<bean id="graphBuilderTask" class="org.opentripplanner.graph_builder.GraphBuilderTask">
<property name="path" value="/path/to/store/graph-bundle" />
<property name="graphBuilders">
<list>
<!-- GTFS comes before OSM, because we use the loaded set of stops to determine our OSM coverage -->
<ref bean="gtfsBuilder" />
<ref bean="osmBuilder" />
<ref bean="transitStreetLink" />
</list>
</property>
</bean>
</beans>
Graph builder class: org.opentripplanner.graph_builder.impl.ned.NEDGraphBuilderImpl
(Javadoc)
OTP can load elevation data in GeoTIFF format and use it to help create bike routes with fewer hills.
You can find global elevation data from ASTER at a http://reverb.echo.nasa.gov. The data is available in horizontal resolutions down to 15m. Using the site is a bit complicated: The process is a written down here.
For the United States, data can be downloaded from the National Elevation Dataset (resolution up to 10m). In order to import NED data you will need some additional data files that describe a vertical datum. Download one of the datum archives at http://www.opentripplanner.org/resources/datum.zip or http://www.opentripplanner.org/resources/datum.tar.xz (smaller) and decompress it into the NED cache directory you specify in your graph builder config. See the section "datum transformation" below for more information.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="nedBuilder" class="org.opentripplanner.graph_builder.impl.ned.NEDGraphBuilderImpl">
<property name="gridCoverageFactory">
<bean class="org.opentripplanner.graph_builder.impl.ned.NEDGridCoverageFactoryImpl">
<property name="cacheDirectory" value="/path/to/cache/ned" />
</bean>
</property>
</bean>
<bean id="osmBuilder" class="org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl">
<property name="provider">
<bean class="org.opentripplanner.openstreetmap.impl.RegionBasedOpenStreetMapProviderImpl">
<property name="regionsSource">
<bean class="org.opentripplanner.graph_builder.impl.TransitStopsRegionsSourceImpl" />
</property>
<property name="cacheDirectory" value="/path/to/cache/osm" />
</bean>
</property>
</bean>
<bean id="graphBuilderTask" class="org.opentripplanner.graph_builder.GraphBuilderTask">
<property name="path" value="/path/to/store/graph-bundle" />
<property name="graphBuilders">
<list>
<ref bean="osmBuilder" />
<ref bean="nedBuilder" />
</list>
</property>
</bean>
</beans>
This assumes you have a GeoTIFF elevation model at /otp/cache/ned/ned.tiff.
<bean id="nedBuilder" class="org.opentripplanner.graph_builder.impl.ned.NEDGraphBuilderImpl">
<property name="gridCoverageFactory">
<bean class="org.opentripplanner.graph_builder.impl.ned.GeotiffGridCoverageFactoryImpl">
<property name="path" value="/otp/cache/ned/ned.tiff" />
</bean>
</property>
</bean>
You may wish to merge a series of GeoTIFFs together if your area encompasses multiple tif regions. First install the binary:
sudo apt-get install python-gdal
Then use the gdal_merge.py command to merge them together http://invisibleroads.com/tutorials/gdal-raster-merge.html:
gdal_merge.py image1.tif image2.tif -o merged.tif
Like GPS, OpenStreetMap uses the World Geodetic System of 1984 (WGS84) coordinate system, and altitudes in OSM are measured relative to the WGS84 datum. On the other hand, USGS elevation data from the National Elevation Dataset (NED, http://ned.usgs.gov/) are referenced to the North American Vertical Datum of 1988 (NAVD88).
The NAVD88 datum used by NED is an orthometric datum based on mean sea level in one particular part of the world; the datums used in GPS and OSM are ellipsoids intended to cover the whole Earth. Orthometric datums like NAVD 88 are equipotential (gravitational) surfaces of the Earth (geoids) which include the effects of topography because the Earth's mass is irregularly distributed. Ellipsoid datums like NAD83 are smooth geometric approximations of the earth’s surface (ellipsoids) without topography. Differences between the two are significant (up to 100 meters).
Current geoid models relate NAD83 ellipsoid heights to NAVD88 orthometric heights, i.e. the geoid for the continental United States is calibrated against and defined relative to the GPS ellipsoid.
According to this surveying article, "it is generally assumed that WGS 84 (original) is identical to NAD 83 (1986)." According to the NOAA "there is a 2 meter difference between two of the most frequently used 3-D datums, the North American Datum of 1983 (NAD 83) and the World Geodetic System of 1984 (WGS 84)" though this is probably also referring to horizontal differences.
In OTP we perform the conversion using a geoid defined relative to a NAD83 ellipsoid. This is backed up by an NOAA publication which states they are for all practical purposes identical, especially when using handheld equipment. NAD 83 and WGS 84 ellipsoid equivalence is also backed up by this post.
Floating islands are sections of the graph that are not connected to any other section. Sometimes, this is legitimate -- Governor's island in New York has no land connections to anywhere else. But sometimes, it's an artifact of how the data in OSM is structured. If you're having trouble making trips to a certain stop, you might consider using the PruneFloatingIslands graph builder. It's pretty simple:
<bean id="floatingIslands" class="org.opentripplanner.graph_builder.impl.PruneFloatingIslands" />
Here are some examples of memory consumption and run time for two graph builds on a 4-core Intel i5 machine:
Portland (GTFS + OSM PBF)
- Run time: real 1m32s user 2m56s
- Heap usage: With -Xmx2G, 1.4GB is actually used.
- Build runs fine with -Xmx1500M.
New York (11 GTFS + OSM PBF)
- Run time: real 5m8s user 7m48s
- Heap usage: With -Xmx8G, 4GB is actually used.
- Build completes with -Xmx3G, but takes 6m12s wall clock time.
On the routing side we have some more formal results. See .
The configurations above are intended for small or medium geographic areas. For walking and biking trips on larger areas, there is an option which makes trip planning faster, at the cost of making graph building much slower. To use it, first decide which modes (walking, biking) you wish to optimize for. The tradeoff is between on one hand graph building speed and memory usage, and on the other hand trip number of modes. More modes means slower and larger graphs. Larger graphs will also take up more space on disk, but this should rarely matter, as disk space is much cheaper than RAM.
NOTE: our recent more flexible biking options are incompatible with this optimization (called 'contraction hierarchies'), so this code is not as up-to-date as other parts of OTP. It will give incorrect results in many cases. It is recommended that this optimization not be used for the time being.
The only modes that can be optimized for are non-transit modes: walking, biking, and car. For multimodal trips including transit, the non-transit portions of the trip will be automatically optimized if the mode of that portion has been optimized. For example, in a walking plus transit trip, with the configuration below, the walking portion of the trip will be optimized.
The contraction factor offers a tradeoff of memory used and graph building time against trip planning performance. The range is 0 to 1, with 0 meaning no speed improvement at all, and 1 meaning the maximum possible speed improvement. The scale is not linear: 0.5 means a tiny improvement in performance for a small cost in memory usage. 0.95 is the smallest value that is reasonable to consider. 0.992 took a few hours to build the graph for the state of Oregon, but provided excellent performance.
Ordinary graph building does not benefit from multiple cores, but the performance optimization phase will automatically use as many cores as you have.
<bean id="graphBuilderTask" class="org.opentripplanner.graph_builder.GraphBuilderTask">
<property name="modes">
<list>
<bean class="org.opentripplanner.routing.core.TraverseOptions">
<property name="mode" value="BICYCLE" />
<property name="optimize" value="QUICK" />
</bean>
<bean class="org.opentripplanner.routing.core.TraverseOptions">
<property name="mode" value="BICYCLE" />
<property name="optimize" value="SAFE" />
</bean>
<bean class="org.opentripplanner.routing.core.TraverseOptions">
<property name="mode" value="WALK" />
<property name="optimize" value="QUICK" />
</bean>
</list>
</property>
<property name="contractionFactor" value="0.992" />
</bean>
For geeks in the audience, the technique used is core-based contraction hierarchies.
See also: JVMPerformance