schedule-router proptotype

This commit is contained in:
Arndt 2015-04-03 13:49:53 +02:00
parent 0316c41924
commit 55f8e7fb4a
18 changed files with 2359 additions and 0 deletions

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.btools</groupId>
<artifactId>brouter</artifactId>
<version>1.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>brouter-mem-router</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-expressions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-mapaccess</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.btools</groupId>
<artifactId>brouter-map-creator</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,265 @@
package btools.memrouter;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
import btools.mapaccess.OsmPos;
import btools.mapcreator.MapCreatorBase;
import btools.mapcreator.NodeData;
import btools.mapcreator.NodeIterator;
import btools.mapcreator.WayData;
import btools.mapcreator.WayIterator;
import btools.util.ByteArrayUnifier;
import btools.util.CompactLongMap;
import btools.util.FrozenLongMap;
import btools.util.LazyArrayOfLists;
/**
* GraphLoader loads the routing graph from
* the nodes+way files (much like mapcreator.WayLinker)
*
* @author ab
*/
public class GraphLoader extends MapCreatorBase
{
private CompactLongMap<OsmNodeP> nodesMap;
private BExpressionContextWay expctxWay;
private ByteArrayUnifier abUnifier;
private int currentTile;
private long linksLoaded = 0L;
private long nodesLoaded = 0L;
private static final int MAXTILES = 2592;
private List<LazyArrayOfLists<OsmNodeP>> seglistsArray = new ArrayList<LazyArrayOfLists<OsmNodeP>>(2592);
public static void main(String[] args) throws Exception
{
System.out.println("*** GraphLoader: load a routing graph in memory");
if (args.length != 5)
{
System.out.println("usage: java GraphLoader <node-tiles-in> <way-tiles-in> <lookup-file> <profile-file> <fahtplan-file>");
return;
}
BExpressionMetaData meta = new BExpressionMetaData();
// read lookup + profile for lookup-version + access-filter
BExpressionContextWay expctxWay = new BExpressionContextWay(meta);
File lookupFile = new File( args[2] );
File profileFile = new File( args[3] );
meta.readMetaData( lookupFile );
expctxWay.parseFile( profileFile, "global" );
GraphLoader graph = new GraphLoader();
File[] fahrplanFiles = new File[2];
fahrplanFiles[0] = new File( args[4] );
fahrplanFiles[1] = new File( args[5] );
graph.process( new File( args[0] ), new File( args[1] ), fahrplanFiles, expctxWay );
}
public void process( File nodeTilesIn, File wayTilesIn, File[] fahrplanFiles, BExpressionContextWay expctxWay ) throws Exception
{
this.expctxWay = expctxWay;
seglistsArray = new ArrayList<LazyArrayOfLists<OsmNodeP>>(MAXTILES);
for( int i=0; i < MAXTILES; i++ )
{
seglistsArray.add( null );
}
abUnifier = new ByteArrayUnifier( 16384, false );
nodesMap = new CompactLongMap<OsmNodeP>();
// read all nodes
new NodeIterator( this, false ).processDir( nodeTilesIn, ".u5d" );
// freeze the nodes-map
nodesMap = new FrozenLongMap<OsmNodeP>( nodesMap );
// trim the list array
for( int i=0; i<MAXTILES; i++ )
{
if ( seglistsArray.get(i) != null )
{
seglistsArray.get(i).trimAll();
}
}
// then read the ways
new WayIterator( this, false ).processDir( wayTilesIn, ".wt5" );
nodesMap = null; // don't need that anymore
System.out.println( "nodesLoaded=" + nodesLoaded + " linksLoaded=" + linksLoaded );
// now load the train-schedules
ScheduleParser.parseTrainTable( fahrplanFiles, this, expctxWay );
System.gc();
long mem = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
System.out.println( "memory after graph loading: " + mem / 1024 / 1024 + " MB" );
}
public OsmNodeP matchNodeForPosition( OsmPos pos, BExpressionContextWay wayCtx )
{
// todo: this creates empty lists lazy
int ilon = pos.getILon();
int ilat = pos.getILat();
List<OsmNodeP> nodes = new ArrayList<OsmNodeP>();
nodes.addAll( subListForPos( ilon-6125, ilat-6125 ) );
nodes.addAll( subListForPos( ilon-6125, ilat+6125 ) );
nodes.addAll( subListForPos( ilon+6125, ilat-6125 ) );
nodes.addAll( subListForPos( ilon+6125, ilat+6125 ) );
int mindist = Integer.MAX_VALUE;
OsmNodeP bestmatch = null;
for( OsmNodeP node : nodes )
{
int dist = pos.calcDistance( node );
if ( dist < mindist )
{
if ( wayCtx == null || hasRoutableLinks(node, wayCtx) )
{
mindist = dist;
bestmatch = node;
}
}
}
return bestmatch;
}
private boolean hasRoutableLinks( OsmNodeP node, BExpressionContextWay wayCtx )
{
for( OsmLinkP link = node.getFirstLink(); link != null; link = link.getNext( node ) )
{
if ( link.isWayLink() )
{
wayCtx.evaluate( false, link.descriptionBitmap, null );
if ( wayCtx.getCostfactor() < 10000.f )
{
return true;
}
}
}
return false;
}
@Override
public void nodeFileStart( File nodefile ) throws Exception
{
currentTile = tileForFilename( nodefile.getName() );
seglistsArray.set(currentTile, new LazyArrayOfLists<OsmNodeP>(160000) );
System.out.println( "nodes currentTile=" + currentTile );
}
@Override
public void nextNode( NodeData data ) throws Exception
{
OsmNodeP n = data.description == null ? new OsmNodeP() : new OsmNodePT(data.description);
n.ilon = data.ilon;
n.ilat = data.ilat;
n.selev = data.selev;
// add to the map
nodesMap.fastPut( data.nid, n );
// add also to the list array
subListForPos( n.ilon, n.ilat ).add( n );
nodesLoaded++;
}
@Override
public void wayFileStart( File wayfile ) throws Exception
{
currentTile = tileForFilename( wayfile.getName() );
System.out.println( "ways currentTile=" + currentTile );
}
@Override
public void nextWay( WayData way ) throws Exception
{
byte[] description = abUnifier.unify( way.description );
byte wayBits = 0;
expctxWay.decode( description );
if ( !expctxWay.getBooleanLookupValue( "bridge" ) ) wayBits |= OsmNodeP.NO_BRIDGE_BIT;
if ( !expctxWay.getBooleanLookupValue( "tunnel" ) ) wayBits |= OsmNodeP.NO_TUNNEL_BIT;
OsmNodeP n1 = null;
OsmNodeP n2 = null;
for (int i=0; i<way.nodes.size(); i++)
{
long nid = way.nodes.get(i);
n1 = n2;
n2 = nodesMap.get( nid );
if ( n1 != null && n2 != null && n1 != n2 )
{
if ( tileForPos( n1.ilon, n1.ilat ) == currentTile )
{
OsmLinkP link = n2.createLink(n1);
link.descriptionBitmap = description;
linksLoaded++;
}
}
if ( n2 != null )
{
n2.wayBits |= wayBits;
}
}
}
// from BInstallerView.java
private int tileForFilename( String filename )
{
String basename = filename.substring( 0, filename.length() - 4 );
String uname = basename.toUpperCase();
int idx = uname.indexOf( "_" );
if ( idx < 0 ) return -1;
String slon = uname.substring( 0, idx );
String slat = uname.substring( idx+1 );
int ilon = slon.charAt(0) == 'W' ? -Integer.valueOf( slon.substring(1) ) :
( slon.charAt(0) == 'E' ? Integer.valueOf( slon.substring(1) ) : -1 );
int ilat = slat.charAt(0) == 'S' ? -Integer.valueOf( slat.substring(1) ) :
( slat.charAt(0) == 'N' ? Integer.valueOf( slat.substring(1) ) : -1 );
if ( ilon < -180 || ilon >= 180 || ilon % 5 != 0 ) return -1;
if ( ilat < - 90 || ilat >= 90 || ilat % 5 != 0 ) return -1;
return (ilon+180) / 5 + 72*((ilat+90)/5);
}
private int tileForPos( int ilon, int ilat )
{
return ilon / 5000000 + 72 * ( ilat / 5000000 );
}
private int subIdxForPos( int ilon, int ilat )
{
int lonModulo = ilon % 5000000;
int latModulo = ilat % 5000000;
return ( lonModulo / 12500 ) + 400 * (latModulo / 12500);
}
private List<OsmNodeP> subListForPos( int ilon, int ilat )
{
if ( ilon < 0 || ilon >= 360000000 || ilat < 0 || ilat >= 180000000 )
{
throw new IllegalArgumentException( "illegal position: " + ilon + " " + ilat );
}
int tileNr = tileForPos( ilon, ilat );
if ( seglistsArray.get(tileNr) == null ) return new ArrayList<OsmNodeP>();
return seglistsArray.get(tileNr).getList( subIdxForPos( ilon, ilat ) );
}
}

