Skip to content

Commit aa6d6ee

Browse files
Encoder ready for micrometer precision
1 parent 736e46a commit aa6d6ee

File tree

3 files changed

+72
-58
lines changed

3 files changed

+72
-58
lines changed

src/main/java/com/mapcode/Encoder.java

Lines changed: 41 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static List<Mapcode> encode(
4646
final boolean stopWithOneResult,
4747
final boolean allowWorld) {
4848

49-
return encode(latDeg, lonDeg, territory, isRecursive, stopWithOneResult, allowWorld, null);
49+
return encode(latDeg, lonDeg, territory, stopWithOneResult, allowWorld, null);
5050
}
5151

5252
// ----------------------------------------------------------------------
@@ -59,17 +59,13 @@ static List<Mapcode> encode(
5959
@SuppressWarnings("ConstantConditions")
6060
@Nonnull
6161
private static List<Mapcode> encode(final double argLatDeg, final double argLonDeg,
62-
@Nullable final Territory territory, final boolean isRecursive, final boolean limitToOneResult, final boolean allowWorld,
62+
@Nullable final Territory territory, final boolean limitToOneResult, final boolean allowWorld,
6363
@Nullable final Territory argStateOverride) {
64-
LOG.trace("encode: latDeg={}, lonDeg={}, territory={}, isRecursive={}, limitToOneResult={}, allowWorld={}",
65-
argLatDeg, argLonDeg, (territory == null) ? null : territory.name(), isRecursive, limitToOneResult,
64+
LOG.trace("encode: latDeg={}, lonDeg={}, territory={}, limitToOneResult={}, allowWorld={}",
65+
argLatDeg, argLonDeg, (territory == null) ? null : territory.name(), limitToOneResult,
6666
allowWorld);
6767

68-
final double latDeg = Point.mapToLat(argLatDeg);
69-
final double lonDeg = Point.mapToLon(argLonDeg);
70-
Territory stateOverride = argStateOverride;
71-
72-
final Point pointToEncode = Point.fromDeg(latDeg, lonDeg);
68+
final Point pointToEncode = Point.fromDeg(argLatDeg, argLonDeg);
7369
final List<SubArea> areas = SubArea.getAreasForPoint(pointToEncode);
7470
final List<Mapcode> results = new ArrayList<Mapcode>();
7571

@@ -102,14 +98,8 @@ private static List<Mapcode> encode(final double argLatDeg, final double argLonD
10298
} else if (Data.recType(i) > 1) {
10399
mapcode = encodeAutoHeader(pointToEncode, i);
104100
} else if ((i == upto) && Data.isRestricted(i) && (currentEncodeTerritory.getParentTerritory() != null)) {
105-
106-
if (!isRecursive) {
107-
stateOverride = currentEncodeTerritory;
108-
results.addAll(encode(argLatDeg, argLonDeg, currentEncodeTerritory.getParentTerritory(), true, limitToOneResult,
109-
allowWorld, stateOverride));
110-
stateOverride = null;
111-
}
112-
101+
results.addAll(encode(argLatDeg, argLonDeg, currentEncodeTerritory.getParentTerritory(), limitToOneResult,
102+
allowWorld, currentEncodeTerritory));
113103
continue;
114104
} else if (!Data.isRestricted(i) || (lastbasesubareaID == from)) {
115105
if (Data.calcCodex(i) < 54) {
@@ -120,10 +110,7 @@ private static List<Mapcode> encode(final double argLatDeg, final double argLonD
120110
if (mapcode.length() > 4) {
121111
mapcode = aeuPack(mapcode, false);
122112

123-
Territory encodeTerritory = currentEncodeTerritory;
124-
if (stateOverride != null) {
125-
encodeTerritory = stateOverride;
126-
}
113+
final Territory encodeTerritory = (argStateOverride != null) ? argStateOverride : currentEncodeTerritory;
127114

128115
// Create new result.
129116
final Mapcode newResult = new Mapcode(mapcode, encodeTerritory);
@@ -146,22 +133,18 @@ private static List<Mapcode> encode(final double argLatDeg, final double argLonD
146133
}
147134
}
148135
}
149-
LOG.trace("encode: isRecursive={}, results={} items", isRecursive, results.size());
136+
LOG.trace("encode: results={} items", results.size());
150137
LOG.trace("");
151138
return results;
152139
}
153140

154-
private static String encodeExtension(final int extrax4, final int extray, final int dividerx4, final int dividery, final int ydirection) {
141+
private static String encodeExtension(final Point pointToEncode, final int extrax4, final int extray, final int dividerx4, final int dividery, final int ydirection) {
155142
int extraDigits = 8; // always generate 8 digits
156-
final double MAX_PRECISION_FACTOR = 810000; // 30^4 (correct for 8 precision digits)
157143

158-
final double encfraclon = 0; // TODO @@@ should be the fraction (of millionths)
159-
final double encfraclat = 0; // TODO @@@
160-
161-
double factorx = MAX_PRECISION_FACTOR * dividerx4;
162-
double factory = MAX_PRECISION_FACTOR * dividery;
163-
double valx = (MAX_PRECISION_FACTOR * extrax4) + encfraclon;
164-
double valy = (MAX_PRECISION_FACTOR * extray ) + (ydirection * encfraclat);
144+
double factorx = Point.MAX_PRECISION_FACTOR * dividerx4;
145+
double factory = Point.MAX_PRECISION_FACTOR * dividery;
146+
double valx = (Point.MAX_PRECISION_FACTOR * extrax4) + pointToEncode.LonFractions();
147+
double valy = (Point.MAX_PRECISION_FACTOR * extray ) + (ydirection * pointToEncode.LatFractions());
165148

166149
String s = "-";
167150

@@ -184,8 +167,7 @@ private static String encodeExtension(final int extrax4, final int extray, final
184167
return s;
185168
}
186169

187-
private static String encodeGrid(final int m, final Point point) {
188-
Point pointToEncode = point;
170+
private static String encodeGrid(final int m, final Point pointToEncode) {
189171
int codexm = Data.calcCodex(m);
190172
final int orgcodex = codexm;
191173
if (codexm == 21) {
@@ -215,14 +197,13 @@ else if (codexm == 14) {
215197
rely = rely / ygridsize;
216198

217199
final int xgridsize = (maxx - minx + divx - 1) / divx;
218-
int relx = pointToEncode.getLonMicroDeg() - minx;
200+
int x = pointToEncode.getLonMicroDeg();
201+
int relx = x - minx;
219202
if (relx < 0) {
220-
pointToEncode =
221-
Point.fromMicroDeg(pointToEncode.getLatMicroDeg(), pointToEncode.getLonMicroDeg() + 360000000);
203+
x += 360000000;
222204
relx += 360000000;
223205
} else if (relx >= 360000000) {
224-
pointToEncode =
225-
Point.fromMicroDeg(pointToEncode.getLatMicroDeg(), pointToEncode.getLonMicroDeg() - 360000000);
206+
x -= 360000000;
226207
relx -= 360000000;
227208
}
228209
if (relx < 0) {
@@ -254,7 +235,7 @@ else if (codexm == 14) {
254235

255236
result += '.';
256237

257-
int difx = pointToEncode.getLonMicroDeg() - relx;
238+
int difx = x - relx;
258239
int dify = pointToEncode.getLatMicroDeg() - rely;
259240

260241
final int extrax = difx % dividerx;
@@ -279,7 +260,7 @@ else if (codexm == 14) {
279260
result = result.charAt(0) + "." + result.charAt(1) + result.substring(3);
280261
}
281262

282-
result += encodeExtension(extrax << 2, extray, dividerx << 2, dividery, 1); // grid
263+
result += encodeExtension(pointToEncode, extrax << 2, extray, dividerx << 2, dividery, 1); // grid
283264

284265
return Data.headerLetter(m) + result;
285266
}
@@ -325,11 +306,14 @@ private static String encodeAutoHeader(final Point pointToEncode, final int this
325306
final int extrax = (pointToEncode.getLonMicroDeg() - minx) % dividerx;
326307

327308
final int dividery = (((maxy - miny) + h) - 1) / h;
328-
final int vy = (maxy - pointToEncode.getLatMicroDeg()) / dividery;
329-
final int extray = (maxy - pointToEncode.getLatMicroDeg()) % dividery;
309+
int vy = (maxy - pointToEncode.getLatMicroDeg()) / dividery;
310+
int extray = (maxy - pointToEncode.getLatMicroDeg()) % dividery;
330311

331312
int value = (vx / 168) * (h / 176);
332-
// placeholder for fraclat
313+
if ((extray==0) && (pointToEncode.LatFractions() > 0)) {
314+
vy--;
315+
extray += dividery;
316+
}
333317
value += (vy / 176);
334318

335319
final int codexlen = (codexm / 10) + (codexm % 10);
@@ -338,7 +322,7 @@ private static String encodeAutoHeader(final Point pointToEncode, final int this
338322
autoheader_result.append(encodeTriple(vx % 168, vy % 176));
339323

340324
autoheader_result.append(
341-
encodeExtension(extrax << 2, extray, dividerx << 2, dividery, -1)); // for encodeAutoHeader
325+
encodeExtension(pointToEncode, extrax << 2, extray, dividerx << 2, dividery, -1)); // AutoHeader
342326
return autoheader_result.toString();
343327
}
344328

@@ -359,9 +343,6 @@ private static String encodeNameless(final Point pointToEncode, final int index,
359343
final int r = 31 % a;
360344
final int nrX = index - first_nameless_record;
361345

362-
final int x = pointToEncode.getLonMicroDeg();
363-
final int y = pointToEncode.getLatMicroDeg();
364-
365346
int storage_offset;
366347

367348
if ((codexm != 21) && (a <= 31)) {
@@ -396,13 +377,20 @@ private static String encodeNameless(final Point pointToEncode, final int index,
396377
final int miny = Data.getBoundaries(index).getMinY();
397378

398379
final int dividerx4 = xDivider(miny, maxy);
399-
final int xFracture = 0;
400-
final int dx = ((4 * (x - minx)) + xFracture) / dividerx4;
401-
final int extrax4 = ((x - minx) * 4) - (dx * dividerx4); // like modulus, but with floating point value
380+
final int xFracture = (int) (pointToEncode.LonFractions() / Point.MAX_PRECISION_FACTOR);
381+
final int dminx = pointToEncode.getLonMicroDeg() - minx;
382+
final int dx = ((4 * dminx) + xFracture) / dividerx4;
383+
final int extrax4 = (4 * dminx) - (dx * dividerx4); // like modulus, but with floating point value
402384

403385
final int dividery = 90;
404-
final int dy = (maxy - y) / dividery;
405-
final int extray = (maxy - y) % dividery;
386+
final int dmaxy = maxy - pointToEncode.getLatMicroDeg();
387+
int dy = dmaxy / dividery;
388+
int extray = dmaxy % dividery;
389+
390+
if ((extray == 0) && (pointToEncode.LatFractions()>0)) {
391+
dy--;
392+
extray += dividery;
393+
}
406394

407395
int v = storage_offset;
408396
if (Data.isSpecialShape(index)) {
@@ -428,7 +416,7 @@ private static String encodeNameless(final Point pointToEncode, final int index,
428416
result = result.substring(0, 3) + '.' + result.substring(3);
429417
}
430418
}
431-
result += encodeExtension(extrax4, extray, dividerx4, dividery, -1); // for encodeNameless
419+
result += encodeExtension(pointToEncode, extrax4, extray, dividerx4, dividery, -1); // for encodeNameless
432420

433421
return result;
434422
}

src/main/java/com/mapcode/Point.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ public static Point fromDeg(final double latDeg, final double lonDeg) {
5757
return new Point(latDeg, lonDeg, true);
5858
}
5959

60+
// Constants to handle fractions without floating point error accumulation
61+
private static final double MICRODEG_TO_DEG_FACTOR = 1000000.0;
62+
public static final double MAX_PRECISION_FACTOR = 810000.0;
63+
public static final double FRACLON_PRECISION_FACTOR = 3240000.0;
64+
private static final double MICROLAT_MAX_PRECISION_FACTOR = (MICRODEG_TO_DEG_FACTOR*MAX_PRECISION_FACTOR);
65+
private static final double MICROLON_MAX_PRECISION_FACTOR = (MICRODEG_TO_DEG_FACTOR*FRACLON_PRECISION_FACTOR);
66+
6067
/**
6168
* Get the latitude in degrees.
6269
*
@@ -77,6 +84,23 @@ public double getLonDeg() {
7784
return lonDeg;
7885
}
7986

87+
88+
/**
89+
* Returns "fractions", which is a whole number of 1/MICROLON_MAX_PRECISION_FACTORth degrees versus the millionths of degrees
90+
*/
91+
public double LonFractions() {
92+
assert defined;
93+
return 0;
94+
}
95+
/**
96+
* Returns "fractions", which is a whole number of 1/MICROLAT_MAX_PRECISION_FACTORth degrees versus the millionths of degrees
97+
*/
98+
public double LatFractions() {
99+
assert defined;
100+
return 0;
101+
}
102+
103+
80104
/**
81105
* Create a random point, uniformly distributed over the surface of the Earth.
82106
*
@@ -224,7 +248,6 @@ private Point(final double latDeg, final double lonDeg, final boolean wrap) {
224248
/**
225249
* Package private methods. Only used in the mapcode implementation modules.
226250
*/
227-
static final double MICRODEG_TO_DEG_FACTOR = 1000000.0;
228251
static final int LON_MICRODEG_MIN = degToMicroDeg(LON_DEG_MIN);
229252
static final int LON_MICRODEG_MAX = degToMicroDeg(LON_DEG_MAX);
230253
static final int LAT_MICRODEG_MIN = degToMicroDeg(LAT_DEG_MIN);

src/test/java/com/mapcode/DecoderTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,17 @@ public void highPrecisionFullMapcode() throws Exception {
103103
assertEqualsWithinMillionth("decode hi-precision latitude", 52376512, point.getLatMicroDeg());
104104
assertEqualsWithinMillionth("decode hi-precision longitude", 4908540, point.getLonMicroDeg());
105105
point = MapcodeCodec.decode("NLD P42.NB1-0");
106-
assertEquals("decode 8-precision latitude", 51999955, point.getLatMicroDeg());
107-
assertEquals("decode 8-precision longitude", 4999901, point.getLonMicroDeg());
106+
assertEquals("decode 8-precision latitude", 51999954, point.getLatMicroDeg());
107+
assertEquals("decode 8-precision longitude", 4999900, point.getLonMicroDeg());
108+
LOG.info("NLD P42.NB1-0 = " + point);
108109
point = MapcodeCodec.decode("NLD P42.NB1-123");
109110
assertEquals("decode 8-precision latitude", 51999948, point.getLatMicroDeg());
110-
assertEquals("decode 8-precision longitude", 4999925, point.getLonMicroDeg());
111+
assertEquals("decode 8-precision longitude", 4999924, point.getLonMicroDeg());
112+
LOG.info("NLD P42.NB1-123 = " + point);
111113
point = MapcodeCodec.decode("NLD P42.NB1-MVRGBD0S");
112-
assertEquals("decode 8-precision latitude", 52000000, point.getLatMicroDeg());
114+
assertEquals("decode 8-precision latitude", 51999999, point.getLatMicroDeg());
113115
assertEquals("decode 8-precision longitude", 5000000, point.getLonMicroDeg());
116+
LOG.info("NLD P42.NB1-MVRGBD0S = " + point);
114117
}
115118

116119
@Test

0 commit comments

Comments
 (0)