package btools.mapaccess; import java.util.List; import btools.codec.WaypointMatcher; import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNodePairSet; import btools.util.CheapRuler; /** * the WaypointMatcher is feeded by the decoder with geoemtries of ways that are * already check for allowed access according to the current routing profile * * It matches these geometries against the list of waypoints to find the best * match for each waypoint */ public final class WaypointMatcherImpl implements WaypointMatcher { private List waypoints; private OsmNodePairSet islandPairs; private int lonStart; private int latStart; private int lonTarget; private int latTarget; private boolean anyUpdate; private int lonLast; private int latLast; public WaypointMatcherImpl( List waypoints, double maxDistance, OsmNodePairSet islandPairs ) { this.waypoints = waypoints; this.islandPairs = islandPairs; for ( MatchedWaypoint mwp : waypoints ) { mwp.radius = maxDistance; } } private void checkSegment( int lon1, int lat1, int lon2, int lat2 ) { // todo: bounding-box pre-filter double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1+lat2) >> 1 ); double dlon2m = lonlat2m[0]; double dlat2m = lonlat2m[1]; double dx = ( lon2 - lon1 ) * dlon2m; double dy = ( lat2 - lat1 ) * dlat2m; double d = Math.sqrt( dy * dy + dx * dx ); if ( d == 0. ) return; for ( MatchedWaypoint mwp : waypoints ) { OsmNode wp = mwp.waypoint; double x1 = ( lon1 - wp.ilon ) * dlon2m; double y1 = ( lat1 - wp.ilat ) * dlat2m; double x2 = ( lon2 - wp.ilon ) * dlon2m; double y2 = ( lat2 - wp.ilat ) * dlat2m; double r12 = x1 * x1 + y1 * y1; double r22 = x2 * x2 + y2 * y2; double radius = Math.abs( r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy ) / d; if ( radius < mwp.radius ) { double s1 = x1 * dx + y1 * dy; double s2 = x2 * dx + y2 * dy; if ( s1 < 0. ) { s1 = -s1; s2 = -s2; } if ( s2 > 0. ) { radius = Math.sqrt( s1 < s2 ? r12 : r22 ); if ( radius > mwp.radius ) continue; } // new match for that waypoint mwp.radius = radius; // shortest distance to way mwp.hasUpdate = true; anyUpdate = true; // calculate crosspoint if ( mwp.crosspoint == null ) mwp.crosspoint = new OsmNode(); if ( s2 < 0. ) { double wayfraction = -s2 / ( d * d ); double xm = x2 - wayfraction * dx; double ym = y2 - wayfraction * dy; mwp.crosspoint.ilon = (int) ( xm / dlon2m + wp.ilon ); mwp.crosspoint.ilat = (int) ( ym / dlat2m + wp.ilat ); } else if ( s1 > s2 ) { mwp.crosspoint.ilon = lon2; mwp.crosspoint.ilat = lat2; } else { mwp.crosspoint.ilon = lon1; mwp.crosspoint.ilat = lat1; } } } } @Override public boolean start( int ilonStart, int ilatStart, int ilonTarget, int ilatTarget ) { if ( islandPairs.size() > 0 ) { long n1 = ( (long) ilonStart ) << 32 | ilatStart; long n2 = ( (long) ilonTarget ) << 32 | ilatTarget; if ( islandPairs.hasPair( n1, n2 ) ) { return false; } } lonLast = lonStart = ilonStart; latLast = latStart = ilatStart; lonTarget = ilonTarget; latTarget = ilatTarget; anyUpdate = false; return true; } @Override public void transferNode( int ilon, int ilat ) { checkSegment( lonLast, latLast, ilon, ilat ); lonLast = ilon; latLast = ilat; } @Override public void end() { checkSegment( lonLast, latLast, lonTarget, latTarget ); if ( anyUpdate ) { for ( MatchedWaypoint mwp : waypoints ) { if ( mwp.hasUpdate ) { mwp.hasUpdate = false; mwp.node1 = new OsmNode( lonStart, latStart ); mwp.node2 = new OsmNode( lonTarget, latTarget ); } } } } }