From dda1ea9edb2db523e3a098d7089454b2cdeeb496 Mon Sep 17 00:00:00 2001 From: Mapcode C Developer Date: Wed, 2 Sep 2015 20:59:44 +0200 Subject: [PATCH] 2.1.5 Much stricter unit test --- README.md | 6 ++-- mapcodelib/mapcoder.c | 74 ++++++++++++++++++++++++++--------------- mapcodelib/mapcoder.h | 2 +- unitttest/decode_test.h | 2 ++ unitttest/unittest.c | 64 ++++++++++++++++++++++++++++++++++- 5 files changed, 117 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 6cd965f..788c2df 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,11 @@ decode Mapcodes. # Release Notes -* 2.1.4 +* 2.1.5 + + Added stricter unit tests - Added isInsideTerritory to API; +* 2.1.4 Added maxErrorinMeters to API; diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 5bb45a0..cda8473 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -182,11 +182,14 @@ static int isInRange(int x, const int minx, int const maxx) // returns nonzero i return 0; } -static int fitsInside(const point32 *coord32, const int m) { - const mminforec *b = boundaries(m); +static int fitsInsideBoundaries(const point32 *coord32, const mminforec *b) { return (b->miny <= coord32->lat && coord32->lat < b->maxy && isInRange(coord32->lon, b->minx, b->maxx)); } +static int fitsInside(const point32 *coord32, const int m) { + return fitsInsideBoundaries(coord32,boundaries(m)); +} + static int xDivider4(const int miny, const int maxy) { if (miny >= 0) { // both above equator? then miny is closest return xdivider19[(miny) >> 19]; @@ -197,6 +200,22 @@ static int xDivider4(const int miny, const int maxy) { return xdivider19[(-maxy) >> 19]; // both negative, so maxy is closest to equator } +static mminforec *getExtendedBoundaries(mminforec *target, const mminforec *source, int deltaLat, int deltaLon) { + target->miny = source->miny - deltaLat; + target->minx = source->minx - deltaLon; + target->maxy = source->maxy + deltaLat; + target->maxx = source->maxx + deltaLon; + return target; +} + +static int isNearBorderOf(const point32 *coord32, int m) { + mminforec tmp; + const mminforec *b=boundaries(m); + int xdiv8 = xDivider4(b->miny, b->maxy) / 6; // should be /8 but there's some extra margin + return (fitsInsideBoundaries(coord32, getExtendedBoundaries(&tmp,boundaries(m),+60,+xdiv8)) && + (! fitsInsideBoundaries(coord32, getExtendedBoundaries(&tmp,boundaries(m),-60,-xdiv8)))); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Lowlevel ccode, iso, and disambiguation @@ -1311,7 +1330,8 @@ static void encoderEngine(const int ccode, const encodeRec *enc, const int stop_ from = firstrec(ccode); upto = lastrec(ccode); - if (ccode != ccode_earth) { + if (ccode != ccode_earth) // @@@ why? + { if (!fitsInside(&enc->coord32, upto)) { return; } @@ -1334,9 +1354,7 @@ static void encoderEngine(const int ccode, const encodeRec *enc, const int stop_ else if (recType(i) > 1) { encodeAutoHeader(result, enc, i, extraDigits); } - else if (i == upto && isRestricted(i) && - isSubdivision(ccode)) // if the last item is a reference to a state's country - { + else if ((i == upto) && isSubdivision(ccode)) { // *** do a recursive call for the parent *** encoderEngine(ParentTerritoryOf(ccode), enc, stop_with_one_result, extraDigits, requiredEncoder, ccode); return; /**/ @@ -2290,33 +2308,35 @@ double maxErrorInMeters(int extraDigits) { return maxErrorInMetersForDigits[extraDigits]; } -// returns nonzero if coordinate is inside territory -int isInsideTerritory(double lat, double lon, int territoryCode) { +// returns nonzero if coordinate is near more than one territory border +int multipleBordersNearby(double lat, double lon, int territoryCode) { const int ccode = territoryCode - 1; - if ((lat < -90) || (lat > 90) || (ccode < 0) || (ccode > ccode_earth)) { - return 0; // invalid arguments! - } - else { - int m; - point32 coord32; - const int from = firstrec(ccode); - const int upto = lastrec(ccode); - convertCoordsToMicrosAndFractions(&coord32, NULL, NULL, lat, lon); - if (fitsInside(&coord32, upto)) { + if ((ccode >= 0) && (ccode < ccode_earth)) { // valid territory, not earth + const int parentTerritoryCode = getParentCountryOf(territoryCode); + if (parentTerritoryCode >= 0) { + // there is a parent! check its borders as well... + if (multipleBordersNearby(lat, lon, parentTerritoryCode)) { + return 1; + } + } + { + int m; + int nrFound = 0; + const int from = firstrec(ccode); + const int upto = lastrec(ccode); + point32 coord32; + convertCoordsToMicrosAndFractions(&coord32, NULL, NULL, lat, lon); for (m = upto; m >= from; m--) { if (!isRestricted(m)) { - if (fitsInside(&coord32, m)) { - return 1; + if (isNearBorderOf(&coord32, m)) { + nrFound++; + if (nrFound > 1) { + return 1; + } } } } } } - return 0; -} - -// Check if a point is inside a territory and (if it has a parent) also inside its parent territory -int isFullyInsideTerritory(double lat, double lon, int territoryCode) { - return (isInsideTerritory(lat, lon, territoryCode) && - ((getParentCountryOf(territoryCode) < 0) || isInsideTerritory(lat, lon, getParentCountryOf(territoryCode)))); + return 0; } diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 72de34c..cb3d85c 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -18,7 +18,7 @@ extern "C" { #endif -#define mapcode_cversion "2.1.4" +#define mapcode_cversion "2.1.5" #define UWORD unsigned short int // 2-byte unsigned integer. diff --git a/unitttest/decode_test.h b/unitttest/decode_test.h index fc36d30..037b0c2 100644 --- a/unitttest/decode_test.h +++ b/unitttest/decode_test.h @@ -24,6 +24,8 @@ typedef struct { static const encode_test_record encode_test[] = { + {39.730401, -79.9541635, 0, 0, ""}, + {39.730391, -79.954152, 0, 0, ""}, {5.60872800 , -10.17926200, 0, 0, ""}, {1.86496200 , 9.47899500, 0, 0, ""}, {33.864759999999997, 75, 0, 0, ""}, diff --git a/unitttest/unittest.c b/unitttest/unittest.c index e5cf3b8..f2235cb 100644 --- a/unitttest/unittest.c +++ b/unitttest/unittest.c @@ -273,7 +273,7 @@ static void testEncodeAndDecode(const char *str, double y, double x, int localso } if (!found) { // within 7.5 meters, but not reproduced! - if ( isFullyInsideTerritory(lat, lon, tc2) ) { // but SHOULD be reproduced! + if ( ! multipleBordersNearby(lat, lon, tc2) ) { // but SHOULD be reproduced! nrErrors++; printf("*** ERROR *** %s does not re-encode (%0.15f,%0.15f) from (%0.15f,%0.15f)\n", str, lat, lon, y, x); printGeneratedMapcodes("Global ", &mapcodes); @@ -520,6 +520,67 @@ void distance_tests() } +void test_territory_insides() { + if (strcmp(mapcode_cversion,"2.1.5") >=0) { + int i; + struct { + const char *territory; + double lat; + double lon; + int nearborders; + } iTestData[] = { + {"AAA", 0, 0,0}, + {"AAA", 0, 999,0}, + {"AAA", 90, 0,0}, + {"AAA", -90, 0,0}, + {"AAA", 0, 180,0}, + {"AAA", 0, -180,0}, + {"ATA", -90, 0,0}, + {"ATA", -70, 0,0}, + + {"USA", 31, -70,0}, // interational waters (not in state) + {"MEX", 19,-115,0}, // interational waters (not in state) + {"MEX", 18.358525, -114.722672,0}, // Isla Clarion, not in a state + {"MX-ROO", 20, -87,0}, // just in ROO + {"MX-ROO", 20,-87.3,0}, // in ROO because in MEX + {"MEX", 20,-87.3,0}, // in ROO because in MEX + + {"IND", 19, 87, 0}, + + {"NLD", 52.6, 4.8,0}, + {"US-WV", 40.18, -80.87,0}, + {"USA", 40.18, -80.87,0}, + {"US-FL", 24.7, -82.7,0}, + {"USA", 24.7, -82.7,0}, + {"IN-TG", 16.13, 78.75,0}, + {"IN-AP", 16.13, 78.75,0}, + {"IN-MH", 16.13, 78.75,0}, + {"IN-PY", 16.13, 78.75,0}, + {"IND", 16.13, 78.75,0}, + {"USA", 40.7, -74,0}, + + {"US-NY", 40.7, -74,1}, + {"MEX", 20.252060, -89.779821,1}, + {"NLD", 52.467314, 4.494037,1}, + {"MEX",21.431778909671 , -89.779828861356,1}, + {"MEX",21.431788272457 , -89.779820144176,1}, + + {NULL} + }; + + for (i = 0; iTestData[i].territory != NULL; i++) { + int territory = convertTerritoryIsoNameToCode(iTestData[i].territory,0); + nrTests++; + if (multipleBordersNearby(iTestData[i].lat, iTestData[i].lon, territory) != iTestData[i].nearborders) { + nrErrors++; + printf("*** ERROR *** multipleBordersNearby(%+18.13f,%+18.13f, \"%s\") not %d\n", + iTestData[i].lat, iTestData[i].lon, iTestData[i].territory, iTestData[i].nearborders); + } + } + } +} + + void main() { #ifdef XSIDE3 const char *mapcode_dataversion = "undefined"; @@ -536,6 +597,7 @@ void main() { printf("-----------------------------------------------------------\nTerritory tests\n"); printf("%d territories\n", MAX_CCODE); test_territories(); + test_territory_insides(); printf("-----------------------------------------------------------\nFailing decode tests\n"); test_failing_decodes();