Hi Gerd

Here is combined patch based on current trunk.

I haven't been through WrongAngleFixer in any detail yet.

Ticker


On Tue, 2017-03-14 at 15:30 +0000, Gerd Petermann wrote:
> Hi Ticker,
> 
> I waited for further feedback from you. Please post your modified
> version or a patch.
> 
> Gerd
> ________________________________________
> Von: mkgmap-dev <[email protected]> im Auftrag
> von Ticker Berkin <[email protected]>
> Gesendet: Dienstag, 14. März 2017 10:53:43
> An: [email protected]
> Betreff: Re: [mkgmap-dev] [Patch v1]  differet Coord implementation
> 
> Hi Gerd
> 
> Are you still planning to implement this patch. I've been using it
> for
> a while with some additional changes and I didn't have any problems.
> I
> think it is tidier and makes things more obvious.
> 
> The addition changes I had were roughly as I listed in a follow-up:
> 
> ... much clearer and have the same effect if
>  imgFmt.Utils.toMapUnit
>  new:Coord.toHighPrec
> were re-coded as
>   return Math.round(degrees / 360.0D * (1 << resolution))
> 
> imgfmt.Utils.roundup()) change to rounding to nearest integer rather
> than being like ceiling():
>         public static int roundUp(int val, int shift) {
>                 if (shift <= 0)
>                         return val;
>                 return (((val >> (shift-1)) + 1) >> 1) << shift;
>         }
> 
> 
> In the new versions of Coord.getLatitude/Longitude(), could have/use
>         final int DELTA_HALF = 1 << (DELTA_SHIFT - 1);
> ...
>         public int getLatitude() {
>                 int lat24 = (latHp + DELTA_HALF) >> DELTA_SHIFT;
> ...
>         public int getLongitude() {
>                 int lon24;
>                 if (HIGH_PREC_BITS == 32 && lonHp ==
> Integer.MAX_VALUE)
>                         lon24 = (lonHp >> DELTA_SHIFT) + 1;
>                 else
>                         lon24 = (lonHp + DELTA_HALF) >> DELTA_SHIFT;
> ...
> 
> Ticker
> 
> On Fri, 2017-02-24 at 15:27 +0000, Gerd Petermann wrote:
> > Hi programmers,
> > 
> > 
> > After writing my findings here
> > http://www.mkgmap.org.uk/pipermail/mkgmap-dev/2017q1/026317.html
> > I did some cycling and decided that I might still be better to use
> > the attached patch.
> > 
> > It works better with 32 bits and is probably a bit easier to
> > understand. The major difference is
> > that it calculates the Garmin position of a point from the already
> > rounded 30 (or 32 bit) value.
> > In some cases this means that points in the map move by one, e.g.
> > one with 52.2452629, 21.0833536
> > 
> > appears on the right grid point instead of the left (it is very
> > close
> > to the middle).
> > 
> > Gerd
> > 
> > _______________________________________________
> > mkgmap-dev mailing list
> > [email protected]
> > http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
> _______________________________________________
> mkgmap-dev mailing list
> [email protected]
> http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
> _______________________________________________
> mkgmap-dev mailing list
> [email protected]
> http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev
Index: src/uk/me/parabola/imgfmt/Utils.java
===================================================================
--- src/uk/me/parabola/imgfmt/Utils.java	(revision 3846)
+++ src/uk/me/parabola/imgfmt/Utils.java	(working copy)
@@ -108,15 +108,11 @@
 	 * A map unit is an integer value that is 1/(2^24) degrees of latitude or
 	 * longitude.
 	 *
-	 * @param l The lat or long as decimal degrees.
+	 * @param degrees The lat or long as decimal degrees.
 	 * @return An integer value in map units.
 	 */
-	public static int toMapUnit(double l) {
-		double DELTA = 360.0D / (1 << 24) / 2; //Correct rounding
-		if (l > 0)
-			return (int) ((l + DELTA) * (1 << 24)/360);
-		else
-			return (int) ((l - DELTA) * (1 << 24)/360);
+	public static int toMapUnit(double degrees) {
+		return (int)Math.round(degrees / 360D * (1 << 24));
 	}
 
 	/**
@@ -223,7 +219,9 @@
 	 * @return the rounded integer.
 	 */
 	public static int roundUp(int val, int shift) {
-		return (val + (1 << shift) - 1) >>> shift << shift;
+		if (shift <= 0)
+			return val;
+		return (((val >> (shift-1)) + 1) >> 1) << shift;
 	} 
 	
 	/**
Index: src/uk/me/parabola/imgfmt/app/Coord.java
===================================================================
--- src/uk/me/parabola/imgfmt/app/Coord.java	(revision 3846)
+++ 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,35 +52,46 @@
 	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;
+	private final static int DELTA_HALF = 1 << (DELTA_SHIFT - 1);
 	
 	public final static double R = 6378137.0; // Radius of earth at equator as defined by WGS84
 	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 +98,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 +124,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 + DELTA_HALF) >> 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 + DELTA_HALF) >> DELTA_SHIFT;
+		if ((flags & LON_DEC) != 0)
+			--lon24; 
+		else if ((flags & LON_INC) != 0)
+			++lon24;
+		return lon24;
 	}
 
 	/**
@@ -380,7 +399,7 @@
 		// max lat: 4194304
 		// max lon: 8388608
 		// max hashCode: 2118123520 < 2147483647 (Integer.MAX_VALUE)
-		return 503 * latitude + longitude;
+		return 503 * getLatitude() + getLongitude();
 	}
 
 	/**
@@ -390,7 +409,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 +518,6 @@
 		double dist = distRad * R;
 
 		return dist;
-		
 	}
 
 	/**
@@ -583,12 +601,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 +615,7 @@
 	 * @return a string representation of the object.
 	 */
 	public String toString() {
-		return (latitude) + "/" + (longitude);
+		return (getLatitude()) + "/" + (getLongitude());
 	}
 
 	public String toDegreeString() {
@@ -626,16 +644,14 @@
 	 * @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
-		double v = (degrees > 0) ? degrees + DELTA : degrees - DELTA;
-		return (int) (v * FACTOR_HP / 360);
+	public static int toHighPrec(double degrees) {
+		return (int)Math.round(degrees / 360D * FACTOR_HP);
 	}
 
 	/* 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 +663,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 +695,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 +728,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 +769,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 +796,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 { 
_______________________________________________
mkgmap-dev mailing list
[email protected]
http://www.mkgmap.org.uk/mailman/listinfo/mkgmap-dev

Reply via email to