View file

@ -0,0 +1,145 @@
/**
* Set off departure offsets (immutable)
*
* @author ab
*/
package btools.memrouter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OffsetSet
{
private static Map<Long,OffsetSet> existingSets = new HashMap<Long,OffsetSet>();
private static OffsetSet empty = new OffsetSet( 0L );
private static OffsetSet full = new OffsetSet( -1L );
protected long mask;
private static int instancecount = 0;
public static OffsetSet emptySet()
{
return empty;
}
public static OffsetSet fullSet()
{
return full;
}
private OffsetSet( long m )
{
mask = m;
}
private static OffsetSet create( long m, OffsetSet template )
{
if ( m == 0L )
{
return null;
}
if ( m == template.mask )
{
return template;
}
Long mm = Long.valueOf( m );
OffsetSet set = existingSets.get( mm );
if ( set == null )
{
set = new OffsetSet( m );
existingSets.put( mm, set );
instancecount++;
System.out.println( "created set: " + set + " instancecount=" + instancecount );
}
return set;
}
public static OffsetSet create( List<Integer> offsets, OffsetSet template )
{
long m = 0L;
for( Integer offset : offsets )
{
int i = offset.intValue();
if ( i >= 0 && i < 64 )
{
m |= ( 1L << i );
}
}
return create( m, template );
}
public int size()
{
return 64;
}
public boolean contains( int offset )
{
return ( ( 1L << offset ) & mask ) != 0L;
}
public OffsetSet add( int offset )
{
return create( mask | ( 1L << offset ), this );
}
public OffsetSet add( OffsetSet offsets )
{
return create(mask | offsets.mask, this );
}
public OffsetSet filter( OffsetSet in )
{
long fmask = in.mask;
fmask = fmask ^ ( fmask & mask );
return create( fmask, in );
}
public static OffsetSet filterAndClose( OffsetSet in, OffsetSetHolder gateHolder, int timeDiff )
{
OffsetSet gate = gateHolder.getOffsetSet();
long gmask = gate.mask;
long fmask = in.mask;
// delete the high offsets with offset + timeDiff >= maxoffset
fmask = timeDiff > 31 ? 0L : ( fmask << timeDiff ) >> timeDiff;
fmask = fmask ^ ( fmask & gmask );
gmask |= fmask;
gateHolder.setOffsetSet( create( gmask, gate ) ); // modify the gate
if ( timeDiff > 0 )
{
fmask = fmask ^ ( fmask & (gmask >> timeDiff) );
}
return create( fmask, in );
}
@Override
public String toString()
{
if ( mask == -1L ) return "*";
StringBuilder sb = new StringBuilder();
int nbits = 0;
for( int i=0; i<65; i++ )
{
boolean bit = i < 64 ? ((1L << i) & mask) != 0L : false;
if ( bit ) nbits++;
else if ( nbits > 0)
{
if ( sb.length() > 0 ) sb.append( ',' );
if ( nbits == 1) sb.append( i-1 );
else sb.append( (i-nbits) + "-" + (i-1) );
nbits = 0;
}
}
return sb.toString();
}
}

View file

@ -0,0 +1,13 @@
/**
* Set off departure offsets (immutable)
*
* @author ab
*/
package btools.memrouter;
public interface OffsetSetHolder
{
OffsetSet getOffsetSet();
void setOffsetSet( OffsetSet offsetSet );
}

View file

@ -0,0 +1,157 @@
/**
* Container for link between two Osm nodes (pre-pocessor version)
*
* @author ab
*/
package btools.memrouter;
public class OsmLinkP implements OffsetSetHolder
{
/**
* The description bitmap is mainly the way description
* used to calculate the costfactor
*/
public byte[] descriptionBitmap;
/**
* The target is either the next link or the target node
*/
protected OsmNodeP sourceNode;
protected OsmNodeP targetNode;
protected OsmLinkP previous;
protected OsmLinkP next;
public static int currentserial = 0; // serial version to invalidate link occupation
private int instanceserial = 0;
private OffsetSet offsets;
private int time;
public boolean isConnection()
{
return descriptionBitmap == null;
}
public boolean isWayLink()
{
return descriptionBitmap != null;
}
public OsmLinkP( OsmNodeP source, OsmNodeP target )
{
sourceNode = source;
targetNode = target;
}
protected OsmLinkP()
{
}
public OffsetSet getOffsetSet()
{
return offsets;
}
public void setOffsetSet( OffsetSet offsets )
{
this.offsets = offsets;
}
public boolean isVirgin()
{
return instanceserial != currentserial;
}
public OffsetSet filterAndClose( OffsetSet in, long arrival, boolean scheduled )
{
int minutesArrival = (int)(arrival/60000L);
if ( offsets == null || isVirgin() )
{
time = minutesArrival;
instanceserial = currentserial;
offsets = in;
return in;
}
return OffsetSet.filterAndClose( in, this, scheduled ? minutesArrival - time : 0 );
}
/**
* Set the relevant next-pointer for the given source
*/
public void setNext( OsmLinkP link, OsmNodeP source )
{
if ( sourceNode == source )
{
next = link;
}
else if ( targetNode == source )
{
previous = link;
}
else
{
throw new IllegalArgumentException( "internal error: setNext: unknown source" );
}
}
/**
* Get the relevant next-pointer for the given source
*/
public OsmLinkP getNext( OsmNodeP source )
{
if ( sourceNode == source )
{
return next;
}
else if ( targetNode == source )
{
return previous;
}
else
{
throw new IllegalArgumentException( "internal error: gextNext: unknown source" );
}
}
/**
* Get the relevant target-node for the given source
*/
public OsmNodeP getTarget( OsmNodeP source )
{
if ( sourceNode == source )
{
return targetNode;
}
else if ( targetNode == source )
{
return sourceNode;
}
else
{
throw new IllegalArgumentException( "internal error: getTarget: unknown source" );
}
}
/**
* Check if reverse link for the given source
*/
public boolean isReverse( OsmNodeP source )
{
if ( sourceNode == source )
{
return false;
}
else if ( targetNode == source )
{
return true;
}
else
{
throw new IllegalArgumentException( "internal error: isReverse: unknown source" );
}
}
}

View file

