From d6219648631aa99b3bb895209f8f69582e6d97df Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Tue, 4 Dec 2018 15:54:50 +0100 Subject: [PATCH 1/4] Radius are now stored in meters, to easily account for different radiuses on X and Y axes. --- .../java/btools/router/MatchedWaypoint.java | 4 +- .../main/java/btools/router/OsmNodeNamed.java | 6 +-- .../java/btools/router/OsmNogoPolygon.java | 38 ++++++++----------- .../src/main/java/btools/router/OsmPath.java | 3 +- .../java/btools/router/RoutingContext.java | 33 +++++++--------- .../java/btools/router/SearchBoundary.java | 19 ++++++---- .../btools/router/WaypointMatcherImpl.java | 4 +- .../java/btools/routingapp/BRouterView.java | 7 +++- .../java/btools/util/CheapRulerSingleton.java | 13 ++++--- 9 files changed, 60 insertions(+), 67 deletions(-) diff --git a/brouter-core/src/main/java/btools/router/MatchedWaypoint.java b/brouter-core/src/main/java/btools/router/MatchedWaypoint.java index 055ccd9..14441f5 100644 --- a/brouter-core/src/main/java/btools/router/MatchedWaypoint.java +++ b/brouter-core/src/main/java/btools/router/MatchedWaypoint.java @@ -17,7 +17,7 @@ final class MatchedWaypoint public OsmNode node2; public OsmNodeNamed crosspoint; public OsmNodeNamed waypoint; - public double radius; + public double radius; // radius in meters public boolean hasUpdate; public void writeToStream( DataOutput dos ) throws IOException @@ -40,7 +40,7 @@ final class MatchedWaypoint mwp.node2 = new OsmNode(); mwp.crosspoint = new OsmNodeNamed(); mwp.waypoint = new OsmNodeNamed(); - + mwp.node1.ilat = dis.readInt(); mwp.node1.ilon = dis.readInt(); mwp.node2.ilat = dis.readInt(); diff --git a/brouter-core/src/main/java/btools/router/OsmNodeNamed.java b/brouter-core/src/main/java/btools/router/OsmNodeNamed.java index 1566174..5433446 100644 --- a/brouter-core/src/main/java/btools/router/OsmNodeNamed.java +++ b/brouter-core/src/main/java/btools/router/OsmNodeNamed.java @@ -10,15 +10,15 @@ import btools.mapaccess.OsmNode; public class OsmNodeNamed extends OsmNode { public String name; - public double radius; // radius of nogopoint + public double radius; // radius of nogopoint (in meters) public boolean isNogo = false; - + @Override public String toString() { return ilon + "," + ilat + "," + name; } - + public static OsmNodeNamed decodeNogo( String s ) { OsmNodeNamed n = new OsmNodeNamed(); diff --git a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java index 2b6c72a..57cdaf3 100644 --- a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java +++ b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java @@ -103,7 +103,7 @@ public class OsmNogoPolygon extends OsmNodeNamed int cx = (cxmax+cxmin) / 2; // center of circle int cy = (cymax+cymin) / 2; - + double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( cy ); double dlon2m = lonlat2m[0]; double dlat2m = lonlat2m[1]; @@ -111,28 +111,24 @@ public class OsmNogoPolygon extends OsmNodeNamed double rad = 0; // radius double rad2 = 0; // radius squared; - double dpx = 0; // x-xomponent of vector from center to point - double dpy = 0; // y-component - double dmax2 = 0; // squared lenght of vector from center to point + double dmax = 0; // length of vector from center to point int i_max = -1; do - { // now identify the point outside of the circle that has the greatest distance - for (int i = 0; i < points.size();i++) + { + // now identify the point outside of the circle that has the greatest distance + for (int i = 0; i < points.size(); i++) { final Point p = points.get(i); - final double dpix = (p.x - cx) * dlon2m; - final double dpiy = (p.y - cy) * dlat2m; - final double dist2 = dpix * dpix + dpiy * dpiy; - if (dist2 <= rad2) + final double dist = CheapRulerSingleton.distance(p.x, p.y, (int) cx, (int) cy); + if (dist <= rad) { continue; } - if (dist2 > dmax2) + if (dist > dmax) { - dmax2 = dist2; // new maximum distance found - dpx = dpix / dlon2m; - dpy = dpiy / dlat2m; + // new maximum distance found + dmax = dist; i_max = i; } } @@ -140,17 +136,13 @@ public class OsmNogoPolygon extends OsmNodeNamed { break; // leave loop when no point outside the circle is found any more. } - final double dist = Math.sqrt(dmax2); - final double dd = 0.5 * (dist - rad) / dist; - - cx += (int)(dd * dpx + 0.5); // shift center toward point - cy += (int)(dd * dpy + 0.5); + final double dd = 0.5 * (1 - rad / dmax); final Point p = points.get(i_max); // calculate new radius to just include this point - final double dpix = (p.x - cx) * dlon2m; - final double dpiy = (p.y - cy) * dlat2m; - dmax2 = rad2 = dpix * dpix + dpiy * dpiy; - rad = Math.sqrt(rad2); + cx += (int)(dd * (p.x - cx) + 0.5); // shift center toward point + cy += (int)(dd * (p.y - cy) + 0.5); + + dmax = rad = CheapRulerSingleton.distance(p.x, p.y, (int) cx, (int) cy); i_max = -1; } while (true); diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index 4133cf4..c4b954e 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -346,7 +346,8 @@ abstract class OsmPath implements OsmLinkHolder if ( rc.startDirectionValid ) { double dir = rc.startDirection.intValue() / CheapRulerSingleton.DEG_TO_RAD; - lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / rc.getCosLat() ); + double coslat = Math.cos(lat1); + lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / coslat ); lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) ); } else diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 9adbffa..480c900 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -193,7 +193,6 @@ public final class RoutingContext public Integer startDirection; public boolean startDirectionValid; - private double coslat; private double cosangle; public boolean nogomatch = false; public boolean isEndpoint = false; @@ -248,8 +247,8 @@ public final class RoutingContext try { ir = Integer.parseInt( s.substring( 4 ) ); } catch( Exception e ) { /* ignore */ } } - // TODO[Phyks] - nogo.radius = ir / 110984.; // 6378000. / 57.3; + // Radius of the nogo point in meters + nogo.radius = ir; } } @@ -259,12 +258,10 @@ public final class RoutingContext List nogos = new ArrayList(); for( OsmNodeNamed nogo : nogopoints ) { - // TODO[Phyks] - int radiusInMeter = (int)(nogo.radius * 111894.); boolean goodGuy = true; for( OsmNodeNamed wp : waypoints ) { - if ( wp.calcDistance( nogo ) < radiusInMeter + if ( wp.calcDistance( nogo ) < nogo.radius && (!(nogo instanceof OsmNogoPolygon) || (((OsmNogoPolygon)nogo).isClosed ? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat) @@ -288,6 +285,7 @@ public final class RoutingContext OsmNodeNamed nogo = nogopoints.get(i); cs[0] += nogo.ilon; cs[1] += nogo.ilat; + // 10 is an arbitrary constant to get sub-integer precision in the checksum cs[2] += (long) ( nogo.radius*10.); } return cs; @@ -342,7 +340,7 @@ public final class RoutingContext if ( s2 > 0. ) { radius = Math.sqrt( s1 < s2 ? r12 : r22 ); - if ( radius > nogo.radius ) continue; // 20m ^ 2 + if ( radius > nogo.radius ) continue; } if ( nogo.isNogo ) { @@ -404,28 +402,23 @@ public final class RoutingContext return (int)(d + 1.0 ); } - // assumes that calcDistance/calcCosAngle called in sequence, so coslat valid public double getCosAngle() { return cosangle; } - public double getCosLat() - { - return coslat; - } - public double calcAngle( int lon0, int lat0, int lon1, int lat1, int lon2, int lat2 ) { - double dlat1 = (lat1 - lat0); - double dlon1 = (lon1 - lon0) * coslat; - double dlat2 = (lat2 - lat1); - double dlon2 = (lon2 - lon1) * coslat; + double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( lat1 ); + double dy10 = (lat1 - lat0) * lonlat2m[1]; + double dx10 = (lon1 - lon0) * lonlat2m[0]; + double dy21 = (lat2 - lat1) * lonlat2m[1]; + double dx21 = (lon2 - lon1) * lonlat2m[0]; - double dd = Math.sqrt( (dlat1*dlat1 + dlon1*dlon1)*(dlat2*dlat2 + dlon2*dlon2) ); + double dd = Math.sqrt( (dx10*dx10 + dy10*dy10)*(dx21*dx21 + dy21*dy21) ); if ( dd == 0. ) { cosangle = 1.; return 0.; } - double sinp = (dlat1*dlon2 - dlon1*dlat2)/dd; - double cosp = (dlat1*dlat2 + dlon1*dlon2)/dd; + double sinp = (dy10*dy21 - dx10*dx21)/dd; + double cosp = (dy10*dy21 + dx10*dx21)/dd; cosangle = cosp; double p; diff --git a/brouter-core/src/main/java/btools/router/SearchBoundary.java b/brouter-core/src/main/java/btools/router/SearchBoundary.java index f40aec1..1d976f9 100644 --- a/brouter-core/src/main/java/btools/router/SearchBoundary.java +++ b/brouter-core/src/main/java/btools/router/SearchBoundary.java @@ -22,19 +22,22 @@ public final class SearchBoundary private int maxlat; private int radius; private OsmNode p; - + int direction; - + + /** + * @param radius Search radius in meters. + */ public SearchBoundary( OsmNode n, int radius, int direction ) { this.radius = radius; this.direction = direction; p = new OsmNode( n.ilon, n.ilat ); - + int lon = (n.ilon / 5000000 ) * 5000000; int lat = (n.ilat / 5000000 ) * 5000000; - + minlon0 = lon - 5000000; minlat0 = lat - 5000000; maxlon0 = lon + 10000000; @@ -45,12 +48,12 @@ public final class SearchBoundary maxlon = lon + 6000000; maxlat = lat + 6000000; } - + public static String getFileName( OsmNode n ) { int lon = (n.ilon / 5000000 ) * 5000000; int lat = (n.ilat / 5000000 ) * 5000000; - + int dlon = lon / 1000000 -180; int dlat = lat / 1000000 - 90; @@ -58,7 +61,7 @@ public final class SearchBoundary String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat; return slon + "_" + slat + ".trf"; } - + public boolean isInBoundary( OsmNode n, int cost ) { if ( radius > 0 ) @@ -83,5 +86,5 @@ public final class SearchBoundary default: throw new IllegalArgumentException( "undefined direction: "+ direction ); } } - + } diff --git a/brouter-core/src/main/java/btools/router/WaypointMatcherImpl.java b/brouter-core/src/main/java/btools/router/WaypointMatcherImpl.java index 82000f9..ee76707 100644 --- a/brouter-core/src/main/java/btools/router/WaypointMatcherImpl.java +++ b/brouter-core/src/main/java/btools/router/WaypointMatcherImpl.java @@ -33,8 +33,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher this.islandPairs = islandPairs; for ( MatchedWaypoint mwp : waypoints ) { - // TODO[Phyks] - mwp.radius = maxDistance * 110984.; // 6378000. / 57.3; + mwp.radius = maxDistance; } } @@ -49,6 +48,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher double dx = ( lon2 - lon1 ) * dlon2m; double dy = ( lat2 - lat1 ) * dlat2m; double d = Math.sqrt( dy * dy + dx * dx ); + if ( d == 0. ) return; diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java index 33d2672..f9a1fb0 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java @@ -635,7 +635,9 @@ public class BRouterView extends View int lat = n.ilat - centerLat; int x = imgw / 2 + (int) ( scaleLon * lon ); int y = imgh / 2 - (int) ( scaleLat * lat ); - int ir = (int) ( n.radius * 1000000. * scaleLat ); + + double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( centerLat ); + int ir = (int) ( n.radius * scaleLat / lonlat2m[1]); if ( ir > minradius ) { Paint paint = new Paint(); @@ -660,7 +662,8 @@ public class BRouterView extends View private void paintPolygon( Canvas canvas, OsmNogoPolygon p, int minradius ) { - final int ir = (int) ( p.radius * 1000000. * scaleLat ); + double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( centerLat ); + final int ir = (int) ( p.radius * scaleLat / lonlat2m[1] ); if ( ir > minradius ) { Paint paint = new Paint(); diff --git a/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java b/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java index 3935282..7a825aa 100644 --- a/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java +++ b/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java @@ -17,10 +17,10 @@ public final class CheapRulerSingleton { public final static int KILOMETERS_TO_METERS = 1000; public final static double DEG_TO_RAD = Math.PI / 180.; - // Cosine cache constants + // Scale cache constants private final static int SCALE_CACHE_LENGTH = 1800; private final static int SCALE_CACHE_INCREMENT = 100000; - // COS_CACHE_LENGTH cached values between 0 and COS_CACHE_MAX_DEGREES degrees. + // SCALE_CACHE_LENGTH cached values between 0 and COS_CACHE_MAX_DEGREES degrees. private final static double[][] SCALE_CACHE = new double[SCALE_CACHE_LENGTH][]; /** @@ -33,7 +33,7 @@ public final class CheapRulerSingleton { } private static double[] calcKxKyFromILat(int ilat) { - double lat = DEG_TO_RAD*(ilat-90000000)/1000000.; + double lat = DEG_TO_RAD*ilat*ILATLNG_TO_LATLNG - 90; double cos = Math.cos(lat); double cos2 = 2 * cos * cos - 1; double cos3 = 2 * cos * cos2 - cos; @@ -50,8 +50,8 @@ public final class CheapRulerSingleton { /** * Calculate the degree->meter scale for given latitude - * - * @result [lon->meter,lat->meter] + * + * @result [lon->meter,lat->meter] */ public static double[] getLonLatToMeterScales( int ilat ) { return SCALE_CACHE[ ilat / SCALE_CACHE_INCREMENT ]; @@ -65,12 +65,13 @@ public final class CheapRulerSingleton { * @param ilat1 Integer latitude for the start point, this is (latitude + 90) * 1e6. * @param ilon2 Integer longitude for the end point, this is (longitude + 180) * 1e6. * @param ilat2 Integer latitude for the end point, this is (latitude + 90) * 1e6. + * @return The distance between the two points, in meters. * * @note Integer longitude is ((longitude in degrees) + 180) * 1e6. * Integer latitude is ((latitude in degrees) + 90) * 1e6. */ public static double distance(int ilon1, int ilat1, int ilon2, int ilat2) { - double[] kxky = getLonLatToMeterScales( ( ilat1 + ilat2 ) >> 1 ); + double[] kxky = getLonLatToMeterScales( ( ilat1 + ilat2 ) >> 1 ); double dlon = (ilon1 - ilon2) * kxky[0]; double dlat = (ilat1 - ilat2) * kxky[1]; return Math.sqrt(dlat * dlat + dlon * dlon); // in m From 780e86587074433e0e41237c6f339e5079dc1fea Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Tue, 4 Dec 2018 17:23:28 +0100 Subject: [PATCH 2/4] Fix JavaDoc errors --- .../java/btools/codec/NoisyDiffCoder.java | 4 +- .../main/java/btools/codec/TagValueCoder.java | 6 +- .../java/btools/router/OsmNogoPolygon.java | 8 +-- .../expressions/BExpressionContext.java | 72 +++++++++---------- .../java/btools/mapdecoder/BitReadBuffer.java | 14 ++-- .../btools/mapsplitter/BitWriteBuffer.java | 21 +++--- .../mapsplitter/HuffmanTreeEncoder.java | 6 +- .../btools/mapsplitter/TagSetEncoder.java | 4 +- .../btools/server/request/ServerHandler.java | 34 ++++----- .../java/btools/util/BitCoderContext.java | 20 +++--- .../java/btools/util/ByteArrayUnifier.java | 2 +- .../java/btools/util/CheapRulerSingleton.java | 9 +-- .../main/java/btools/util/DenseLongMap.java | 14 ++-- .../src/main/java/btools/util/SortedHeap.java | 38 +++++----- 14 files changed, 124 insertions(+), 128 deletions(-) diff --git a/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java b/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java index 2dcde92..1fffa4c 100644 --- a/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java +++ b/brouter-codec/src/main/java/btools/codec/NoisyDiffCoder.java @@ -4,8 +4,8 @@ package btools.codec; * Encoder/Decoder for signed integers that automatically detects the typical * range of these numbers to determine a noisy-bit count as a very simple * dictionary - * - * Adapted for 3-pass encoding (counters -> statistics -> encoding ) + * + * Adapted for 3-pass encoding (counters -> statistics -> encoding ) * but doesn't do anything at pass1 */ public final class NoisyDiffCoder diff --git a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java index 580c816..b15c0a5 100644 --- a/brouter-codec/src/main/java/btools/codec/TagValueCoder.java +++ b/brouter-codec/src/main/java/btools/codec/TagValueCoder.java @@ -12,7 +12,7 @@ import btools.util.BitCoderContext; * It detects identical descriptions and sorts them * into a huffman-tree according to their frequencies * - * Adapted for 3-pass encoding (counters -> statistics -> encoding ) + * Adapted for 3-pass encoding (counters -> statistics -> encoding ) * but doesn't do anything at pass1 */ public final class TagValueCoder @@ -124,9 +124,9 @@ public final class TagValueCoder break; } inum += delta; - + int data = bc.decodeVarBits(); - + if ( validator == null || validator.isLookupIdxUsed( inum ) ) { hasdata = true; diff --git a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java index 57cdaf3..02991da 100644 --- a/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java +++ b/brouter-core/src/main/java/btools/router/OsmNogoPolygon.java @@ -109,7 +109,6 @@ public class OsmNogoPolygon extends OsmNodeNamed double dlat2m = lonlat2m[1]; double rad = 0; // radius - double rad2 = 0; // radius squared; double dmax = 0; // length of vector from center to point int i_max = -1; @@ -246,10 +245,9 @@ public class OsmNogoPolygon extends OsmNodeNamed /** * winding number test for a point in a polygon * - * @param p a point - * @param v list of vertex points forming a polygon. This polygon - * is implicitly closed connecting the last and first point. - * @return the winding number (=0 only when P is outside) + * @param px longitude of the point to check + * @param py latitude of the point to check + * @return a boolean whether the point is within the polygon or not. */ public boolean isWithin(final long px, final long py) { diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java index 74d874d..95d2864 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContext.java @@ -28,12 +28,12 @@ public abstract class BExpressionContext implements IByteArrayUnifier { private static final String CONTEXT_TAG = "---context:"; private static final String MODEL_TAG = "---model:"; - + private String context; private boolean _inOurContext = false; private BufferedReader _br = null; private boolean _readerDone = false; - + public String _modelClass; private Map lookupNumbers = new HashMap(); @@ -49,7 +49,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier private byte[] abBuf = new byte[256]; private BitCoderContext ctxEndode = new BitCoderContext( abBuf ); private BitCoderContext ctxDecode = new BitCoderContext( new byte[0] ); - + private Map variableNumbers = new HashMap(); private float[] variableData; @@ -69,12 +69,12 @@ public abstract class BExpressionContext implements IByteArrayUnifier // build-in variable indexes for fast access private int[] buildInVariableIdx; private int nBuildInVars; - + private float[] currentVars; private int currentVarOffset; - + private BExpressionContext foreignContext; - + protected void setInverseVars() { currentVarOffset = nBuildInVars; @@ -82,7 +82,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier abstract String[] getBuildInVariableNames(); - public final float getBuildInVariable( int idx ) + public final float getBuildInVariable( int idx ) { return currentVars[idx+currentVarOffset]; } @@ -107,11 +107,11 @@ public abstract class BExpressionContext implements IByteArrayUnifier { this.context = context; this.meta = meta; - + if ( meta != null ) meta.registerListener(context, this ); if ( Boolean.getBoolean( "disableExpressionCache" ) ) hashSize = 1; - + // create the expression cache if ( hashSize > 0 ) { @@ -133,12 +133,12 @@ public abstract class BExpressionContext implements IByteArrayUnifier { BitCoderContext ctx = ctxEndode; ctx.reset(); - + int skippedTags = 0; int nonNullTags= 0; // (skip first bit ("reversedirection") ) - + // all others are generic for( int inum = 1; inum < lookupValues.size(); inum++ ) // loop over lookup names { @@ -151,29 +151,29 @@ public abstract class BExpressionContext implements IByteArrayUnifier ctx.encodeVarBits( skippedTags+1 ); nonNullTags++; skippedTags = 0; - + // 0 excluded already, 1 (=unknown) we rotate up to 8 // to have the good code space for the popular values int dd = d < 2 ? 7 : ( d < 9 ? d - 2 : d - 1); ctx.encodeVarBits( dd ); } ctx.encodeVarBits( 0 ); - + if ( nonNullTags == 0) return null; - + int len = ctx.getEncodedLength(); byte[] ab = new byte[len]; System.arraycopy( abBuf, 0, ab, 0, len ); - - + + // crosscheck: decode and compare int[] ld2 = new int[lookupValues.size()]; decode( ld2, false, ab ); for( int inum = 1; inum < lookupValues.size(); inum++ ) // loop over lookup names (except reverse dir) { if ( ld2[inum] != ld[inum] ) throw new RuntimeException( "assertion failed encoding inum=" + inum + " val=" + ld[inum] + " " + getKeyValueDescription(false, ab) ); - } - + } + return ab; } @@ -186,7 +186,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier decode( lookupData, false, ab ); lookupDataValid = true; } - + /** @@ -196,10 +196,10 @@ public abstract class BExpressionContext implements IByteArrayUnifier { BitCoderContext ctx = ctxDecode; ctx.reset( ab ); - + // start with first bit hardwired ("reversedirection") ld[0] = inverseDirection ? 2 : 0; - + // all others are generic int inum = 1; for(;;) @@ -207,7 +207,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier int delta = ctx.decodeVarBits(); if ( delta == 0) break; if ( inum + delta > ld.length ) break; // higher minor version is o.k. - + while ( delta-- > 1 ) ld[inum++] = 0; // see encoder for value rotation @@ -255,7 +255,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier private int parsedLines = 0; private boolean fixTagsWritten = false; - + public void parseMetaLine( String line ) { parsedLines++; @@ -278,7 +278,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier // add aliases while( newValue != null && tk.hasMoreTokens() ) newValue.addAlias( tk.nextToken() ); } - + public void finishMetaParsing() { if ( parsedLines == 0 && !"global".equals(context) ) @@ -288,7 +288,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier // post-process metadata: lookupDataFrozen = true; - + lookupIdxUsed = new boolean[lookupValues.size()]; } @@ -310,12 +310,12 @@ public abstract class BExpressionContext implements IByteArrayUnifier private long requests; private long requests2; private long cachemisses; - + public String cacheStats() { return "requests=" + requests + " requests2=" + requests2 + " cachemisses=" + cachemisses; } - + private CacheNode lastCacheNode = new CacheNode(); // @Override @@ -355,7 +355,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { requests++; lookupDataValid = false; // this is an assertion for a nasty pifall - + if ( cache == null ) { decode( lookupData, inverseDirection, ab ); @@ -383,7 +383,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier if ( cn == null ) { cachemisses++; - + cn = (CacheNode)cache.removeLru(); if ( cn == null ) { @@ -405,9 +405,9 @@ public abstract class BExpressionContext implements IByteArrayUnifier // inverse direction lookupData[0] = 2; // inverse shortcut: reuse decoding evaluateInto( probeVarSet.vars, nBuildInVars ); - + probeVarSet.hash = Arrays.hashCode( probeVarSet.vars ); - + // unify the result variable set VarWrapper vw = (VarWrapper)resultVarCache.get( probeVarSet ); if ( vw == null ) @@ -427,7 +427,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier else { if ( ab == cn.ab ) requests2++; - + cache.touch( cn ); } @@ -647,7 +647,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier /** * special hack for yes/proposed relations: - * add a lookup value if not yet a smaller, >1 value was added + * add a lookup value if not yet a smaller, > 1 value was added * add a 2=yes if the provided value is out of range * value-index means here 0=unknown, 1=other, 2=yes, 3=proposed */ @@ -680,7 +680,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier Integer num = lookupNumbers.get( name ); return num != null && lookupData[num.intValue()] == 2; } - + public int getOutputVariableIndex( String name, boolean mustExist ) { int idx = getVariableIdx( name, false ); @@ -708,7 +708,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier buildInVariableIdx = extended; return nBuildInVars++; } - + public void setForeignContext( BExpressionContext foreignContext ) { this.foreignContext = foreignContext; @@ -855,7 +855,7 @@ public abstract class BExpressionContext implements IByteArrayUnifier { lookupIdxUsed[ idx ] = true; } - + public final boolean isLookupIdxUsed( int idx ) { return idx < lookupIdxUsed.length ? lookupIdxUsed[idx] : false; diff --git a/brouter-map-creator/src/main/java/btools/mapdecoder/BitReadBuffer.java b/brouter-map-creator/src/main/java/btools/mapdecoder/BitReadBuffer.java index ac40d03..a073bed 100644 --- a/brouter-map-creator/src/main/java/btools/mapdecoder/BitReadBuffer.java +++ b/brouter-map-creator/src/main/java/btools/mapdecoder/BitReadBuffer.java @@ -14,7 +14,7 @@ public final class BitReadBuffer this.ab = ab; idxMax = ab.length-1; } - + public boolean decodeBit() { fillBuffer(); @@ -60,10 +60,10 @@ public final class BitReadBuffer /** * decode a small number with a variable bit length * (poor mans huffman tree) - * 1 -> 0 - * 01 -> 1 + following 1-bit word ( 1..2 ) - * 001 -> 3 + following 2-bit word ( 3..6 ) - * 0001 -> 7 + following 3-bit word ( 7..14 ) etc. + * {@code 1 -> 0} + * {@code 01 -> 1} + following 1-bit word ( 1..2 ) + * {@code 001 -> 3} + following 2-bit word ( 3..6 ) + * {@code 0001 -> 7} + following 3-bit word ( 7..14 ) etc. */ public int decodeInt() { @@ -139,7 +139,7 @@ public final class BitReadBuffer } } - + private void fillBuffer() { while (bits <= 56) @@ -150,5 +150,5 @@ public final class BitReadBuffer } bits += 8; } - } + } } diff --git a/brouter-map-creator/src/main/java/btools/mapsplitter/BitWriteBuffer.java b/brouter-map-creator/src/main/java/btools/mapsplitter/BitWriteBuffer.java index 6fe54d2..4189581 100644 --- a/brouter-map-creator/src/main/java/btools/mapsplitter/BitWriteBuffer.java +++ b/brouter-map-creator/src/main/java/btools/mapsplitter/BitWriteBuffer.java @@ -23,12 +23,13 @@ public final class BitWriteBuffer /** * encode a distance with a variable bit length * (poor mans huffman tree) - * 1 -> 0 - * 01 -> 1 + following 1-bit word ( 1..2 ) - * 001 -> 3 + following 2-bit word ( 3..6 ) - * 0001 -> 7 + following 3-bit word ( 7..14 ) etc. - * - * @see #decodeVarBits + * {@code 1 -> 0} + * {@code 01 -> 1} + following 1-bit word ( 1..2 ) + * {@code 001 -> 3} + following 2-bit word ( 3..6 ) + * {@code 0001 -> 7} + following 3-bit word ( 7..14 ) etc. + * + * @see btools.util.BitCoderContext#decodeVarBits + * */ public void encodeInt( int value ) { @@ -39,7 +40,7 @@ public final class BitWriteBuffer value -= range + 1; range = 2 * range + 1; } - + encodeBit( true ); encodeBounded( range, value ); } @@ -127,7 +128,7 @@ public final class BitWriteBuffer if ( size == 0 ) { return; - } + } long maxValue = values[size-1]; int nbits = 0; while ( maxValue > 0 ) @@ -187,7 +188,7 @@ public final class BitWriteBuffer /** * assign the de-/encoded bits since the last call assignBits to the given * name. Used for encoding statistics - * + * * @see #getBitReport */ public void assignBits( String name ) @@ -210,7 +211,7 @@ public final class BitWriteBuffer /** * Get a textual report on the bit-statistics - * + * * @see #assignBits */ public static String getBitReport() diff --git a/brouter-map-creator/src/main/java/btools/mapsplitter/HuffmanTreeEncoder.java b/brouter-map-creator/src/main/java/btools/mapsplitter/HuffmanTreeEncoder.java index 35bf4bf..7ff6b16 100644 --- a/brouter-map-creator/src/main/java/btools/mapsplitter/HuffmanTreeEncoder.java +++ b/brouter-map-creator/src/main/java/btools/mapsplitter/HuffmanTreeEncoder.java @@ -10,7 +10,7 @@ import java.util.PriorityQueue; * It detects identical sets and sorts them * into a huffman-tree according to their frequencies * - * Adapted for 3-pass encoding (counters -> statistics -> encoding ) + * Adapted for 3-pass encoding (counters -> statistics -> encoding ) * but doesn't do anything at pass1 */ public abstract class HuffmanTreeEncoder @@ -124,10 +124,10 @@ public abstract class HuffmanTreeEncoder } } } - + @Override public boolean equals( Object o ) - { + { return itemEquals( ((TreeNode)o).data, data ); } diff --git a/brouter-map-creator/src/main/java/btools/mapsplitter/TagSetEncoder.java b/brouter-map-creator/src/main/java/btools/mapsplitter/TagSetEncoder.java index c0123d5..c9408f2 100644 --- a/brouter-map-creator/src/main/java/btools/mapsplitter/TagSetEncoder.java +++ b/brouter-map-creator/src/main/java/btools/mapsplitter/TagSetEncoder.java @@ -10,7 +10,7 @@ import java.util.PriorityQueue; * It detects identical sets and sorts them * into a huffman-tree according to their frequencies * - * Adapted for 3-pass encoding (counters -> statistics -> encoding ) + * Adapted for 3-pass encoding (counters -> statistics -> encoding ) * but doesn't do anything at pass1 */ public final class TagSetEncoder @@ -131,7 +131,7 @@ public final class TagSetEncoder } } } - + @Override public boolean equals( Object o ) { diff --git a/brouter-server/src/main/java/btools/server/request/ServerHandler.java b/brouter-server/src/main/java/btools/server/request/ServerHandler.java index bec049d..12c3621 100644 --- a/brouter-server/src/main/java/btools/server/request/ServerHandler.java +++ b/brouter-server/src/main/java/btools/server/request/ServerHandler.java @@ -13,11 +13,11 @@ import java.util.HashMap; import java.util.List; /** - * URL query parameter handler for web and standalone server. Supports all + * URL query parameter handler for web and standalone server. Supports all * BRouter features without restrictions. - * + * * Parameters: - * + * * lonlats = lon,lat|... (unlimited list of lon,lat waypoints separated by |) * nogos = lon,lat,radius|... (optional, radius in meters) * profile = profile file name without .brf @@ -25,8 +25,8 @@ import java.util.List; * format = [kml|gpx|geojson] (optional, default gpx) * * Example URLs: - * http://localhost:17777/brouter?lonlats=8.799297,49.565883|8.811764,49.563606&nogos=&profile=trekking&alternativeidx=0&format=gpx - * http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml + * {@code http://localhost:17777/brouter?lonlats=8.799297,49.565883|8.811764,49.563606&nogos=&profile=trekking&alternativeidx=0&format=gpx} + * {@code http://localhost:17777/brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml} * */ public class ServerHandler extends RequestHandler { @@ -59,7 +59,7 @@ public class ServerHandler extends RequestHandler { rc.localFunction = profile; rc.setAlternativeIdx(Integer.parseInt(params.get( "alternativeidx" ))); - + List nogoList = readNogoList(); List nogoPolygonsList = readNogoPolygons(); @@ -89,14 +89,14 @@ public class ServerHandler extends RequestHandler { if (lonLats == null) throw new IllegalArgumentException( "lonlats parameter not set" ); String[] coords = lonLats.split("\\|"); - if (coords.length < 2) + if (coords.length < 2) throw new IllegalArgumentException( "we need two lat/lon points at least!" ); - + List wplist = new ArrayList(); for (int i = 0; i < coords.length; i++) { String[] lonLat = coords[i].split(","); - if (lonLat.length < 2) + if (lonLat.length < 2) throw new IllegalArgumentException( "we need two lat/lon points at least!" ); wplist.add( readPosition( lonLat[0], lonLat[1], "via" + i ) ); } @@ -106,7 +106,7 @@ public class ServerHandler extends RequestHandler { return wplist; } - + @Override public String formatTrack(OsmTrack track) { @@ -175,7 +175,7 @@ public class ServerHandler extends RequestHandler { result = "text/tab-separated-values"; } } - + return result; } @@ -197,10 +197,10 @@ public class ServerHandler extends RequestHandler { { if ( vlon == null ) throw new IllegalArgumentException( "lon " + name + " not found in input" ); if ( vlat == null ) throw new IllegalArgumentException( "lat " + name + " not found in input" ); - + return readPosition(Double.parseDouble( vlon ), Double.parseDouble( vlat ), name); } - + private static OsmNodeNamed readPosition( double lon, double lat, String name ) { OsmNodeNamed n = new OsmNodeNamed(); @@ -209,7 +209,7 @@ public class ServerHandler extends RequestHandler { n.ilat = (int)( ( lat + 90. ) *1000000. + 0.5); return n; } - + private List readNogoList() { // lon,lat,radius|... @@ -227,7 +227,7 @@ public class ServerHandler extends RequestHandler { return nogoList; } - + private static OsmNodeNamed readNogo( String lon, String lat, String radius ) { return readNogo(Double.parseDouble( lon ), Double.parseDouble( lat ), Integer.parseInt( radius ) ); @@ -250,7 +250,7 @@ public class ServerHandler extends RequestHandler { parseNogoPolygons( params.get("polygons"), result, true ); return result.size() > 0 ? result : null; } - + private static void parseNogoPolygons(String polygons, List result, boolean closed ) { if ( polygons != null ) @@ -277,6 +277,6 @@ public class ServerHandler extends RequestHandler { } } } - } + } } } diff --git a/brouter-util/src/main/java/btools/util/BitCoderContext.java b/brouter-util/src/main/java/btools/util/BitCoderContext.java index 7a8f038..fa92c3e 100644 --- a/brouter-util/src/main/java/btools/util/BitCoderContext.java +++ b/brouter-util/src/main/java/btools/util/BitCoderContext.java @@ -15,13 +15,13 @@ public class BitCoderContext this.ab = ab; idxMax = ab.length-1; } - + public final void reset( byte[] ab ) { this.ab = ab; idxMax = ab.length-1; reset(); - } + } public final void reset() { @@ -29,16 +29,16 @@ public class BitCoderContext bm = 0x100; bits = 0; b = 0; - } + } /** * encode a distance with a variable bit length * (poor mans huffman tree) - * 1 -> 0 - * 01 -> 1 + following 1-bit word ( 1..2 ) - * 001 -> 3 + following 2-bit word ( 3..6 ) - * 0001 -> 7 + following 3-bit word ( 7..14 ) etc. - * + * {@code 1 -> 0} + * {@code 01 -> 1} + following 1-bit word ( 1..2 ) + * {@code 001 -> 3} + following 2-bit word ( 3..6 ) + * {@code 0001 -> 7} + following 3-bit word ( 7..14 ) etc. + * * @see #decodeVarBits */ public final void encodeVarBits( int value ) @@ -175,7 +175,7 @@ public class BitCoderContext bits -= count; return value; } - + private void fillBuffer() { while (bits < 24) @@ -186,7 +186,7 @@ public class BitCoderContext } bits += 8; } - } + } /** * @return the encoded length in bytes diff --git a/brouter-util/src/main/java/btools/util/ByteArrayUnifier.java b/brouter-util/src/main/java/btools/util/ByteArrayUnifier.java index a9eb65f..b1d7c3b 100644 --- a/brouter-util/src/main/java/btools/util/ByteArrayUnifier.java +++ b/brouter-util/src/main/java/btools/util/ByteArrayUnifier.java @@ -17,7 +17,7 @@ public final class ByteArrayUnifier implements IByteArrayUnifier * Unify a byte array in order to reuse instances when possible. * The byte arrays are assumed to be treated as immutable, * allowing the reuse - * @param the byte array to unify + * @param ab the byte array to unify * @return the cached instance or the input instanced if not cached */ public byte[] unify( byte[] ab ) diff --git a/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java b/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java index 7a825aa..7872390 100644 --- a/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java +++ b/brouter-util/src/main/java/btools/util/CheapRulerSingleton.java @@ -49,9 +49,9 @@ public final class CheapRulerSingleton { } /** - * Calculate the degree->meter scale for given latitude + * Calculate the degree->meter scale for given latitude * - * @result [lon->meter,lat->meter] + * @return [lon->meter,lat->meter] */ public static double[] getLonLatToMeterScales( int ilat ) { return SCALE_CACHE[ ilat / SCALE_CACHE_INCREMENT ]; @@ -67,8 +67,9 @@ public final class CheapRulerSingleton { * @param ilat2 Integer latitude for the end point, this is (latitude + 90) * 1e6. * @return The distance between the two points, in meters. * - * @note Integer longitude is ((longitude in degrees) + 180) * 1e6. - * Integer latitude is ((latitude in degrees) + 90) * 1e6. + * Note: + * Integer longitude is ((longitude in degrees) + 180) * 1e6. + * Integer latitude is ((latitude in degrees) + 90) * 1e6. */ public static double distance(int ilon1, int ilat1, int ilon2, int ilat2) { double[] kxky = getLonLatToMeterScales( ( ilat1 + ilat2 ) >> 1 ); diff --git a/brouter-util/src/main/java/btools/util/DenseLongMap.java b/brouter-util/src/main/java/btools/util/DenseLongMap.java index be5d078..0e9385f 100644 --- a/brouter-util/src/main/java/btools/util/DenseLongMap.java +++ b/brouter-util/src/main/java/btools/util/DenseLongMap.java @@ -31,8 +31,6 @@ public class DenseLongMap * Creates a DenseLongMap for the default block size * ( 512 bytes per bitplane, covering a key range of 4096 keys ) * Note that one value range is limited to 0..254 - * - * @param valuebits number of bits to use per value */ public DenseLongMap() { @@ -85,7 +83,7 @@ public class DenseLongMap while (blocklist.size() < blockn+1 ) { blocklist.add(null); - } + } blocklist.set( blockn, block ); } else @@ -99,7 +97,7 @@ public class DenseLongMap int headersize = 1 << valuebits; byte v = (byte)(value + 1); // 0 is reserved (=unset) - + // find the index in the lookup table or the first entry int idx = 1; while( idx < headersize ) @@ -122,7 +120,7 @@ public class DenseLongMap valuebits++; headersize = 1 << valuebits; } - + int bitmask = 1 << (offset & 0x7); int invmask = bitmask ^ 0xff; int probebit = 1; @@ -149,7 +147,7 @@ public class DenseLongMap // size is lookup table + datablocks return ( 1 << bits ) + blocksize * bits; } - + private byte[] expandBlock( byte[] block, int valuebits ) { bitplaneCount[valuebits] ++; @@ -204,7 +202,7 @@ public class DenseLongMap { return -1; } - + // check how many bitplanes we have from the arrayzize int valuebits = 1; while( sizeForBits( valuebits) < block.length ) @@ -212,7 +210,7 @@ public class DenseLongMap valuebits++; } int headersize = 1 << valuebits; - + int bitmask = 1 << (offset & 7); int probebit = 1; int blockidx = (offset >> 3) + headersize; diff --git a/brouter-util/src/main/java/btools/util/SortedHeap.java b/brouter-util/src/main/java/btools/util/SortedHeap.java index 0953a6a..ff0b6a1 100644 --- a/brouter-util/src/main/java/btools/util/SortedHeap.java +++ b/brouter-util/src/main/java/btools/util/SortedHeap.java @@ -4,7 +4,7 @@ import java.util.Random; /** * Memory efficient and lightning fast heap to get the lowest-key value of a set of key-object pairs - * + * * @author ab */ public final class SortedHeap @@ -54,7 +54,7 @@ public final class SortedHeap Object[] vla; // value array int lv; // low value int lp; // low pointer - + SortedBin( int binsize, SortedHeap parent ) { this.binsize = binsize; @@ -63,7 +63,7 @@ public final class SortedHeap vla = new Object[binsize]; lp = binsize; } - + SortedBin next() { if ( next == null ) @@ -108,7 +108,7 @@ public final class SortedHeap neBin = next; } } - + void add( int key, Object value ) { int p = lp; @@ -126,17 +126,15 @@ public final class SortedHeap p++; } } - + } - + /** * add a key value pair to the heap - * - * @param id - * the key to insert - * @param value - * the value to insert object + * + * @param key the key to insert + * @param value the value to insert object */ public void add( int key, V value ) { @@ -164,16 +162,16 @@ public final class SortedHeap first.nextNonEmpty = second; } } - + } - + private void sortUp() { if ( size > peaksize ) { peaksize = size; } - + // determine the first array big enough to take them all int cnt = 8; // value count of first 2 bins is always 8 SortedBin tbin = second; // target bin @@ -193,10 +191,10 @@ public final class SortedHeap int[] al_t = tbin.al; Object[] vla_t = tbin.vla; int tp = tbin.binsize-cnt; // target pointer - + // unlink any higher, non-empty arrays SortedBin otherNonEmpty = lastNonEmpty.nextNonEmpty; - lastNonEmpty.nextNonEmpty = null; + lastNonEmpty.nextNonEmpty = null; // now merge the content of these non-empty bins into the target bin while( firstNonEmpty != null ) @@ -215,7 +213,7 @@ public final class SortedHeap } // current minimum found, copy to target array - al_t[tp] = minId; + al_t[tp] = minId; vla_t[tp++] = minBin.dropLowest(); } @@ -233,7 +231,7 @@ public final class SortedHeap second = new SortedBin( 4, this ); firstNonEmpty = null; } - + public int getSize() { return size; @@ -267,7 +265,7 @@ public final class SortedHeap } return tp; } - + public static void main(String[] args) { SortedHeap sh = new SortedHeap(); @@ -296,6 +294,6 @@ System.out.println( "popLowestKeyValue: " + val); // Assert.assertTrue( "total count test", cnt == 100000 ); } - + } From 34f5258f5ea472db72761a86349ac368c4b8ea9e Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Fri, 7 Dec 2018 09:15:03 +0100 Subject: [PATCH 3/4] Remove last use of rc.coslat --- brouter-core/src/main/java/btools/router/OsmPath.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index c4b954e..346704a 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -346,9 +346,9 @@ abstract class OsmPath implements OsmLinkHolder if ( rc.startDirectionValid ) { double dir = rc.startDirection.intValue() / CheapRulerSingleton.DEG_TO_RAD; - double coslat = Math.cos(lat1); - lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / coslat ); - lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) ); + double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( lat1 ); + lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] ); + lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] ); } else { From e0259e4cde9822620b6c5d970781250bbd41af42 Mon Sep 17 00:00:00 2001 From: "Phyks (Lucas Verney)" Date: Fri, 7 Dec 2018 14:24:51 +0100 Subject: [PATCH 4/4] Use center latitude for getLonLatToMeters calls --- brouter-core/src/main/java/btools/router/OsmPath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index 346704a..c8f3f00 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -346,7 +346,7 @@ abstract class OsmPath implements OsmLinkHolder if ( rc.startDirectionValid ) { double dir = rc.startDirection.intValue() / CheapRulerSingleton.DEG_TO_RAD; - double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( lat1 ); + double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( (lon0 + lat1) >> 1 ); lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] ); lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] ); }