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-devIndex: 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