@ -0,0 +1,150 @@
/**
* Container for an osm node (pre-pocessor version)
*
* @author ab
*/
package btools.memrouter;
import btools.mapaccess.OsmPos;
public class OsmNodeP extends OsmLinkP implements Comparable<OsmNodeP>, OsmPos
{
public OsmNodeP( double dlon, double dlat )
{
ilon = (int)(dlon * 1000000 + 180000000);
ilat = (int)(dlat * 1000000 + 90000000);
}
public OsmNodeP()
{
}
/**
* The latitude
*/
public int ilat;
/**
* The longitude
*/
public int ilon;
/**
* The elevation
*/
public short selev;
public final static int NO_BRIDGE_BIT = 1;
public final static int NO_TUNNEL_BIT = 2;
public byte wayBits = 0;
// interface OsmPos
@Override
public int getILat()
{
return ilat;
}
@Override
public int getILon()
{
return ilon;
}
@Override
public short getSElev()
{
// if all bridge or all tunnel, elevation=no-data
return ( wayBits & NO_BRIDGE_BIT ) == 0 || ( wayBits & NO_TUNNEL_BIT ) == 0 ? Short.MIN_VALUE : selev;
}
@Override
public double getElev()
{
return selev / 4.;
}
// populate and return the inherited link, if available,
// else create a new one
public OsmLinkP createLink( OsmNodeP source )
{
if ( sourceNode == null && targetNode == null )
{
// inherited instance is available, use this
sourceNode = source;
targetNode = this;
source.addLink( this );
return this;
}
OsmLinkP link = new OsmLinkP( source, this );
addLink( link );
source.addLink( link );
return link;
}
// memory-squeezing-hack: OsmLinkP's "previous" also used as firstlink..
public void addLink( OsmLinkP link )
{
link.setNext( previous, this );
previous = link;
}
public OsmLinkP getFirstLink()
{
return sourceNode == null && targetNode == null ? previous : this;
}
// interface OsmPos
@Override
public int calcDistance( OsmPos p )
{
double l = (ilat-90000000) * 0.00000001234134;
double l2 = l*l;
double l4 = l2*l2;
double coslat = 1.- l2 + l4 / 6.;
double dlat = (ilat - p.getILat() )/1000000.;
double dlon = (ilon - p.getILon() )/1000000. * coslat;
double d = Math.sqrt( dlat*dlat + dlon*dlon ) * (6378000. / 57.3);
return (int)(d + 1.0 );
}
@Override
public long getIdFromPos()
{
return ((long)ilon)<<32 | ilat;
}
public byte[] getNodeDecsription()
{
return null;
}
public String toString2()
{
return (ilon-180000000) + "_" + (ilat-90000000) + "_" + (selev/4);
}
/**
* Compares two OsmNodes for position ordering.
*
* @return -1,0,1 depending an comparson result
*/
public int compareTo( OsmNodeP n )
{
long id1 = getIdFromPos();
long id2 = n.getIdFromPos();
if ( id1 < id2 ) return -1;
if ( id1 > id2 ) return 1;
return 0;
}
}

View file

@ -0,0 +1,27 @@
/**
* Container for an osm node with tags (pre-pocessor version)
*
* @author ab
*/
package btools.memrouter;
public class OsmNodePT extends OsmNodeP
{
public byte[] descriptionBits;
public OsmNodePT()
{
}
public OsmNodePT( byte[] descriptionBits )
{
this.descriptionBits = descriptionBits;
}
@Override
public final byte[] getNodeDecsription()
{
return descriptionBits;
}
}

View file

@ -0,0 +1,156 @@
/**
* Parser for a train schedule
*
* @author ab
*/
package btools.memrouter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import btools.expressions.BExpressionContextWay;
final class ScheduleParser
{
public static void parseTrainTable( File[] files, GraphLoader graph, BExpressionContextWay expctxWay )
{
try
{
ScheduledLine currentLine = null;
StationNode lastStationInLine = null;
boolean readingLocations = false;
Map<String,StationNode> stationMap = new HashMap<String,StationNode>();
for( File file : files )
{
BufferedReader br = new BufferedReader( new FileReader( file ) );
for(;;)
{
String line = br.readLine();
if ( line == null ) break;
line = line.trim();
if ( line.length() == 0 ) continue;
if ( line.startsWith( "#" ) ) continue;
if ( line.startsWith( "-- locations" ) )
{
readingLocations = true;
continue;
}
if ( line.startsWith( "-- trainline" ) )
{
readingLocations = false;
currentLine = new ScheduledLine();
currentLine.name = line.substring("-- trainline".length() ).trim();
lastStationInLine = null;
continue;
}
if ( readingLocations )
{
StationNode station = new StationNode();
// Eschborn 50.14323,8.56112
StringTokenizer tk = new StringTokenizer( line, " " );
station.name = tk.nextToken();
if ( stationMap.containsKey( station.name ) )
{
System.out.println( "skipping station name already known: " + station.name );
continue;
}
int locIdx = 0;
String loc = null;
int elev = 0;
int nconnections = 0;
while( tk.hasMoreTokens() || locIdx == 1 )
{
if ( tk.hasMoreTokens() )
{
loc = tk.nextToken();
}
StringTokenizer tloc = new StringTokenizer( loc, "," );
int ilat = (int)( ( Double.parseDouble( tloc.nextToken() ) + 90. ) *1000000. + 0.5);
int ilon = (int)( ( Double.parseDouble( tloc.nextToken() ) + 180. ) *1000000. + 0.5);
if ( locIdx == 0 )
{
station.ilat = ilat;
station.ilon = ilon;
}
else
{
OsmNodeP pos = new OsmNodeP();
pos.ilat = ilat;
pos.ilon = ilon;
OsmNodeP node = graph.matchNodeForPosition( pos,expctxWay );
if ( node != null )
{
elev += node.selev;
nconnections++;
// link station to connecting node
OsmLinkP link = new OsmLinkP( station, node );
link.descriptionBitmap = null;
station.addLink( link );
node.addLink( link );
int distance = station.calcDistance( node );
System.out.println( "matched connection for station " + station.name + " at " + distance + " meter" );
}
}
locIdx++;
}
if ( nconnections > 0 )
{
station.selev = (short)(elev / nconnections);
}
stationMap.put( station.name, station );
}
else if ( currentLine != null )
{
int idx = line.indexOf( ' ' );
String name = line.substring( 0, idx );
StationNode nextStationInLine = stationMap.get( name );
String value = line.substring( idx ).trim();
int offsetMinute = 0;
if ( lastStationInLine == null )
{
currentLine.schedule = new TrainSchedule( value );
}
else
{
if ( value.startsWith( "+") ) value = value.substring( 1 );
offsetMinute = Integer.parseInt( value );
ScheduledLink link = new ScheduledLink( lastStationInLine, nextStationInLine );
link.line = currentLine;
link.indexInLine = currentLine.offsetMinutes.size()-1;
System.out.println( "adding: " + link );
lastStationInLine.addLink( link );
}
currentLine.offsetMinutes.add( Integer.valueOf( offsetMinute ) );
lastStationInLine = nextStationInLine;
}
}
br.close();
System.out.println( "read " + stationMap.size() + " stations" );
}
}
catch( Exception e )
{
throw new RuntimeException( e );
}
}
}

View file

@ -0,0 +1,22 @@
/**
* A specific departure
* (relative to a certain station and line)
*
* @author ab
*/
package btools.memrouter;
final class ScheduledDeparture
{
long waitTime;
long rideTime;
OffsetSet offsets;
@Override
public String toString()
{
return "wait=" + waitTime + " ride=" + rideTime + " offsets=" + offsets;
}
}

View file

@ -0,0 +1,103 @@
/**
* A train line as a common set of stations with many departures
*
* @author ab
*/
package btools.memrouter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
final class ScheduledLine
{
String name;
List<Integer> offsetMinutes = new ArrayList<Integer>();
TrainSchedule schedule;
/**
* get a list of departures relative to the start-time plus
* the individual offsets according to the offset mask
*
* departures with the same wait-time are aggregated in one
* result element with multiple 1-bits in the offset mask
*
* departures with different wait-times are returned as separate items
*
* @param id the value to add to this set.
* @return true if "id" already contained in this set.
*/
public List<ScheduledDeparture> getScheduledDepartures( int idx, long timeFrom, OffsetSet offsets )
{
List<ScheduledDeparture> result = new ArrayList<ScheduledDeparture>();
long minutesFrom = (timeFrom + 59999L) / 60000L;
long timeFromCorrection = minutesFrom * 60000L - timeFrom;
if ( idx < 0 || idx >= offsetMinutes.size() -1 ) return result;
int offsetStart = offsetMinutes.get(idx).intValue();
int offsetEnd = offsetMinutes.get(idx+1).intValue();
Map<Integer,List<Integer>> waitOffsets = getDepartures( offsetStart, timeFrom + timeFromCorrection, offsets );
for( Map.Entry<Integer,List<Integer>> e : waitOffsets.entrySet() )
{
ScheduledDeparture depart = new ScheduledDeparture( );
depart.waitTime = e.getKey().intValue() * 60000L + timeFromCorrection;
depart.offsets = OffsetSet.create( e.getValue(), offsets );
depart.rideTime = (offsetEnd-offsetStart)*60000L;
result.add( depart );
}
return result;
}
private Map<Integer,List<Integer>> getDepartures( int offsetStart, long timeFrom, OffsetSet offsets )
{
Map<Integer,List<Integer>> waitOffsets = new HashMap<Integer,List<Integer>>();
int size = offsets.size();
for(int offset = 0;;)
{
// skip to next offset bit
while( offset < size && !offsets.contains( offset ) )
{
offset++;
}
if ( offset >= size ) return waitOffsets;
int toNext = schedule.getMinutesToNext( timeFrom + 60000L*(offset - offsetStart ) );
if ( toNext < 0 ) return waitOffsets;
int departOffset = offset + toNext;
// whats the closest offset within the next toNext minutes
int lastOffset = offset;
while( toNext-- >= 0 && offset < size )
{
if ( offsets.contains( offset ) )
{
lastOffset = offset;
}
offset++;
}
if ( lastOffset == size-1 ) return waitOffsets; // todo?
int waitTime = departOffset - lastOffset;
// if we have that wait time in the list, just add the offset bit
List<Integer> offsetList = waitOffsets.get( Integer.valueOf( waitTime ) );
if ( offsetList == null )
{
offsetList = new ArrayList<Integer>();
waitOffsets.put( Integer.valueOf( waitTime ), offsetList );
}
offsetList.add( Integer.valueOf( lastOffset ) );
}
}
}

