Skip to content

Commit 4bbeeb3

Browse files
2.1.4 Added isInsideTerritory to API, adjusted unit test
1 parent a493fe5 commit 4bbeeb3

File tree

3 files changed

+93
-40
lines changed

3 files changed

+93
-40
lines changed

src/main/java/com/mapcode/Decoder.java

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -227,34 +227,46 @@ static Point decode(@Nonnull final String argMapcode,
227227

228228

229229
if (Data.isRestricted(i) && !mapcodeZone.isEmpty()) {
230-
boolean fitssomewhere = false;
230+
int nrZoneOverlaps = 0;
231231
int j;
232232
Point result = mapcodeZone.midPoint();
233+
// see if midpoint of mapcode zone is in any sub-area...
233234
for (j = i - 1; j >= from; j--) {
234235
if (!Data.isRestricted(j)) {
235236
final int xdiv8 = Common.xDivider(Data.getBoundaries(j).getMinY(),
236237
Data.getBoundaries(j).getMaxY()) / 4;
237238
if (Data.getBoundaries(j).containsPoint(result)) {
238-
fitssomewhere = true;
239+
nrZoneOverlaps++;
239240
break;
240241
}
241242
}
242243
}
243244

244-
if (!fitssomewhere) { // FORCE_RECODE
245+
if (nrZoneOverlaps == 0) {
246+
// see if mapcode zone OVERLAPS any sub-area...
247+
MapcodeZone zfound = MapcodeZone.empty();
245248
for (j = from; j < i; j++) { // try all smaller rectangles j
246-
if (!Data.isRestricted(j)) {
247-
MapcodeZone z = mapcodeZone.restrictZoneTo(Data.getBoundaries(j));
248-
if (!z.isEmpty()) {
249-
mapcodeZone.fillFrom(z);
250-
fitssomewhere = true;
251-
break;
252-
}
253-
}
249+
if (!Data.isRestricted(j)) {
250+
MapcodeZone z = mapcodeZone.restrictZoneTo(Data.getBoundaries(j));
251+
if (!z.isEmpty()) {
252+
nrZoneOverlaps++;
253+
if (nrZoneOverlaps == 1) {
254+
// first fit! remember...
255+
zfound.fillFrom(z);
256+
}
257+
else { // nrZoneOverlaps > 1
258+
// more than one hit
259+
break; // give up!
260+
}
261+
}
262+
}
263+
}
264+
if (nrZoneOverlaps == 1) { // intersected exactly ONE sub-area?
265+
mapcodeZone.fillFrom(zfound); // use the intersection found...
254266
}
255-
} //FORCE_RECODE
267+
}
256268

257-
if (!fitssomewhere) {
269+
if (nrZoneOverlaps == 0) {
258270
mapcodeZone.setEmpty();
259271
}
260272
}
@@ -921,8 +933,6 @@ private static MapcodeZone decodeExtension(final int y, final int x, final int d
921933
mapcodeZone.fminy = (extremeLatMicroDeg * Point.MICROLAT_TO_FRACTIONS_FACTOR);
922934
}
923935
}
924-
925-
// return the coordinate in the center of the mapcode-defined zone
926936
return mapcodeZone;
927937
}
928938
}

src/main/java/com/mapcode/MapcodeCodec.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,45 @@ public static Point decode(
250250
}
251251
return point;
252252
}
253+
254+
/**
255+
* Is coordinate within a given territory?
256+
*
257+
* @param point Latitude/Longitude in degrees
258+
* @param territory Territory
259+
* @return true iff the coordinate is inside the specified territory.
260+
*/
261+
public static boolean isInsideTerritory(@Nonnull final Point point, @Nonnull final Territory territory) {
262+
final int ccode = territory.getNumber();
263+
final int from = DataAccess.dataFirstRecord(ccode);
264+
final int upto = DataAccess.dataLastRecord(ccode);
265+
if (Data.getBoundaries(upto).containsPoint(point)) {
266+
for (int m = upto; m >= from; m--) {
267+
if (!Data.isRestricted(m)) {
268+
if (Data.getBoundaries(m).containsPoint(point)) {
269+
return true;
270+
}
271+
}
272+
}
273+
}
274+
return false;
275+
}
276+
277+
/**
278+
* Is coordinate "fully within" a given territory?
279+
*
280+
* @param point Latitude/Longitude in degrees
281+
* @param territory Territory
282+
* @return true iff the coordinate is inside the specified territory, and furthermore,
283+
* IF the territory has a perent territory: inside the parent territory.
284+
*
285+
* Note that for the mapcode system, the following should hold: IF a point p has a
286+
* mapcode M, THEN decode(M) delivers a point q within maxErrorInMeters() of p.
287+
* Furthermore, encode(q) must yield back M *unless* point q is not "fully inside"
288+
* the mapcode territory.
289+
*/
290+
public static boolean isFullyInsideTerritory(@Nonnull final Point point, @Nonnull final Territory territory) {
291+
final Territory parentTerritory = territory.getParentTerritory();
292+
return isInsideTerritory(point, territory) && ((parentTerritory == null) || isInsideTerritory(point, parentTerritory));
293+
}
253294
}

