schedule-router proptotype
This commit is contained in:
parent
0316c41924
commit
55f8e7fb4a
18 changed files with 2359 additions and 0 deletions
45
brouter-mem-router/pom.xml
Normal file
45
brouter-mem-router/pom.xml
Normal 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>
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
145
brouter-mem-router/src/main/java/btools/memrouter/OffsetSet.java
Normal file
145
brouter-mem-router/src/main/java/btools/memrouter/OffsetSet.java
Normal 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Set off departure offsets (immutable)
|
||||
*
|
||||
* @author ab
|
||||
*/
|
||||
package btools.memrouter;
|
||||
|
||||
|
||||
public interface OffsetSetHolder
|
||||
{
|
||||
OffsetSet getOffsetSet();
|
||||
void setOffsetSet( OffsetSet offsetSet );
|
||||
}
|
157
brouter-mem-router/src/main/java/btools/memrouter/OsmLinkP.java
Normal file
157
brouter-mem-router/src/main/java/btools/memrouter/OsmLinkP.java
Normal 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" );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
150
brouter-mem-router/src/main/java/btools/memrouter/OsmNodeP.java
Normal file
150
brouter-mem-router/src/main/java/btools/memrouter/OsmNodeP.java
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
0
brouter-mem-router/src/test/resources/dummy.txt
Normal file
0
brouter-mem-router/src/test/resources/dummy.txt
Normal file
546
brouter-mem-router/src/test/resources/fahrplan.txt
Normal file
546
brouter-mem-router/src/test/resources/fahrplan.txt
Normal 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
|
Loading…
Reference in a new issue