View file

@ -0,0 +1,33 @@
/**
* Container for link between two Osm nodes (pre-pocessor version)
*
* @author ab
*/
package btools.memrouter;
public class ScheduledLink extends OsmLinkP
{
public ScheduledLink( StationNode source, StationNode target )
{
super( source, target );
}
public ScheduledLine line;
public int indexInLine;
public boolean isConnection()
{
return false;
}
public boolean isWayLink()
{
return false;
}
public String toString()
{
return "ScheduledLink: line=" + line.name + " indexInLine=" + indexInLine;
}
}

View file

@ -0,0 +1,421 @@
/**
* Simple Train Router
*
* @author ab
*/
package btools.memrouter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import btools.mapaccess.OsmPos;
import btools.router.OsmPathElement;
import btools.router.OsmTrack;
import btools.router.RoutingContext;
import btools.router.RoutingEngine;
import btools.util.SortedHeap;
final class ScheduledRouter
{
private GraphLoader graph;
private int solutionCount = 0;
public long linksProcessed = 0L;
public long linksReProcessed = 0L;
public long closedSkippedChained = 0L;
public long skippedChained = 0L;
private RoutingContext rc;
private RoutingEngine re;
private long time0;
private OsmNodeP start;
private OsmNodeP end;
SortedHeap<ScheduledTrip> openSet = new SortedHeap<ScheduledTrip>();
ScheduledRouter( GraphLoader graph, RoutingContext rc, RoutingEngine re )
{
this.graph = graph;
this.rc = rc;
this.re = re;
}
public OsmTrack findRoute( OsmPos startPos, OsmPos endPos, String startTime, int alternativeIdx ) throws Exception
{
OsmTrack track = null;
start = graph.matchNodeForPosition( startPos, rc.expctxWay );
if ( start == null ) throw new IllegalArgumentException( "unmatched start: " + startPos );
end = graph.matchNodeForPosition( endPos, rc.expctxWay );
if ( end == null ) throw new IllegalArgumentException( "unmatched end: " + endPos );
// SimpleDateFormat df = new SimpleDateFormat( "dd.MM.yyyy-HH:mm" );
// time0 = df.parse(startTime).getTime();
time0 = System.currentTimeMillis() + (long)(rc.starttimeoffset * 60000L );
long minutes0 = (time0 + 59999L) / 60000L;
time0 = minutes0 * 60000L;
OffsetSet finishedOffsets = OffsetSet.emptySet();
OsmLinkP startLink = new OsmLinkP( null, start );
ScheduledTrip startTrip = new ScheduledTrip( OffsetSet.fullSet(), startLink, null, null );
openSet.add( 0, startTrip );
for(;;)
{
if ( re.isTerminated() )
{
throw new RuntimeException( "operation terminated" );
}
if ( linksProcessed + linksReProcessed > 5000000 )
{
throw new RuntimeException( "5 Million links limit reached" );
}
// get cheapest trip from heap
ScheduledTrip trip = openSet.popLowestKeyValue();
if ( trip == null )
{
break;
}
OsmLinkP currentLink = trip.link;
OsmNodeP currentNode = trip.getTargetNode();
if ( currentNode == null )
{
System.out.println( "ups: " + trip );
continue;
}
if ( currentLink.isVirgin() )
{
linksProcessed++;
}
else
{
linksReProcessed++;
}
// check global closure
OffsetSet offsets = finishedOffsets.filter( trip.offsets );
if ( offsets == null ) continue;
// check local closure for links:
offsets = currentLink.filterAndClose( offsets, trip.arrival, currentLink instanceof ScheduledLink );
if ( offsets == null ) continue;
// check for arrival
if ( currentNode == end )
{
for( int offset = 0; offset<trip.offsets.size(); offset++ )
{
if ( trip.offsets.contains( offset ) )
{
track = compileTrip( trip, offset );
System.out.println( "---- begin route ------ (cost " + track.cost + ")" );
for( String s : track.iternity ) System.out.println( s );
System.out.println( "---- end route ------" );
break; // + plus more offsets..
}
}
finishedOffsets = finishedOffsets.add( offsets );
if ( solutionCount++ >= alternativeIdx ) return track;
}
for( OsmLinkP link = currentNode.getFirstLink(); link != null; link = link.getNext( currentNode ) )
{
addNextTripsForLink(trip, currentNode, currentLink, link, offsets, 0 );
}
}
return track;
}
private void addToOpenSet( ScheduledTrip nextTrip )
{
int distance = nextTrip.getTargetNode().calcDistance( end );
nextTrip.adjustedCost = nextTrip.cost + (int)(distance * rc.pass1coefficient + 0.5);
openSet.add( nextTrip.adjustedCost, nextTrip );
}
private void addNextTripsForLink( ScheduledTrip trip, OsmNodeP currentNode, OsmLinkP currentLink, OsmLinkP link, OffsetSet offsets, int level )
{
if ( link == currentLink )
{
return; // just reverse, ignore
}
OsmNodeP node = link.getTarget(currentNode);
if ( node == null )
{
System.out.println( "ups2: " + link );
return;
}
// calc distance and check nogos
rc.nogomatch = false;
int distance = rc.calcDistance( currentNode.ilon, currentNode.ilat, node.ilon, node.ilat );
if ( rc.nogomatch )
{
return;
}
if ( link instanceof ScheduledLink )
{
// System.out.println( "next trip for link: " + link + " at offset " + offsets );
ScheduledLink slink = (ScheduledLink)link;
ScheduledLine line = slink.line;
// line change delay
long delay = 0L;
if ( currentLink instanceof ScheduledLink )
{
delay = ((ScheduledLink)currentLink).line == line ? 0L : (long)(rc.changetime * 1000.); // 3 minutes
}
long changePenalty = delay > 0 ? 60000L : 0L;
List<ScheduledDeparture> nextDepartures = line.getScheduledDepartures( slink.indexInLine, time0 + trip.arrival + delay, offsets );
for( ScheduledDeparture nextDeparture : nextDepartures )
{
ScheduledTrip nextTrip = new ScheduledTrip( nextDeparture.offsets, link, currentNode, trip );
long waitTime = nextDeparture.waitTime + delay;
long rideTime = nextDeparture.rideTime;
nextTrip.cost = trip.cost + (int)( ( rideTime + changePenalty + waitTime*rc.waittimeadjustment ) * rc.cost1speed / 3600. ); // 160ms / meter = 22km/h
nextTrip.departure = trip.arrival + waitTime;
nextTrip.arrival = nextTrip.departure + rideTime;
addToOpenSet( nextTrip );
// System.out.println( "found: " + nextTrip );
}
}
else if ( link.isWayLink() )
{
// get costfactor
rc.expctxWay.evaluate( link.isReverse(currentNode), link.descriptionBitmap, null );
// *** penalty for distance
float costfactor = rc.expctxWay.getCostfactor();
if ( costfactor > 9999. )
{
return;
}
int waycost = (int)(distance * costfactor + 0.5f);
// *** add initial cost if factor changed
float costdiff = costfactor - trip.lastcostfactor;
if ( costdiff > 0.0005 || costdiff < -0.0005 )
{
waycost += (int)rc.expctxWay.getInitialcost();
}
if ( node.getNodeDecsription() != null )
{
rc.expctxNode.evaluate( rc.expctxWay.getNodeAccessGranted() != 0. , node.getNodeDecsription(), null );
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
return;
}
waycost += (int)initialcost;
}
// *** penalty for turning angles
if ( trip.originNode != null )
{
// penalty proportional to direction change
double cos = rc.calcCosAngle( trip.originNode.ilon, trip.originNode.ilat, currentNode.ilon, currentNode.ilat, node.ilon, node.ilat );
int turncost = (int)(cos * rc.expctxWay.getTurncost() + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
waycost += turncost;
}
ScheduledTrip nextTrip = new ScheduledTrip( offsets, link, currentNode, trip );
// *** penalty for elevation
short ele2 = node.selev;
short ele1 = trip.selev;
int elefactor = 250000;
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
nextTrip.selev = ele2;
if ( ele1 != Short.MIN_VALUE )
{
nextTrip.ehbd = trip.ehbd + (ele1 - ele2)*elefactor - distance * rc.downhillcutoff;
nextTrip.ehbu = trip.ehbu + (ele2 - ele1)*elefactor - distance * rc.uphillcutoff;
}
if ( nextTrip.ehbd > rc.elevationpenaltybuffer )
{
int excess = nextTrip.ehbd - rc.elevationpenaltybuffer;
int reduce = distance * rc.elevationbufferreduce;
if ( reduce > excess )
{
reduce = excess;
}
excess = nextTrip.ehbd - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
nextTrip.ehbd -= reduce;
if ( rc.downhillcostdiv > 0 )
{
int elevationCost = reduce/rc.downhillcostdiv;
waycost += elevationCost;
}
}
else if ( nextTrip.ehbd < 0 )
{
nextTrip.ehbd = 0;
}
if ( nextTrip.ehbu > rc.elevationpenaltybuffer )
{
int excess = nextTrip.ehbu - rc.elevationpenaltybuffer;
int reduce = distance * rc.elevationbufferreduce;
if ( reduce > excess )
{
reduce = excess;
}
excess = nextTrip.ehbu - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
nextTrip.ehbu -= reduce;
if ( rc.uphillcostdiv > 0 )
{
int elevationCost = reduce/rc.uphillcostdiv;
waycost += elevationCost;
}
}
else if ( nextTrip.ehbu < 0 )
{
nextTrip.ehbu = 0;
}
nextTrip.lastcostfactor = costfactor;
nextTrip.cost = trip.cost + (int)(waycost*rc.additionalcostfactor + 0.5);
nextTrip.departure = trip.arrival;
nextTrip.arrival = nextTrip.departure + (long) ( waycost * 3600. / rc.cost1speed ); // 160ms / meter = 22km/h
addToOpenSet( nextTrip );
}
else // connecting link
{
ScheduledTrip nextTrip = new ScheduledTrip( offsets, link, currentNode, trip );
long delay = (long)(rc.buffertime * 1000.); // 2 min
nextTrip.cost = trip.cost + (int)( delay*rc.waittimeadjustment * rc.cost1speed / 3600. );
nextTrip.departure = trip.arrival;
nextTrip.arrival = nextTrip.departure + delay;
addToOpenSet( nextTrip );
}
}
private OsmTrack compileTrip( ScheduledTrip trip, int offset )
{
OsmTrack track = new OsmTrack();
track.iternity = new ArrayList<String>();
ScheduledTrip current = trip;
ScheduledLine lastLine = new ScheduledLine();
ScheduledLine dummyLine = new ScheduledLine();
List<ScheduledTrip> list = new ArrayList<ScheduledTrip>();
int distance = 0;
ScheduledTrip itrip = null;
String profile = extractProfile( rc.localFunction );
OsmNodeP nextNode = null;
while( current != null )
{
System.out.println( "trip=" + current );
OsmNodeP node = current.getTargetNode();
OsmPathElement pe = new OsmPathElement(node.ilon, node.ilat, node.selev, null );
track.addNode(pe);
if ( nextNode != null )
{
distance += node.calcDistance( nextNode );
}
boolean isScheduled = current.link instanceof ScheduledLink;
boolean isConnection = current.link.descriptionBitmap == null && !isScheduled;
ScheduledLine line = isScheduled ? ((ScheduledLink)current.link).line : isConnection ? dummyLine : null;
if ( line != lastLine && !isConnection )
{
itrip = new ScheduledTrip();
itrip.departure = current.departure;
itrip.arrival = current.arrival;
itrip.originNode = current.originNode;
itrip.link = current.link;
if ( isScheduled && list.size() > 0 )
{
list.get( list.size()-1 ).originNode = current.getTargetNode();
}
list.add(itrip);
}
else if ( itrip != null && !isConnection )
{
itrip.departure = current.departure;
itrip.originNode = current.originNode;
}
lastLine = line;
current = current.origin;
nextNode = node;
}
track.distance = distance;
track.cost = trip.cost;
for( int i=list.size()-1; i>=0; i-- )
{
current = list.get(i);
String lineName = profile;
boolean isScheduled = current.link instanceof ScheduledLink;
if ( isScheduled )
{
lineName = ((ScheduledLink)current.link).line.name;
}
String stationName = "*position*";
if ( current.originNode instanceof StationNode )
{
stationName = ((StationNode)current.originNode).name;
}
String nextStationName = "*position*";
if ( i > 0 && list.get(i-1).originNode instanceof StationNode )
{
nextStationName = ((StationNode)list.get(i-1).originNode).name;
}
{
Date d0 = new Date( time0 + 60000L * offset + current.departure );
Date d1 = new Date( time0 + 60000L * offset + current.arrival );
if ( track.iternity.size() > 0 ) track.iternity.add( "" );
track.iternity.add( "depart: " + d0 + " " + stationName );
track.iternity.add( " --- " + lineName + " ---" );
track.iternity.add( "arrive: " + d1 + " " + nextStationName );
}
}
return track;
}
private String extractProfile( String s )
{
int idx = s.lastIndexOf( '/' );
if ( idx >= 0 ) s = s.substring( idx+1 );
idx = s.indexOf( '.' );
if ( idx >= 0 ) s = s.substring( 0,idx );
return s;
}
}

