Index: src/uk/me/parabola/imgfmt/app/Coord.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/Coord.java	(revision 3820)
+++ src/uk/me/parabola/imgfmt/app/Coord.java	(working copy)
@@ -22,12 +22,13 @@
 
 import uk.me.parabola.imgfmt.Utils;
 import uk.me.parabola.mkgmap.filters.ShapeMergeFilter;
+import uk.me.parabola.mkgmap.osmstyle.WrongAngleFixer;
 
 /**
- * A point coordinate in unshifted map-units.
- * A map unit is 360/2^24 degrees.  In some places <i>shifted</i> coordinates
- * are used, which means that they are divided by some power of two to save
- * space in the file.
+ * A point coordinate in extended map-units. A map unit is 360/2^24 degrees, we
+ * use a higher resolution (see HIGH_PREC_BITS). In some places <i>shifted</i>
+ * coordinates are used, which means that they are divided by some power of two
+ * to save space in the file.
  *
  * You can create one of these with lat/long by calling the constructor with
  * double args.
@@ -35,6 +36,7 @@
  * See also http://www.movable-type.co.uk/scripts/latlong.html
  *
  * @author Steve Ratcliffe
+ * @author Gerd Petermann
  */
 public class Coord implements Comparable<Coord> {
 	private final static short ON_BOUNDARY_MASK = 0x0001; // bit in flags is true if point lies on a boundary
@@ -50,8 +52,12 @@
 	private final static short END_OF_WAY = 0x0200; // use only in WrongAngleFixer
 	private final static short HOUSENUMBER_NODE = 0x0400; // start/end of house number interval
 	private final static short ADDED_HOUSENUMBER_NODE = 0x0800; // node was added for house numbers
+	private final static short LAT_DEC = 0x1000; // for high precision to garmin calculations
+	private final static short LAT_INC = 0x2000; // for high precision to garmin calculations
+	private final static short LON_DEC = 0x4000; // for high precision to garmin calculations
+	private final static short LON_INC = (short) 0x8000; // for high precision to garmin calculations
 	
-	private final static int HIGH_PREC_BITS = 30;
+	private final static int HIGH_PREC_BITS = 30; // TODO : 31 or 32 still cause problems
 	public final static int DELTA_SHIFT = HIGH_PREC_BITS - 24; 
 	private final static int MAX_DELTA = 1 << (DELTA_SHIFT - 2); // max delta abs value that is considered okay
 	private final static long FACTOR_HP = 1L << HIGH_PREC_BITS;
@@ -60,25 +66,31 @@
 	public final static double U = R * 2 * Math.PI; // circumference of earth at equator (WGS84)
 	public final static double MEAN_EARTH_RADIUS = 6371000; // earth is a flattened sphere
 	
-	private final int latitude;
-	private final int longitude;
+	private final int latHp;
+	private final int lonHp;
 	private byte highwayCount; // number of highways that use this point
 	private short flags; // further attributes
-	private final byte latDelta; // delta to high precision latitude value 
-	private final byte lonDelta; // delta to high precision longitude value
+	
 	private short approxDistanceToDisplayedCoord = -1;
 
 	/**
 	 * Construct from co-ordinates that are already in map-units.
-	 * @param latitude The latitude in map units.
-	 * @param longitude The longitude in map units.
+	 * @param latitude24 The latitude in map units.
+	 * @param longitude24 The longitude in map units.
 	 */
-	public Coord(int latitude, int longitude) {
-		this.latitude = latitude;
-		this.longitude = longitude;
-		latDelta = lonDelta = 0;
+	public Coord(int latitude24, int longitude24) {
+		latHp = garminToHighPrec(latitude24);
+		lonHp = garminToHighPrec(longitude24);
 	}
 
+	@SuppressWarnings("unused")
+	public static int garminToHighPrec (int latLon24) {
+		if (HIGH_PREC_BITS == 32 && latLon24 == 0x800000) {
+			// catch overflow 
+			return Integer.MAX_VALUE; 
+		}
+		return latLon24 << DELTA_SHIFT;
+	}
 	/**
 	 * Construct from regular latitude and longitude.
 	 * @param latitude The latitude in degrees.
@@ -85,37 +97,24 @@
 	 * @param longitude The longitude in degrees.
 	 */
 	public Coord(double latitude, double longitude) {
-		this.latitude = Utils.toMapUnit(latitude);
-		this.longitude = Utils.toMapUnit(longitude);
-		int latHighPrec = toHighPrec(latitude);
-		int lonHighPrec = toHighPrec(longitude);
-		this.latDelta = (byte) ((this.latitude << DELTA_SHIFT) - latHighPrec); 
-		this.lonDelta = (byte) ((this.longitude << DELTA_SHIFT) - lonHighPrec);
-
-		// verify math
-		assert (this.latitude << DELTA_SHIFT) - latDelta == latHighPrec;
-		assert (this.longitude << DELTA_SHIFT) - lonDelta == lonHighPrec;
+		latHp = toHighPrec(latitude);
+		lonHp = toHighPrec(longitude);
 	}
 	
-	private Coord (int lat, int lon, byte latDelta, byte lonDelta){
-		this.latitude = lat;
-		this.longitude = lon;
-		this.latDelta = latDelta;
-		this.lonDelta = lonDelta;
+	private Coord(int latHighPrec, int lonHighPrec, boolean highPrec) {
+		latHp = latHighPrec;
+		lonHp = lonHighPrec;
 	}
+
 	
 	/**
-	 * Constructor for high precision values.
+	 * Factory for high precision values.
 	 * @param latHighPrec latitude in high precision
 	 * @param lonHighPrec longitude in high precision
 	 * @return Coord instance
 	 */
 	public static Coord makeHighPrecCoord(int latHighPrec, int lonHighPrec){
-		int lat24 = (latHighPrec + (1 << (DELTA_SHIFT - 1))) >> DELTA_SHIFT;
-		int lon24 = (lonHighPrec + (1 << (DELTA_SHIFT - 1))) >> DELTA_SHIFT;
-		byte dLat = (byte) ((lat24 << DELTA_SHIFT) - latHighPrec);
-		byte dLon = (byte) ((lon24 << DELTA_SHIFT) - lonHighPrec);
-		return new Coord(lat24, lon24, dLat, dLon);
+		return new Coord(latHighPrec, lonHighPrec, true);
 	}
 	
 	/**
@@ -124,19 +123,38 @@
 	 * @param other
 	 */
 	public Coord(Coord other) {
-		this.latitude = other.latitude;
-		this.longitude = other.longitude;
-		this.latDelta = other.latDelta;
-		this.lonDelta = other.lonDelta;
 		this.approxDistanceToDisplayedCoord = other.approxDistanceToDisplayedCoord;
+		this.latHp = other.latHp;
+		this.lonHp = other.lonHp;
+		this.flags = (short) (other.flags & 0xf000);
 	}
 
+	/**
+	 * @return latitude in Garmin (24 bit) precision.
+	 */
 	public int getLatitude() {
-		return latitude;
+		int lat24 = (latHp + (1 << (DELTA_SHIFT - 1))) >> DELTA_SHIFT;
+		if ((flags & LAT_DEC) != 0)
+			--lat24; 
+		else if ((flags & LAT_INC) != 0)
+			++lat24;
+		return lat24;
 	}
 
+	/**
+	 * @return longitude in Garmin (24 bit) precision
+	 */
 	public int getLongitude() {
-		return longitude;
+		int lon24;
+		if (HIGH_PREC_BITS == 32 && lonHp == Integer.MAX_VALUE)
+			lon24 = (lonHp >> DELTA_SHIFT) + 1;
+		else 
+			lon24 = (lonHp + (1 << (DELTA_SHIFT - 1))) >> DELTA_SHIFT;
+		if ((flags & LON_DEC) != 0)
+			--lon24; 
+		else if ((flags & LON_INC) != 0)
+			++lon24;
+		return lon24;
 	}
 
 	/**
@@ -380,7 +398,7 @@
 		// max lat: 4194304
 		// max lon: 8388608
 		// max hashCode: 2118123520 < 2147483647 (Integer.MAX_VALUE)
-		return 503 * latitude + longitude;
+		return 503 * getLatitude() + getLongitude();
 	}
 
 	/**
@@ -390,7 +408,7 @@
 		if (obj == null || !(obj instanceof Coord))
 			return false;
 		Coord other = (Coord) obj;
-		return latitude == other.latitude && longitude == other.longitude;
+		return getLatitude() == other.getLatitude() && getLongitude() == other.getLongitude();
 	}
 	
 	/**
@@ -499,7 +517,6 @@
 		double dist = distRad * R;
 
 		return dist;
-		
 	}
 
 	/**
@@ -583,12 +600,12 @@
 	 * This ordering is used for sorting entries in NOD3.
 	 */
 	public int compareTo(Coord other) {
-		if (longitude == other.getLongitude()) {
-			if (latitude == other.getLatitude())
+		if (getLongitude() == other.getLongitude()) {
+			if (getLatitude() == other.getLatitude())
 				return 0;
-			return latitude > other.getLatitude() ? 1 : -1;
+			return getLatitude() > other.getLatitude() ? 1 : -1;
 		}
-		return longitude > other.getLongitude() ? 1 : -1;
+		return getLongitude() > other.getLongitude() ? 1 : -1;
 	}			
 
 	/**
@@ -597,7 +614,7 @@
 	 * @return a string representation of the object.
 	 */
 	public String toString() {
-		return (latitude) + "/" + (longitude);
+		return (getLatitude()) + "/" + (getLongitude());
 	}
 
 	public String toDegreeString() {
@@ -626,8 +643,8 @@
 	 * @param degrees The latitude or longitude as decimal degrees.
 	 * @return An integer value with {@code HIGH_PREC_BITS} bit precision.
 	 */
-	private static int toHighPrec(double degrees) {
-		final double DELTA = 360.0D / FACTOR_HP / 2; // Correct rounding
+	public static int toHighPrec(double degrees) {
+		final double DELTA = (360.0D / FACTOR_HP) / 2; // Correct rounding
 		double v = (degrees > 0) ? degrees + DELTA : degrees - DELTA;
 		return (int) (v * FACTOR_HP / 360);
 	}
@@ -635,7 +652,7 @@
 	/* Factor for conversion to radians using HIGH_PREC_BITS bits
 	 * (Math.PI / 180) * (360.0 / (1 << HIGH_PREC_BITS)) 
 	 */
-	final static double HIGH_PREC_RAD_FACTOR = 2 * Math.PI / FACTOR_HP;
+	private final static double HIGH_PREC_RAD_FACTOR = 2 * Math.PI / FACTOR_HP;
 	
 	/**
 	 * Convert to radians using high precision 
@@ -647,17 +664,21 @@
 	}
 
 	/**
-	 * @return Latitude as signed HIGH_PREC_BITS bit integer 
+	 * @return Latitude from input data as signed HIGH_PREC_BITS bit integer. 
+	 * When this instance was created from double values, the returned value 
+	 * is as close as possible to the original (OSM / polish) position.  
 	 */
 	public int getHighPrecLat() {
-		return (latitude << DELTA_SHIFT) - latDelta;
+		return latHp;
 	}
 
 	/**
-	 * @return Longitude as signed HIGH_PREC_BITS bit integer 
+	 * @return Longitude from input data as signed HIGH_PREC_BITS bit integer 
+	 * When this instance was created from double values, the returned value 
+	 * is as close as possible to the original (OSM / polish) position.  
 	 */
 	public int getHighPrecLon() {
-		return (longitude << DELTA_SHIFT) - lonDelta;
+		return lonHp;
 	}
 	
 	/**
@@ -675,17 +696,27 @@
 	}
 	
 	public Coord getDisplayedCoord(){
-		return new Coord(latitude,longitude);
+		return new Coord(getLatitude(), getLongitude());
 	}
 
+	private int getLatDelta () {
+		return latHp - (getLatitude() << DELTA_SHIFT);
+	}
+
+	private int getLonDelta () {
+		return lonHp - (getLongitude() << DELTA_SHIFT);
+	}
+	
 	/**
 	 * Check if the rounding to 24 bit resolution caused large error. If so, the point may be placed
 	 * at an alternative position. 
-	 * @return true if rounding error is large. 
+	 * @return true if rounding error is large.
 	 */
 	public boolean hasAlternativePos(){
 		if (getOnBoundary())
 			return false;
+		int latDelta = getLatDelta();
+		int lonDelta = getLonDelta();
 		return (Math.abs(latDelta) > MAX_DELTA || Math.abs(lonDelta) > MAX_DELTA);
 	}
 	/**
@@ -698,38 +729,41 @@
 		ArrayList<Coord> list = new ArrayList<>();
 		if (getOnBoundary())
 			return list; 
-		int modLatDelta = 0;
-		int modLonDelta = 0;
+
+		int latDelta = getLatDelta();
+		int lonDelta = getLonDelta();
 		
-		int modLat = latitude;
-		int modLon = longitude;
+		boolean up = false;
+		boolean down = false;
+		boolean left = false;
+		boolean right = false;
+		
 		if (latDelta > MAX_DELTA)
-			modLat--;
+			up = true;
 		else if (latDelta < -MAX_DELTA)
-			modLat++;
+			down = true;
 		if (lonDelta > MAX_DELTA)
-			modLon--;
+			right= true;
 		else if (lonDelta < -MAX_DELTA)
-			modLon++;
-		int latHighPrec = getHighPrecLat();
-		int lonHighPrec = getHighPrecLon();
-		modLatDelta = (modLat << DELTA_SHIFT) - latHighPrec;
-		modLonDelta = (modLon << DELTA_SHIFT) - lonHighPrec;
-		assert modLatDelta >= Byte.MIN_VALUE && modLatDelta <= Byte.MAX_VALUE;
-		assert modLonDelta >= Byte.MIN_VALUE && modLonDelta <= Byte.MAX_VALUE;
-		if (modLat != latitude){
-			if (modLon != longitude)
-				list.add(new Coord(modLat, modLon, (byte)modLatDelta, (byte)modLonDelta));
-			list.add(new Coord(modLat, longitude, (byte)modLatDelta, lonDelta));
-		} 
-		if (modLon != longitude)
-			list.add(new Coord(latitude, modLon, latDelta, (byte)modLonDelta));
-		/* verify math
-		for(Coord co:list){
-			double d = distance(new Coord (co.getLatitude(),co.getLongitude()));
-			assert d < 3.0;
+			left = true;
+		if (down || up) {
+			if (left || right) {
+				Coord mod2 = new Coord(this);
+				mod2.flags |= (left ? LON_DEC : LON_INC);
+				mod2.flags |= (down ? LAT_DEC : LAT_INC);
+				list.add(mod2);
+			}
+			Coord mod1 = new Coord(this);
+			mod1.flags |= (down ? LAT_DEC : LAT_INC);
+			list.add(mod1);
+			
 		}
-		*/
+		if (left || right) {
+			Coord mod = new Coord(this);
+			mod.flags |= (left ? LON_DEC : LON_INC);
+			list.add(mod);
+			
+		}
 		return list;
 	}
 	
@@ -736,9 +770,9 @@
 	/**
 	 * @return approximate distance in cm 
 	 */
-	public short getDistToDisplayedPoint() {
-		if (approxDistanceToDisplayedCoord < 0) {
-			approxDistanceToDisplayedCoord = (short) Math.round(getDisplayedCoord().distance(this) * 100);
+	public short getDistToDisplayedPoint(){
+		if (approxDistanceToDisplayedCoord < 0){
+			approxDistanceToDisplayedCoord = (short)Math.round(getDisplayedCoord().distance(this)*100);
 		}
 		return approxDistanceToDisplayedCoord;
 	}
@@ -763,7 +797,7 @@
 		// check for some daft bugger going past the pole, normalise latitude if so
 		if (Math.abs(lat2) > Math.PI/2) lat2 = lat2>0 ? Math.PI-lat2 : -Math.PI-lat2;
 		double lon2;
-		// catch special case: normalised value would be -8388608
+		// catch special case: normalised value would be -8388608  
 		if (this.getLongitude() == 8388608 && brng == 0)
 			lon2 = lon1;
 		else { 