src/test/java/com/mapcode/EncodeDecodeTest.java

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public void run() {
9090
LOG.info("encodeDecodeTest: #{}/{}", count, NUMBER_OF_POINTS);
9191
}
9292

93-
try {
93+
{
9494
// Walk through the list in reverse order to get International first.
9595
for (final Territory territory : Territory.values()) {
9696
final List<Mapcode> resultsLimited = MapcodeCodec.encode(latDeg, lonDeg, territory);
@@ -102,7 +102,16 @@ public void run() {
102102
// Check max distance at every nrDigits, and verify encode(decode(m))=m
103103
for (int nrDigits = 0; nrDigits <= 8; nrDigits++) {
104104
final String codePrecision = mapcode.getCode(nrDigits);
105-
final Point decodeLocation = MapcodeCodec.decode(codePrecision, territory);
105+
final Point decodeLocation;
106+
try {
107+
decodeLocation = MapcodeCodec.decode(codePrecision, territory);
108+
} catch (final UnknownMapcodeException e) {
109+
LOG.error("FAILED {} Decode({} {}) generated from ({}, {}) in {}", count, territory, codePrecision, latDeg, lonDeg );
110+
LOG.error("encodeDecodeTest: Unknown mapcode exception", e);
111+
errors.getAndIncrement();
112+
continue;
113+
}
114+
106115
final double distance = Point.distanceInMeters(encode, decodeLocation);
107116
if (distance >= Mapcode.getSafeMaxOffsetInMeters(nrDigits)) {
108117
LOG.error("encodeDecodeTest: " + mapcode + " digits = " + nrDigits + " distance = " + distance + " >= " + Mapcode.getSafeMaxOffsetInMeters(nrDigits));
@@ -119,31 +128,27 @@ public void run() {
119128
break;
120129
}
121130
}
122-
if (!found) {
123-
// perhaps it was inherited from the parent?
124-
final Territory parentTerritory = territory.getParentTerritory();
125-
if (parentTerritory != null) {
126-
final List<Mapcode> recodedMapcodesFromParent = MapcodeCodec.encode(decodeLocation,parentTerritory);
127-
for (final Mapcode candidate : recodedMapcodesFromParent) {
128-
if (codePrecision.equals(candidate.getCode(nrDigits))) {
129-
found = true;
130-
break;
131+
if (!found) { // not found?
132+
if (MapcodeCodec.isFullyInsideTerritory(decodeLocation, territory)) { // but should be found!
133+
// perhaps it was inherited from the parent?
134+
final Territory parentTerritory = territory.getParentTerritory();
135+
if (parentTerritory != null) {
136+
final List<Mapcode> recodedMapcodesFromParent = MapcodeCodec.encode(decodeLocation,parentTerritory);
137+
for (final Mapcode candidate : recodedMapcodesFromParent) {
138+
if (codePrecision.equals(candidate.getCode(nrDigits))) {
139+
found = true;
140+
break;
141+
}
131142
}
132143
}
133-
if (!found) { // provide parent info for error that will fire below
134-
LOG.info("Re-encode{} of {} failed for {} {} from ({},{})", nrDigits, decodeLocation, parentTerritory, codePrecision, latDeg, lonDeg);
135-
for (final Mapcode candidate : recodedMapcodesFromParent) {
136-
LOG.info(" * parent candidate: {}", candidate.getCode(nrDigits));
144+
if (!found) {
145+
LOG.error("Re-encode{} of {} failed for {} {} from ({},{})", nrDigits, decodeLocation, territory, codePrecision, latDeg, lonDeg);
146+
errors.getAndIncrement();
147+
for (final Mapcode candidate : recodedMapcodes) {
148+
LOG.info(" * candidate: {}", candidate.getCode(nrDigits));
137149
}
138150
}
139151
}
140-
if (!found) {
141-
LOG.error("Re-encode{} of {} failed for {} {} from ({},{})", nrDigits, decodeLocation, territory, codePrecision, latDeg, lonDeg);
142-
errors.getAndIncrement();
143-
for (final Mapcode candidate : recodedMapcodes) {
144-
LOG.info(" * candidate: {}", candidate.getCode(nrDigits));
145-
}
146-
}
147152
}
148153
}
149154
}
@@ -161,9 +166,6 @@ public void run() {
161166
}
162167
}
163168
}
164-
} catch (final UnknownMapcodeException e) {
165-
LOG.error("encodeDecodeTest: Unknown mapcode exception", e);
166-
errors.getAndIncrement();
167169
}
168170
}
169171
});

0 commit comments

Comments
 (0)