View file

@ -0,0 +1,64 @@
/**
* Simple Train Router
*
* @author ab
*/
package btools.memrouter;
public final class ScheduledTrip
{
public OffsetSet offsets;
ScheduledTrip origin;
OsmLinkP link;
OsmNodeP originNode;
int cost; // in meter!
int adjustedCost; // in meter!
long arrival; // in millis!
long departure; // in millis!
public float lastcostfactor;
public int ehbd; // in micrometer
public int ehbu; // in micrometer
public short selev = Short.MIN_VALUE;
ScheduledTrip()
{
// dummy for OpenSetM
}
ScheduledTrip( OffsetSet offsets, OsmLinkP link, OsmNodeP originNode, ScheduledTrip origin )
{
this.offsets = offsets;
this.link = link;
this.origin = origin;
this.originNode = originNode;
}
public OsmNodeP getTargetNode()
{
return link.getTarget(originNode);
}
@Override
public String toString()
{
String prefix = "PlainLink";
if ( link instanceof ScheduledLink )
{
ScheduledLink l = (ScheduledLink)link;
ScheduledLine line = l.line;
prefix = "ScheduledLink: line=" + line.name;
}
else if ( link.isConnection() )
{
prefix = "ConnectingLink";
}
return prefix + " depart=" + departure + " arrival=" + arrival + " cost=" + cost + " offsets=" + offsets;
}
}

View file

@ -0,0 +1,25 @@
/**
* A train station and it's connections
*
* @author ab
*/
package btools.memrouter;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeSet;
final class StationNode extends OsmNodeP
{
String name;
}

View file

@ -0,0 +1,146 @@
/**
* Information on matched way point
*
* @author ab
*/
package btools.memrouter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
final class TrainSchedule
{
private static class TrainScheduleCron
{
long minutes;
long hours;
long dows;
boolean isNegative;
String cronSource;
TrainScheduleCron( String value )
{
StringTokenizer tk = new StringTokenizer( value, "_" );
minutes = parseElement( tk.nextToken() );
hours = parseElement( tk.nextToken() );
dows = parseElement( tk.nextToken() );
cronSource = value;
}
private long parseElement( String s )
{
if ( "*".equals( s ) ) return Long.MAX_VALUE;
StringTokenizer tk = new StringTokenizer( s, "," );
long res = 0;
while( tk.hasMoreTokens() )
{
String sub = tk.nextToken();
int start, end;
int idx = sub.indexOf( '-' );
if ( idx < 0 )
{
start = Integer.parseInt( sub );
end = start;
}
else
{
start = Integer.parseInt( sub.substring( 0, idx ) );
end = Integer.parseInt( sub.substring( idx + 1) );
}
for( int i=start; i <= end ; i++ )
{
res |= (1L<<i);
}
}
return res;
}
boolean matches( int minute, int hour, int dow )
{
return ( (1L << minute) & minutes ) != 0
&& ( (1L << hour) & hours ) != 0
&& ( (1L << dow) & dows ) != 0;
}
}
private List<TrainScheduleCron> cronsPositive;
private List<TrainScheduleCron> cronsNegative;
public TrainSchedule( String cronstring )
{
StringTokenizer tk = new StringTokenizer( cronstring, " " );
cronsPositive = new ArrayList<TrainScheduleCron>();
cronsNegative = new ArrayList<TrainScheduleCron>();
while ( tk.hasMoreTokens() )
{
String sign = tk.nextToken();
String value = tk.nextToken();
TrainScheduleCron cron = new TrainScheduleCron( value );
if ( "+".equals( sign ) )
{
cronsPositive.add( cron );
}
else if ( "-".equals( sign ) )
{
cronsNegative.add( cron );
}
else throw new IllegalArgumentException( "invalid cron sign: " + sign );
}
}
public int getMinutesToNext( long timeFrom )
{
Calendar cal = Calendar.getInstance();
cal.setTime( new Date( timeFrom ) );
int minute = cal.get( Calendar.MINUTE );
int hour = cal.get( Calendar.HOUR_OF_DAY );
int dow = cal.get( Calendar.DAY_OF_WEEK );
dow = dow > 1 ? dow -1 : dow+6;
for( int cnt=0; cnt < 10080; cnt++ )
{
boolean veto = false;
for( TrainScheduleCron cron : cronsNegative )
{
if ( cron.matches( minute, hour, dow ) )
{
veto = true;
break;
}
}
if ( !veto )
{
for( TrainScheduleCron cron : cronsPositive )
{
if ( cron.matches( minute, hour, dow ) ) return cnt;
}
}
if ( ++minute == 60 )
{
minute = 0;
if ( ++hour == 24 )
{
hour = 0;
if ( ++dow == 8 )
{
dow = 1;
}
}
}
}
return -1;
}
}

View file

@ -0,0 +1,41 @@
package btools.memrouter;
import java.util.Random;
import java.util.HashMap;
import org.junit.Assert;
import org.junit.Test;
import java.net.URL;
import java.io.File;
import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
public class MemrouterTest
{
@Test
public void memrouterTest() throws Exception
{
URL dummyurl = this.getClass().getResource( "/dummy.txt" );
Assert.assertTrue( "dummy.txt not found", dummyurl != null );
File workingDir = new File(dummyurl.getFile()).getParentFile();
File profileDir = new File( workingDir, "/../../../misc/profiles2" );
File dataDir = new File( workingDir, "/../../../brouter-map-creator/target/test-classes/tmp" );
File lookupFile = new File( profileDir, "lookups.dat" );
File profileFile = new File( profileDir, "trekking.brf" );
File waytiles55 = new File( dataDir, "waytiles55" );
File unodes55 = new File( dataDir, "unodes55" );
File[] fahrplanFiles = new File[1];
fahrplanFiles[0] = new File( workingDir, "fahrplan.txt" );
// read lookup + profile for lookup-version + access-filter
BExpressionMetaData meta = new BExpressionMetaData();
BExpressionContextWay expctxWay = new BExpressionContextWay(meta);
meta.readMetaData( lookupFile );
expctxWay.parseFile( profileFile, "global" );
// run GraphLoader
new GraphLoader().process( unodes55, waytiles55, fahrplanFiles, expctxWay );
}
}

View file

@ -0,0 +1,546 @@
-- locations
KoblenzHbf 50.35066,7.58997
Frankfurt(M)Hbf 50.10676,8.66323 50.10754,8.66107 50.10596,8.66448 50.10765,8.66566
Frankfurt/Hoechst 50.10271,8.54212 50.10363,8.54240 50.10186,8.54298
Bensheim-Auerbach 49.70216,8.61371
Bensheim 49.68108,8.61724
F-Niederrad 50.08096,8.63768
Walldorf 50.00159,8.58155
Moerfelden 49.97940,8.56534
Gross-G-Dornberg 49.91241,8.49420
Riedstatt-Goddelau 49.83462,8.49087
Stockstadt-Rhein 49.80906,8.47207
Biebesheim 49.78091,8.47350
Gernsheim 49.75359,8.49062
Gross-Rohrheim 49.71415,8.47826
Biblis 49.68821,8.45130
Buerstadt 49.64535,8.45888
Lampertheim 49.59978,8.47715
Mannheim-Waldhof 49.52538,8.48217
MannheimHbf 49.47983,8.47003
WiesbadenHbf 50.07118,8.24377
MainzHbf 50.00159,8.25939
Mainz-Roemisches-Th 49.99350,8.27852
Mainz-Bischofsheim 49.99228,8.35835
Nauheim 49.94192,8.45068
Gross-Gerau 49.92421,8.48589
Klein-Gerau 49.92004,8.51734
Weiterstadt 49.91027,8.57899
DarmstadtHbf 49.87254,8.63142
Darmstadt-Sued 49.85558,8.63703
Wiesbaden-Ost 50.04053,8.25616
Mainz-Nord 50.01062,8.24626
Mainz-Gustavsburg 49.99454,8.31414
Ruesselsheim-Opel 49.98780,8.40201
Ruesselsheim 49.99205,8.41375
Raunheim 50.01000,8.45704
Kelsterbach 50.06329,8.53006
F-Stadion 50.06778,8.63561
Bad-Soden 50.14283,8.50455 50.14339,8.50457 50.14308,8.50359
Sulzbach-Nord 50.13905,8.51788 50.13812,8.51823
Schwalbach-Limes 50.15444,8.52819 50.15485,8.52801
Schwalbach-Nord 50.15976,8.53459 50.15965,8.53487
Niederhoechsstadt 50.15439,8.54707 50.15476,8.54627
Eschborn 50.14371,8.56076 50.14323,8.56112
Eschborn-Sued 50.13352,8.57863 50.13327,8.57807 50.13441,8.57850
Frankfurt-Roedelheim 50.12457,8.60708 50.12431,8.60628 50.12473,8.60800
Frankfurt-West 50.11885,8.63981 50.11876,8.63932
Frankfurt-Messe 50.11190,8.64354 50.11271,8.64371
Frankfurt-Galluswarte 50.10384,8.64487 50.10384,8.64487
F-Taunusanlage 50.11325,8.66906
F-Hauptwache 50.11393,8.67835
F-Konstablerwache 50.11423,8.68621
F-Ostendstrasse 50.11226,8.69710
F-Lokalbahnhof 50.10198,8.69294
Frankfurt-Süd 50.09907,8.68627
F-Stresemannallee 50.09455,8.67173
F-Louisa 50.08320,8.66947
Neu-Isenburg 50.05303,8.66450
Dreieich-Buchschlag 50.02230,8.66256
Langen-Flugsicherung 50.00559,8.65843
Langen 49.99373,8.65786
Egelsbach 49.96785,8.65403
Erzhausen 49.95128,8.65086
D-Wixhausen 49.92889,8.64724
D-Arheiligen 49.91424,8.64625
F-ZusckschwerdStr 50.10290,8.55181
F-Bolognaropalast 50.10140,8.55297
F-TillyStr 50.10173,8.56031
F-NiedKirche 50.09823,8.56726
F-LuthmerStr 50.09907,8.57265
F-BirminghamStr 50.10009,8.57790
F-Jaegerallee 50.10024,8.59112
F-Linnegraben 50.10017,8.59781
F-WaldschulStr 50.10061,8.60305
F-MoenchhofStr 50.10079,8.61903
F-WickererStr 50.10057,8.62240
F-RebstoeckerStr 50.10154,8.62866
F-SchwalbacherStr 50.10235,8.63628
F-SpeyererStr 50.10542,8.65043
F-Gueterplatz 50.10751,8.65557
F-PlatzDerRepublik 50.10929,8.66073
Kronberg 50.17983,8.51726 50.17949,8.51744
Kronberg-Sued 50.17289,8.52965 50.17368,8.52843
Sulzbach(Taunus) 50.12957,8.52836 50.13012,8.52812
Weisskirchen-Steinb 50.17346,8.58782
Stierstadt 50.18486,8.58486
Oberursel 50.19853,8.58769
Bad-Homburg 50.21945,8.62057
Seulberg 50.24286,8.64559
Friedrichsdorf 50.25200,8.64432
Diez-Ost 50.37614,8.03851 50.37512,8.03780
Staffel 50.39929,8.04620 50.39838,8.04605
Elz 50.41450,8.03848 50.41490,8.03826
Niederhadamar 50.43309,8.03737 50.43340,8.03737
Hadamar 50.44710,8.04351 50.44771,8.04416
Niederzeuzheim 50.46802,8.03966 50.46880,8.03954
Frickofen 50.50485,8.03060 50.50485,8.03002
Wilsenroth 50.53191,8.03165 50.53076,8.03371
Wilmenroth 50.53940,7.98573 50.53940,7.98477
Westerburg 50.55747,7.96709 50.55674,7.96668
Langenhahn 50.58733,7.91494 50.58761,7.91676
Elz-Sued 50.40851,8.03211 50.40845,8.03290
Niedererbach 50.42488,7.97713
Dreikirchen 50.44986,7.95879
Steinefrenz 50.44778,7.93970,50.44737,7.94294
Girod 50.45439,7.90259
Goldhausen 50.46131,7.87345 50.46185,7.87268
Montabaur 50.44479,7.82520 50.44415,7.82538 50.44504,7.83017
Dernbach 50.45672,7.78386
Wirges 50.46728,7.78485
Sirshahn 50.48593,7.77162 50.48597,7.77052
Limburg(Lahn) 50.38426,8.06296 50.38495,8.06215
Eschofen 50.39337,8.10427 50.39396,8.10594
Lindenholzhausen 50.37840,8.13335 50.37768,8.13321
Niederbrechen 50.35957,8.15976 50.36031,8.15854
Oberbrechen 50.35454,8.18765 50.35524,8.18722
Niederselters 50.33249,8.22995 50.33283,8.23066
Bad-Camberg 50.29644,8.25516 50.29597,8.25585
Woersdorf 50.24550,8.24926 50.24614,8.24910
Idstein 50.21591,8.25755 50.21665,8.25795
Niedernhausen 50.15972,8.31272 50.16053,8.31281
Hofheim 50.08406,8.44493 50.08502,8.44549
Unterliederbach 50.10796,8.52798 50.10842,8.52797
Liederbach-Sued 50.11538,8.50159 50.11571,8.50118
Liederbach 50.11861,8.48631 50.11823,8.48802
Kelkheim-Muenster 50.12477,8.46183 50.12517,8.46202
Kelkheim 50.13694,8.44781 50.13730,8.44812
Kelkheim-Hornau 50.14692,8.44459 50.14770,8.44509
Schneidhain 50.17261,8.45209 50.17245,8.45160
Koenigstein 50.17758,8.46892 50.17738,8.46989
FfmFlughfFbf 50.05297,8.57086 50.05202,8.57030
Limburg-Sued 50.38241,8.09621 50.38283,8.09445 50.38349,8.09597
Siegburg-Bonn 50.79377,7.20269 50.79446,7.20331
Koeln 50.94299,6.95907 50.94220,6.95798
Kerkerbach 50.40172,8.13610
Runkel 50.40502,8.15961
Villmar 50.39477,8.18734
Arfurt(Lahn) 50.40644,8.21076
Aumenau 50.39990,8.24952
Fürfurt 50.42938,8.25411
Gräveneck 50.45194,8.25117
Weilburg 50.48731,8.26838
Löhnberg 50.50920,8.27367
Stockhausen(Lahn) 50.54039,8.32492
Leun/Braunfels 50.53957,8.36628
Solms 50.54520,8.40752
Albshausen 50.54561,8.43360
Wetzlar 50.56509,8.50360
Dutenhofen(Wetzlar) 50.56379,8.59979
Gießen 50.57804,8.66280
DA-RheinNeckarStrasse 49.87183,8.64463
DA-EscholbrueckerStr 49.86537,8.64717
DA-PrinzEmilGarten 49.86154,8.64708
DA-BessungerStr 49.85836,8.64698
DA-LandskronenStr 49.85335,8.64668
DA-Marienhoehe 49.84355,8.64609
Eberstadt-FriedrichEbert 49.83855,8.64559
Eberstadt-CarlUlrich 49.83321,8.64496
Eberstadt-VonKetteler 49.82911,8.64520
Eberstadt-KatharinenStr 49.82476,8.64482
Eberstadt-Wartehalle 49.82027,8.64411
Eberstadt-Modaubruecke 49.81739,8.64425
Eberstadt-Kirche 49.81429,8.64600
Eberstadt-Friedhof 49.81108,8.64603
Eberstadt-Frankenstein 49.80744,8.64542
Eberstadt-Mittelschneise 49.80234,8.64689
Malchen-Seeheim 49.79020,8.64875
Seeheim-Wingert 49.77274,8.64814
Seeheim-NeuesRathaus 49.76666,8.64623
Seeheim-Tannenberg 49.76287,8.64482
Jugenheim-LudwigStr 49.75676,8.63487
Jugenheim-Bickenbacher 49.75357,8.62937
Alsbach-Beuneweg 49.74641,8.62061
Alsbach-Hikelstein 49.74028,8.61432
-- trainline STR11
Frankfurt(M)Hbf + 4,14,24,34,44,54_6-22_*
F-PlatzDerRepublik +2
F-Gueterplatz +4
F-SpeyererStr +5
Frankfurt-Galluswarte +7
F-SchwalbacherStr +9
F-RebstoeckerStr +11
F-WickererStr +12
F-MoenchhofStr +13
F-WaldschulStr +15
F-Linnegraben +16
F-Jaegerallee +17
F-BirminghamStr +19
F-LuthmerStr +20
F-NiedKirche +22
F-TillyStr +24
F-ZusckschwerdStr +26
-- trainline STR11
F-ZusckschwerdStr + 0,10,20,30,40,50_6-22_*
F-Bolognaropalast +1
F-TillyStr +3
F-NiedKirche +4
F-LuthmerStr +5
F-BirminghamStr +7
F-Jaegerallee +9
F-Linnegraben +10
F-WaldschulStr +11
F-MoenchhofStr +13
F-WickererStr +14
F-RebstoeckerStr +15
F-SchwalbacherStr +17
Frankfurt-Galluswarte +19
F-SpeyererStr +20
F-Gueterplatz +22
F-PlatzDerRepublik +24
Frankfurt(M)Hbf +26
-- trainline S3
Bad-Soden + 20,50_0,5-23_*
Sulzbach-Nord +2
Schwalbach-Limes +5
Schwalbach-Nord +6
Niederhoechsstadt +9
Eschborn +11
Eschborn-Sued +13
Frankfurt-Roedelheim +17
Frankfurt-West +20
Frankfurt-Messe +22
Frankfurt-Galluswarte +24
Frankfurt(M)Hbf +26
F-Taunusanlage +28
F-Hauptwache +30
F-Konstablerwache +31
F-Ostendstrasse +33
F-Lokalbahnhof +35
Frankfurt-Süd +37
F-Stresemannallee +39
F-Louisa +41
Neu-Isenburg +44
Dreieich-Buchschlag +47
Langen-Flugsicherung +49
Langen +51
Egelsbach +54
Erzhausen +56
D-Wixhausen +58
D-Arheiligen +61
DarmstadtHbf +64
-- trainline S4
Kronberg + 8,38_0,5-23_*
Kronberg-Sued +2
Niederhoechsstadt +6
Eschborn +8
Eschborn-Sued +10
Frankfurt-Roedelheim +14
Frankfurt-West +17
Frankfurt-Messe +19
Frankfurt-Galluswarte +21
Frankfurt(M)Hbf +23
F-Taunusanlage +25
F-Hauptwache +27
F-Konstablerwache +28
F-Ostendstrasse +30
F-Lokalbahnhof +32
Frankfurt-Süd +34
F-Stresemannallee +36
F-Louisa +38
Neu-Isenburg +41
Dreieich-Buchschlag +44
Langen-Flugsicherung +46
Langen +48
-- trainline S3
Frankfurt(M)Hbf + 14,44_0,5-23_*
Frankfurt-Galluswarte +3
Frankfurt-Messe +4
Frankfurt-West +6
Frankfurt-Roedelheim +9
Eschborn-Sued +12
Eschborn +15
Bad-Soden +26
-- trainline S4
Frankfurt(M)Hbf + 29,59_0,5-23_*
Frankfurt-Galluswarte +3
Frankfurt-Messe +4
Frankfurt-West +6
Frankfurt-Roedelheim +9
Eschborn-Sued +12
Eschborn +15
Niederhoechsstadt +17
Kronberg-Sued +20
Kronberg +22
-- trainline S5
Frankfurt(M)Hbf + 24,54_0,5-23_*
Frankfurt-Galluswarte +3
Frankfurt-Messe +4
Frankfurt-West +6
Frankfurt-Roedelheim +9
Weisskirchen-Steinb +13
Stierstadt +15
Oberursel +18
Bad-Homburg +21
Seulberg +25
Friedrichsdorf +26
-- trainline S5
Friedrichsdorf + 8,38_0,5-23_*
Seulberg +3
Bad-Homburg +7
Oberursel +11
Stierstadt +13
Weisskirchen-Steinb +15
Frankfurt-Roedelheim +19
Frankfurt-West +22
Frankfurt-Messe +24
Frankfurt-Galluswarte +26
Frankfurt(M)Hbf +29
-- trainline S8
WiesbadenHbf + 19,49_0,5-23_*
Wiesbaden-Ost +4
Mainz-Nord +8
MainzHbf +14
Mainz-Roemisches-Th +17
Mainz-Gustavsburg +20
Mainz-Bischofsheim +23
Ruesselsheim-Opel +26
Ruesselsheim +29
Raunheim +32
Kelsterbach +37
FfmFlughfFbf +43
F-Stadion +47
F-Niederrad +50
Frankfurt(M)Hbf +54
-- trainline HLB
Frankfurt/Hoechst + 28,58_6-23_*
Frankfurt(M)Hbf +11
-- trainline HLB
Frankfurt(M)Hbf + 17,47_6-23_*
Frankfurt/Hoechst +12
Unterliederbach +14
Liederbach-Sued +16
Liederbach +18
Kelkheim-Muenster +21
Kelkheim +25
Kelkheim-Hornau +27
Schneidhain +31
Koenigstein +35
-- trainline HLB
Frankfurt/Hoechst + 0,30_5-20_* - 0_9-15_*
Sulzbach(Taunus) +6
Bad-Soden +9
-- trainline HLB
Bad-Soden + 14,44_5-20_* - 14_9-15_*
Sulzbach(Taunus) +3
Frankfurt/Hoechst +9
-- trainline SE60
Frankfurt(M)Hbf + 6_0,5-23_*
DarmstadtHbf +24
Darmstadt-Sued +27
Bensheim-Auerbach +50
Bensheim +53
-- trainline SE60
Bensheim + 0_0,5-23_*
Bensheim-Auerbach +3
DarmstadtHbf +30
Frankfurt(M)Hbf +48
-- trainline RE60
Frankfurt(M)Hbf + 34_6,8,12,14,16,18,20_1-5
Langen +10
DarmstadtHbf +20
#Bickenbach +29
Bensheim +34
-- trainline RB15707
WiesbadenHbf + 38_6-23_*
MainzHbf +11
Mainz-Roemisches-Th +14
Mainz-Bischofsheim +19
Nauheim +26
Gross-Gerau +29
Klein-Gerau +32
Weiterstadt +36
DarmstadtHbf +43
-- trainline RE50
Frankfurt(M)Hbf + 10_6-23_*
F-Niederrad +6
Walldorf +15
Moerfelden +18
Gross-G-Dornberg +24
Riedstatt-Goddelau +30
Stockstadt-Rhein +33
Biebesheim +37
Gernsheim +41
Gross-Rohrheim +45
Biblis +48
Buerstadt +53
Lampertheim +57
Mannheim-Waldhof +62
MannheimHbf +69
-- trainline Vectus
Frickofen + 49_6,7,9,11,13,15,17,19_* + 19_6_* + 13_9_* + 6_15_*
Niederzeuzheim +6
Hadamar +4
Niederhadamar +13
Elz +16
Staffel +19
Diez-Ost +23
Limburg(Lahn) +26
-- trainline Vectus
Limburg(Lahn) + 8_8,10,12,14,16,18,20_*
Diez-Ost +3
Staffel +8
Elz +11
Niederhadamar +14
Hadamar +16
Niederzeuzheim +20
Frickofen +26
Wilsenroth +31
Wilmenroth +36
Westerburg +42
Langenhahn +49
-- trainline Vectus
Limburg(Lahn) + 54_7,9,10,11,12,13,14,15,16,17,18,19,20_*
Diez-Ost +4
Staffel +7
Elz-Sued +10
Niedererbach +17
Dreikirchen +23
Steinefrenz +26
Girod +30
Goldhausen +38
Montabaur +46
Dernbach +53
Wirges +55
Sirshahn +59
-- trainline Vectus
Sirshahn + 9_7,9,10,11,12,13,14,15,16,17,18,19,20_*
Wirges +4
Dernbach +7
Montabaur +14
Goldhausen +21
Girod +25
Steinefrenz +29
Dreikirchen +32
Niedererbach +38
Elz-Sued +45
Staffel +48
Diez-Ost +52
Limburg(Lahn) +55
-- trainline SE10
Limburg(Lahn) + 18_7-21_*
Eschofen +4
Lindenholzhausen +8
Niederbrechen +12
Oberbrechen +15
Niederselters +19
Bad-Camberg +24
Woersdorf +29
Idstein +33
Niedernhausen +40
Hofheim +52
Frankfurt/Hoechst +59
Frankfurt(M)Hbf +70
-- trainline HLB
Limburg(Lahn) + 23_7-21_*
Eschofen +4
Kerkerbach +7
Runkel +10
Villmar +14
Arfurt(Lahn) +19
Aumenau +23
Fürfurt +27
Gräveneck +31
Weilburg +37
Löhnberg +41
Stockhausen(Lahn) +46
Leun/Braunfels +50
Solms +54
Albshausen +57
Wetzlar +63
Dutenhofen(Wetzlar) +69
Gießen +75
-- trainline SE10
Frankfurt(M)Hbf + 31_7-22_*
Frankfurt/Hoechst +10
Hofheim +18
Niedernhausen +30
Idstein +37
Woersdorf +41
Bad-Camberg +46
Niederselters +50
Oberbrechen +54
Niederbrechen +57
Lindenholzhausen +61
Eschofen +65
Limburg(Lahn) +69
-- trainline RE1530x
Limburg(Lahn) + 55_6,7_*
Eschofen +4
Frankfurt/Hoechst +52
Frankfurt(M)Hbf +63
-- trainline STR8
DA-RheinNeckarStrasse + 16,46_6-23_*
DA-EscholbrueckerStr +2
DA-PrinzEmilGarten +3
DA-BessungerStr +4
DA-LandskronenStr +6
DA-Marienhoehe +8
Eberstadt-FriedrichEbert +9
Eberstadt-CarlUlrich +10
Eberstadt-VonKetteler +12
Eberstadt-KatharinenStr +13
Eberstadt-Wartehalle +14
Eberstadt-Modaubruecke +16
Eberstadt-Kirche +17
Eberstadt-Friedhof +18
Eberstadt-Frankenstein +20
Eberstadt-Mittelschneise +21
Malchen-Seeheim +23
Seeheim-Wingert +26
Seeheim-NeuesRathaus +28
Seeheim-Tannenberg +29
Jugenheim-LudwigStr +31
Jugenheim-Bickenbacher +32
Alsbach-Beuneweg +34
Alsbach-Hikelstein +36