diff --git a/README.md b/README.md index a1d0772..3162323 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,14 @@ decode Mapcodes. # Release Notes +* 2.1.3 + + Added useful routine DistanceInMeters to API + +* 2.1.2 + + Rewrote fraction floating points to integer arithmetic + * 2.1.1 Added unittest\*.* which can be compiled and executed to check diff --git a/mapcode_library_c.doc b/mapcode_library_c.doc index 1391a57..d213be0 100644 Binary files a/mapcode_library_c.doc and b/mapcode_library_c.doc differ diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 22c0d59..c31c85b 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#include // strlen strcpy strcat memcpy memmove strstr strchr memcmp strupr +#include // strlen strcpy strcat memcpy memmove strstr strchr memcmp #include // atof #include // toupper #include // floor @@ -80,15 +80,15 @@ typedef struct { // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static int firstrec(int ccode) { return data_start[ccode]; } +static int firstrec(const int ccode) { return data_start[ccode]; } -static int lastrec(int ccode) { return data_start[ccode + 1] - 1; } +static int lastrec(const int ccode) { return data_start[ccode + 1] - 1; } #ifdef FAST_ALPHA -#define ParentLetter(ccode) (parentletter[ccode]) +#define ParentLetter(ccode) ((int)parentletter[ccode]) #else -static int ParentLetter(int ccode) // returns parent index (>0), or 0 +static int ParentLetter(const int ccode) // returns parent index (>0), or 0 { if (ccode >= usa_from && ccode <= usa_upto) { return 1; } if (ccode >= ind_from && ccode <= ind_upto) { return 2; } @@ -103,12 +103,12 @@ static int ParentLetter(int ccode) // returns parent index (>0), or 0 #endif -static int ParentTerritoryOf(int ccode) // returns parent, or -1 +static int ParentTerritoryOf(const int ccode) // returns parent, or -1 { return parentnr[ParentLetter(ccode)]; } -static int isSubdivision(int ccode) { return (ParentTerritoryOf(ccode) >= 0); } +static int isSubdivision(const int ccode) { return (ParentTerritoryOf(ccode) >= 0); } static int coDex(int m) { int c = mminfo[m].flags & 31; @@ -125,7 +125,7 @@ static int coDex(int m) { #define smartDiv(m) (mminfo[m].flags>>16) #define boundaries(m) (&mminfo[m]) -static int isInRange(int x, int minx, int maxx) // returns nonzero if x in the range minx...maxx +static int isInRange(int x, const int minx, int const maxx) // returns nonzero if x in the range minx...maxx { if (minx <= x && x < maxx) { return 1; } if (x < minx) { x += 360000000; } else { x -= 360000000; } // 1.32 fix FIJI edge case @@ -133,12 +133,12 @@ static int isInRange(int x, int minx, int maxx) // returns nonzero if x in the r return 0; } -static int fitsInside(const point32 *coord32, int m) { +static int fitsInside(const point32 *coord32, const int m) { const mminforec *b = boundaries(m); return (b->miny <= coord32->lat && coord32->lat < b->maxy && isInRange(coord32->lon, b->minx, b->maxx)); } -static int xDivider4(int miny, int maxy) { +static int xDivider4(const int miny, const int maxy) { if (miny >= 0) { // both above equator? then miny is closest return xdivider19[(miny) >> 19]; } @@ -148,7 +148,7 @@ static int xDivider4(int miny, int maxy) { return xdivider19[(-maxy) >> 19]; // both negative, so maxy is closest to equator } -static int fitsInsideWithRoom(const point32 *coord32, int m) { +static int fitsInsideWithRoom(const point32 *coord32, const int m) { const mminforec *b = boundaries(m); int xdiv8 = xDivider4(b->miny, b->maxy) / 4; // should be /8 but there's some extra margin return (b->miny - 60 <= coord32->lat && coord32->lat < b->maxy + 60 && isInRange(coord32->lon, b->minx - xdiv8, b->maxx + xdiv8)); @@ -167,7 +167,12 @@ static const char *get_entity_iso3(char *entity_iso3_result, int ccode) { return entity_iso3_result; } -static int disambiguate_str(const char *s, int len) // returns disambiguation 1-8, or negative if error +static void makeupper(char *s) +{ + for(;*s;*s++) { *s = toupper(*s); } +} + +static int disambiguate_str(const char *s, const int len) // returns disambiguation 1-8, or negative if error { const char *p = (len == 2 ? parents2 : parents3); const char *f; @@ -176,7 +181,7 @@ static int disambiguate_str(const char *s, int len) // returns disambiguation 1- if (len != 2 && len != 3) { return -923; } // solve bad args memcpy(country, s, len); country[len] = 0; - strupr(country); + makeupper(country); f = strstr(p, country); if (f == NULL) { return -23; // unknown country @@ -305,8 +310,8 @@ static int ccode_of_iso3(const char *in_iso, int parentcode) { // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -static void encodeExtension(char *result, int extrax4, int extray, int dividerx4, int dividery, int extraDigits, - int ydirection, +static void encodeExtension(char *result, const int extrax4, const int extray, const int dividerx4, + const int dividery, int extraDigits, const int ydirection, const encodeRec *enc) // append extra characters to result for more precision { if (extraDigits > 0) { // anything to do? @@ -358,7 +363,7 @@ static void encodeExtension(char *result, int extrax4, int extray, int dividerx4 // this routine takes the integer-arithmeteic decoding results (in millionths of degrees), adds any floating-point precision digits, and returns the result (still in millionths) static int decodeExtension(decodeRec *dec, int dividerx4, int dividery0, int lon_offset4) { const char *extrapostfix = dec->extension; - double dividerx = dividerx4 / 4.0, dividery = dividery0; + const double dividerx = dividerx4 / 4.0, dividery = dividery0; int lon32 = 0; int lat32 = 0; int processor = 1; @@ -368,13 +373,12 @@ static int decodeExtension(decodeRec *dec, int dividerx4, int dividery0, int lon } while (*extrapostfix) { int column1, row1, column2, row2; - int c1 = *extrapostfix++; - c1 = decodeChar(c1); + const int c1 = decodeChar(*extrapostfix++); if (c1 < 0 || c1 == 30) { return -1; } // illegal extension character row1 = (c1 / 5); column1 = (c1 % 5); if (*extrapostfix) { - int c2 = decodeChar(*extrapostfix++); + const int c2 = decodeChar(*extrapostfix++); if (c2 < 0 || c2 == 30) { return -1; } // illegal extension character row2 = (c2 / 6); column2 = (c2 % 6); @@ -455,12 +459,12 @@ static int decodeBase31(const char *code) { static void encode_triple(char *result, int difx, int dify) { if (dify < 4 * 34) // first 4(x34) rows of 6(x28) wide { - encodeBase31(result, ((difx / 28) + 6 * (dify / 34)), 1); + *result = encode_chars[ ((difx / 28) + 6 * (dify / 34)) ]; encodeBase31(result + 1, ((difx % 28) * 34 + (dify % 34)), 2); } else // bottom row { - encodeBase31(result, (difx / 24) + 24, 1); + *result = encode_chars[ (difx / 24) + 24 ]; encodeBase31(result + 1, (difx % 24) * 40 + (dify - 136), 2); } } // encode_triple @@ -468,7 +472,7 @@ static void encode_triple(char *result, int difx, int dify) { static void decode_triple(const char *result, int *difx, int *dify) { // decode the first character - int c1 = decodeChar(*result++); + const int c1 = decodeChar(*result++); if (c1 < 24) { int m = decodeBase31(result); *difx = (c1 % 6) * 28 + (m / 34); @@ -498,7 +502,7 @@ static int encodeSixWide(int x, int y, int width, int height) { int v; int D = 6; int col = x / 6; - int maxcol = (width - 4) / 6; + const int maxcol = (width - 4) / 6; if (col >= maxcol) { col = maxcol; D = width - maxcol * 6; @@ -511,7 +515,7 @@ static void decodeSixWide(int v, int width, int height, int *x, int *y) { int w; int D = 6; int col = v / (height * 6); - int maxcol = (width - 4) / 6; + const int maxcol = (width - 4) / 6; if (col >= maxcol) { col = maxcol; D = width - maxcol * 6; @@ -525,7 +529,7 @@ static void decodeSixWide(int v, int width, int height, int *x, int *y) { // decodes dec->mapcode in context of territory rectangle m; returns negative if error static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { const char *input = (hasHeaderLetter ? dec->mapcode + 1 : dec->mapcode); - int codexlen = (int) (strlen(input) - 1); + const int codexlen = (int) (strlen(input) - 1); int prelen = (int) (strchr(input, '.') - input); char result[MAX_PROPER_MAPCODE_LEN + 1]; @@ -540,7 +544,7 @@ static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { } { - int postlen = codexlen - prelen; + const int postlen = codexlen - prelen; int divx, divy; @@ -550,11 +554,10 @@ static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { divy = yside[prelen]; } else { - int pw = nc[prelen]; - divx = (pw / divy); + divx = (nc[prelen] / divy); } - if (prelen == 4 && divx == xside[4] && divy == yside[4]) { + if (prelen == 4 && divx == 961 && divy == 961) { char t = result[1]; result[1] = result[2]; result[2] = t; @@ -562,7 +565,7 @@ static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { { int relx, rely; - int v = decodeBase31(result); + const int v = decodeBase31(result); if (divx != divy && prelen > 2) { // special grid, useful when prefix is 3 or more, and not a nice 961x961 @@ -580,18 +583,18 @@ static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { { const mminforec *b = boundaries(m); - int ygridsize = (b->maxy - b->miny + divy - 1) / divy; // microdegrees per cell - int xgridsize = (b->maxx - b->minx + divx - 1) / divx; // microdegrees per cell + const int ygridsize = (b->maxy - b->miny + divy - 1) / divy; // microdegrees per cell + const int xgridsize = (b->maxx - b->minx + divx - 1) / divx; // microdegrees per cell // encode relative to THE CORNER of this cell rely = b->miny + (rely * ygridsize); relx = b->minx + (relx * xgridsize); { - int xp = xside[postlen]; - int dividerx = ((((xgridsize)) + xp - 1) / xp); - int yp = yside[postlen]; - int dividery = ((((ygridsize)) + yp - 1) / yp); + const int xp = xside[postlen]; + const int dividerx = ((xgridsize + xp - 1) / xp); + const int yp = yside[postlen]; + const int dividery = ((ygridsize + yp - 1) / yp); // decoderelative { @@ -629,7 +632,7 @@ static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { } { - int err = decodeExtension(dec, dividerx << 2, dividery, 0); // grid + const int err = decodeExtension(dec, dividerx << 2, dividery, 0); // grid if (err) { return err; } @@ -662,18 +665,22 @@ static int decodeGrid(decodeRec *dec, int m, int hasHeaderLetter) { static void encodeGrid(char *result, const encodeRec *enc, int const m, int extraDigits, char headerLetter) { const mminforec *b = boundaries(m); - int orgcodex = coDex(m); + const int orgcodex = coDex(m); int codexm = orgcodex; - if (codexm == 21) { codexm = 22; } - if (codexm == 14) { codexm = 23; } + if (codexm == 21) { + codexm = 22; + } + else if (codexm == 14) { + codexm = 23; + } *result = 0; if (headerLetter) { result++; } { // encode int divx, divy; - int prelen = codexm / 10; - int postlen = codexm % 10; + const int prelen = codexm / 10; + const int postlen = codexm % 10; divy = smartDiv(m); if (divy == 1) { @@ -681,13 +688,12 @@ static void encodeGrid(char *result, const encodeRec *enc, int const m, int extr divy = yside[prelen]; } else { - int pw = nc[prelen]; - divx = (pw / divy); + divx = (nc[prelen] / divy); } { // grid - int ygridsize = (b->maxy - b->miny + divy - 1) / divy; - int xgridsize = (b->maxx - b->minx + divx - 1) / divx; + const int ygridsize = (b->maxy - b->miny + divy - 1) / divy; + const int xgridsize = (b->maxx - b->minx + divx - 1) / divx; int rely = enc->coord32.lat - b->miny; int x = enc->coord32.lon; int relx = x - b->minx; @@ -719,8 +725,8 @@ static void encodeGrid(char *result, const encodeRec *enc, int const m, int extr encodeBase31(result, v, prelen); } // prefix - if (prelen == 4 && divx == xside[4] && divy == yside[4]) { - char t = result[1]; + if (prelen == 4 && divx == 961 && divy == 961) { + const char t = result[1]; result[1] = result[2]; result[2] = t; } @@ -729,8 +735,8 @@ static void encodeGrid(char *result, const encodeRec *enc, int const m, int extr relx = b->minx + (relx * xgridsize); { // postfix - int dividery = ((((ygridsize)) + yside[postlen] - 1) / yside[postlen]); - int dividerx = ((((xgridsize)) + xside[postlen] - 1) / xside[postlen]); + const int dividery = ((ygridsize + yside[postlen] - 1) / yside[postlen]); + const int dividerx = ((xgridsize + xside[postlen] - 1) / xside[postlen]); int extrax, extray; { @@ -785,15 +791,15 @@ static void encodeGrid(char *result, const encodeRec *enc, int const m, int extr // find first territory rectangle of the same type as m static int firstNamelessRecord(int m, int firstcode) { int i = m; - int codexm = coDex(m); + const int codexm = coDex(m); while (i >= firstcode && coDex(i) == codexm && isNameless(i)) { i--; } return (i + 1); } // count all territory rectangles of the same type as m static int countNamelessRecords(int m, int firstcode) { - int i = firstNamelessRecord(m, firstcode); - int codexm = coDex(m); + const int i = firstNamelessRecord(m, firstcode); + const int codexm = coDex(m); while (coDex(m) == codexm) { m++; } return (m - i); } @@ -804,25 +810,25 @@ static int countNamelessRecords(int m, int firstcode) { static int decodeNameless(decodeRec *dec, int m) { int A, F; char input[8]; - int codexm = coDex(m); - int dc = (codexm != 22) ? 2 : 3; - - int codexlen = (int) (strlen(dec->mapcode) - 1); + const int codexm = coDex(m); + const int codexlen = (int) (strlen(dec->mapcode) - 1); if (codexlen != 4 && codexlen != 5) { return -2; } // solve bad args // copy without dot - strcpy(input, dec->mapcode); - strcpy(input + dc, dec->mapcode + dc + 1); - + { + const int dc = (codexm != 22) ? 2 : 3; + strcpy(input, dec->mapcode); + strcpy(input + dc, dec->mapcode + dc + 1); + } A = countNamelessRecords(m, firstrec(dec->context)); F = firstNamelessRecord(m, firstrec(dec->context)); { - int p = 31 / A; - int r = 31 % A; + const int p = 31 / A; + const int r = 31 % A; int v; int SIDE; int swapletters = 0; @@ -836,13 +842,13 @@ static int decodeNameless(decodeRec *dec, int m) { // now determine X = index of first area, and SIDE if (codexm != 21 && A <= 31) { - int offset = decodeChar(*result); + const int offset = decodeChar(*result); if (offset < r * (p + 1)) { X = offset / (p + 1); } else { - swapletters = (p == 1 && codexm == 22); + swapletters = ((p == 1) && (codexm == 22)); X = r + (offset - (r * (p + 1))) / p; } } @@ -858,7 +864,7 @@ static int decodeNameless(decodeRec *dec, int m) { } else // code==21 || A>=62 { - int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; + const int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; int BASEPOWERA = (BASEPOWER / A); if (A == 62) { BASEPOWERA++; } else { BASEPOWERA = 961 * (BASEPOWERA / 961); } @@ -871,7 +877,7 @@ static int decodeNameless(decodeRec *dec, int m) { if (swapletters) { if (!isSpecialShape22(F + X)) { - char t = result[codexlen - 3]; + const char t = result[codexlen - 3]; result[codexlen - 3] = result[codexlen - 2]; result[codexlen - 2] = t; } @@ -898,17 +904,16 @@ static int decodeNameless(decodeRec *dec, int m) { xSIDE = SIDE = smartDiv(m); b = boundaries(m); - if (isSpecialShape22(m)) { - xSIDE *= SIDE; - SIDE = 1 + ((b->maxy - b->miny) / 90); // side purely on y range - xSIDE = xSIDE / SIDE; - } // decode { int dx, dy; if (isSpecialShape22(m)) { + xSIDE *= SIDE; + SIDE = 1 + ((b->maxy - b->miny) / 90); // side purely on y range + xSIDE = xSIDE / SIDE; + decodeSixWide(v, xSIDE, SIDE, &dx, &dy); dy = SIDE - 1 - dy; } @@ -923,8 +928,8 @@ static int decodeNameless(decodeRec *dec, int m) { } { - int dividerx4 = xDivider4(b->miny, b->maxy); // *** note: dividerx4 is 4 times too large! - int dividery = 90; + const int dividerx4 = xDivider4(b->miny, b->maxy); // *** note: dividerx4 is 4 times too large! + const int dividery = 90; int err; // *** note: FIRST multiply, then divide... more precise, larger rects @@ -950,7 +955,7 @@ static int decodeNameless(decodeRec *dec, int m) { } -static void repack_if_alldigits(char *input, int aonly) { +static void repack_if_alldigits(char *input, const int aonly) { char *s = input; int alldigits = 1; // assume all digits char *e; @@ -973,14 +978,14 @@ static void repack_if_alldigits(char *input, int aonly) { { if (aonly) // v1.50 - encode only using the letter A { - int v = ((*input) - '0') * 100 + ((*s) - '0') * 10 + ((*e) - '0'); + const int v = ((*input) - '0') * 100 + ((*s) - '0') * 10 + ((*e) - '0'); *input = 'A'; *s = encode_chars[v / 32]; *e = encode_chars[v % 32]; } else // encode using A,E,U { - int v = ((*s) - '0') * 10 + ((*e) - '0'); + const int v = ((*s) - '0') * 10 + ((*e) - '0'); *s = encode_chars[(v / 34) + 31]; *e = encode_chars[v % 34]; } @@ -992,7 +997,7 @@ static int unpack_if_alldigits(char *input) { // rewrite all-digit codes char *s = input; char *dotpos = NULL; - int aonly = (*s == 'A' || *s == 'a'); + const int aonly = (*s == 'A' || *s == 'a'); if (aonly) { s++; } //*** v1.50 for (; *s != 0 && s[2] != 0 && s[2] != '-'; s++) { if (*s == '-') { @@ -1007,7 +1012,7 @@ static int unpack_if_alldigits(char *input) if (dotpos) { if (aonly) // v1.50 encoded only with A's { - int v = (s[0] == 'A' || s[0] == 'a' ? 31 : decodeChar(s[0])) * 32 + + const int v = (s[0] == 'A' || s[0] == 'a' ? 31 : decodeChar(s[0])) * 32 + (s[1] == 'A' || s[1] == 'a' ? 31 : decodeChar(s[1])); *input = (char) ('0' + (v / 100)); s[0] = (char) ('0' + ((v / 10) % 10)); @@ -1051,16 +1056,16 @@ static int unpack_if_alldigits(char *input) // *result==0 in case of error static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, int extraDigits, int m) { // determine how many nameless records there are (A), and which one is this (X)... - int A = countNamelessRecords(m, firstrec(input_ctry)); - int X = m - firstNamelessRecord(m, firstrec(input_ctry)); + const int A = countNamelessRecords(m, firstrec(input_ctry)); + const int X = m - firstNamelessRecord(m, firstrec(input_ctry)); *result = 0; { - int p = 31 / A; - int r = 31 % A; // the first r items are p+1 - int codexm = coDex(m); - int codexlen = (codexm / 10) + (codexm % 10); + const int p = 31 / A; + const int r = 31 % A; // the first r items are p+1 + const int codexm = coDex(m); + const int codexlen = (codexm / 10) + (codexm % 10); // determine side of square around centre int SIDE; @@ -1070,7 +1075,7 @@ static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, i int xSIDE, orgSIDE; if (codexm != 21 && A <= 31) { - storage_offset = (X * p + (X < r ? X : r)) * (961 * 961); // p=4,r=3: offset(X)={0,5,10,15,19,23,27}-31 + storage_offset = (X * p + (X < r ? X : r)) * (961 * 961); } else if (codexm != 21 && A < 62) { if (X < (62 - A)) { @@ -1084,7 +1089,7 @@ static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, i } } else { - int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; + const int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; int BASEPOWERA = (BASEPOWER / A); if (A == 62) { BASEPOWERA++; @@ -1099,22 +1104,17 @@ static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, i b = boundaries(m); orgSIDE = xSIDE = SIDE; - if (isSpecialShape22(m)) // - keep the existing rectangle! - { - SIDE = 1 + ((b->maxy - b->miny) / 90); // new side, based purely on y-distance - xSIDE = (orgSIDE * orgSIDE) / SIDE; - } { int v = storage_offset; - int dividerx4 = xDivider4(b->miny, b->maxy); // *** note: dividerx4 is 4 times too large! - int xFracture = (int)(enc->fraclon / MAX_PRECISION_FACTOR); - int dx = (4 * (enc->coord32.lon - b->minx) + xFracture) / dividerx4; // div with quarters - int extrax4 = (enc->coord32.lon - b->minx) * 4 - dx * dividerx4; // mod with quarters + const int dividerx4 = xDivider4(b->miny, b->maxy); // *** note: dividerx4 is 4 times too large! + const int xFracture = (int)(enc->fraclon / MAX_PRECISION_FACTOR); + const int dx = (4 * (enc->coord32.lon - b->minx) + xFracture) / dividerx4; // div with quarters + const int extrax4 = (enc->coord32.lon - b->minx) * 4 - (dx * dividerx4); // mod with quarters - int dividery = 90; - int dy = (b->maxy - enc->coord32.lat) / dividery; // between 0 and SIDE-1 + const int dividery = 90; + int dy = (b->maxy - enc->coord32.lat) / dividery; int extray = (b->maxy - enc->coord32.lat) % dividery; if (extray == 0 && enc->fraclat > 0) { @@ -1123,6 +1123,8 @@ static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, i } if (isSpecialShape22(m)) { + SIDE = 1 + ((b->maxy - b->miny) / 90); // new side, based purely on y-distance + xSIDE = (orgSIDE * orgSIDE) / SIDE; v += encodeSixWide(dx, SIDE - 1 - dy, xSIDE, SIDE); } else { @@ -1141,7 +1143,7 @@ static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, i if (!isSpecialShape22(m)) { if (codexm == 22 && A < 62 && orgSIDE == 961) { - char t = result[codexlen - 2]; + const char t = result[codexlen - 2]; result[codexlen - 2] = result[codexlen]; result[codexlen] = t; } @@ -1159,8 +1161,8 @@ static void encodeNameless(char *result, const encodeRec *enc, int input_ctry, i // decodes dec->mapcode in context of territory rectangle m or one of its mates static int decodeAutoHeader(decodeRec *dec, int m) { const char *input = dec->mapcode; - int codexm = coDex(m); - char *dot = strchr(input, '.'); + const int codexm = coDex(m); + const char *dot = strchr(input, '.'); int STORAGE_START = 0; int value; @@ -1176,7 +1178,7 @@ static int decodeAutoHeader(decodeRec *dec, int m) { const mminforec *b = boundaries(m); // determine how many cells int H = (b->maxy - b->miny + 89) / 90; // multiple of 10m - int xdiv = xDivider4(b->miny, b->maxy); + const int xdiv = xDivider4(b->miny, b->maxy); int W = ((b->maxx - b->minx) * 4 + (xdiv - 1)) / xdiv; int product; @@ -1186,13 +1188,13 @@ static int decodeAutoHeader(decodeRec *dec, int m) { product = (W / 168) * (H / 176) * 961 * 31; if (recType(m) == 2) { - int GOODROUNDER = coDex(m) >= 23 ? (961 * 961 * 31) : (961 * 961); + const int GOODROUNDER = codexm >= 23 ? (961 * 961 * 31) : (961 * 961); product = ((STORAGE_START + product + GOODROUNDER - 1) / GOODROUNDER) * GOODROUNDER - STORAGE_START; } if (value >= STORAGE_START && value < STORAGE_START + product) { - int dividerx = (b->maxx - b->minx + W - 1) / W; - int dividery = (b->maxy - b->miny + H - 1) / H; + const int dividerx = (b->maxx - b->minx + W - 1) / W; + const int dividery = (b->maxy - b->miny + H - 1) / H; int err; value -= STORAGE_START; @@ -1202,8 +1204,8 @@ static int decodeAutoHeader(decodeRec *dec, int m) { int difx, dify; decode_triple(dot + 1, &difx, &dify); // decode bottom 3 chars { - int vx = (value / (H / 176)) * 168 + difx; // is vx/168 - int vy = (value % (H / 176)) * 176 + dify; // is vy/176 + const int vx = (value / (H / 176)) * 168 + difx; // is vx/168 + const int vy = (value % (H / 176)) * 176 + dify; // is vy/176 dec->coord32.lat = b->maxy - vy * dividery; dec->coord32.lon = b->minx + vx * dividerx; @@ -1235,7 +1237,7 @@ static int decodeAutoHeader(decodeRec *dec, int m) { } // encode in m (known to fit) -static void encodeAutoHeader(char *result, const encodeRec *enc, int m, int extraDigits) { +static void encodeAutoHeader(char *result, const encodeRec *enc, const int m, const int extraDigits) { int i; int STORAGE_START = 0; int W, H, xdiv, product; @@ -1243,7 +1245,7 @@ static void encodeAutoHeader(char *result, const encodeRec *enc, int m, int extr // search back to first of the group int firstindex = m; - int codexm = coDex(m); + const int codexm = coDex(m); while (recType(firstindex - 1) > 1 && coDex(firstindex - 1) == codexm) { firstindex--; } @@ -1260,7 +1262,7 @@ static void encodeAutoHeader(char *result, const encodeRec *enc, int m, int extr W = 168 * ((W + 168 - 1) / 168); product = (W / 168) * (H / 176) * 961 * 31; if (recType(i) == 2) { // plus pipe - int GOODROUNDER = codexm >= 23 ? (961 * 961 * 31) : (961 * 961); + const int GOODROUNDER = codexm >= 23 ? (961 * 961 * 31) : (961 * 961); product = ((STORAGE_START + product + GOODROUNDER - 1) / GOODROUNDER) * GOODROUNDER - STORAGE_START; } if (i < m) { @@ -1270,15 +1272,15 @@ static void encodeAutoHeader(char *result, const encodeRec *enc, int m, int extr { // encode - int dividerx = (b->maxx - b->minx + W - 1) / W; - int vx = (enc->coord32.lon - b->minx) / dividerx; - int extrax = (enc->coord32.lon - b->minx) % dividerx; + const int dividerx = (b->maxx - b->minx + W - 1) / W; + const int vx = (enc->coord32.lon - b->minx) / dividerx; + const int extrax = (enc->coord32.lon - b->minx) % dividerx; - int dividery = (b->maxy - b->miny + H - 1) / H; + const int dividery = (b->maxy - b->miny + H - 1) / H; int vy = (b->maxy - enc->coord32.lat) / dividery; int extray = (b->maxy - enc->coord32.lat) % dividery; - int codexlen = (codexm / 10) + (codexm % 10); + const int codexlen = (codexm / 10) + (codexm % 10); int value = (vx / 168) * (H / 176); if (extray == 0 && enc->fraclat > 0) { @@ -1297,9 +1299,8 @@ static void encodeAutoHeader(char *result, const encodeRec *enc, int m, int extr } } -static void encoderEngine(int ccode, const encodeRec *enc, int stop_with_one_result, int extraDigits, - int requiredEncoder, - int result_override) { +static void encoderEngine(const int ccode, const encodeRec *enc, const int stop_with_one_result, + const int extraDigits, const int requiredEncoder, const int ccode_override) { int from, upto; if (enc == NULL || ccode < 0 || ccode > ccode_earth) { @@ -1357,7 +1358,7 @@ static void encoderEngine(int ccode, const encodeRec *enc, int stop_with_one_res repack_if_alldigits(result, 0); if (requiredEncoder < 0 || requiredEncoder == i) { - int cc = (result_override >= 0 ? result_override : ccode); + const int cc = (ccode_override >= 0 ? ccode_override : ccode); if (*result && enc->mapcodes && enc->mapcodes->count < MAX_NR_OF_MAPCODE_RESULTS) { char *s = enc->mapcodes->mapcode[enc->mapcodes->count++]; if (cc == ccode_earth) { @@ -1380,24 +1381,24 @@ static void encoderEngine(int ccode, const encodeRec *enc, int stop_with_one_res // returns nonzero if error static int decoderEngine(decodeRec *dec) { - int hasvowels = 0; - int hasletters = 0; + int ccode; int err = -817; - int codex, prelen; // input codex - int from, upto; // record range for territory - int i; - const char *dot = NULL; + int codex; char *s; { + int hasvowels = 0; + int hasletters = 0; + const char *dot = NULL; + int prelen; int len; char *w; // skip whitesace s = (char *) dec->orginput; while (*s <= 32 && *s > 0) { s++; } // remove trail and overhead - len = strlen(s); + len = (int) strlen(s); if (len > MAX_MAPCODE_RESULT_LEN - 1) { len = MAX_MAPCODE_RESULT_LEN - 1; } while (len > 0 && s[len - 1] <= 32 && s[len - 1] >= 0) { len--; } // copy into dec->minput; @@ -1466,7 +1467,7 @@ static int decoderEngine(decodeRec *dec) { dec->extension = ""; } - codex = prelen * 10 + strlen(dot) - 1; + codex = prelen * 10 + (int) strlen(dot) - 1; if (hasvowels) { if (unpack_if_alldigits(s) <= 0) { @@ -1492,25 +1493,25 @@ static int decoderEngine(decodeRec *dec) { } { - from = firstrec(ccode); - upto = lastrec(ccode); + const int from = firstrec(ccode); + const int upto = lastrec(ccode); + int i; // try all ccode rectangles to decode s (pointing to first character of proper mapcode) for (i = from; i <= upto; i++) { - int r = recType(i); + const int codexi = coDex(i); + const int r = recType(i); if (r == 0) { if (isNameless(i)) { - int codexi = coDex(i); - if ((codexi == 21 && codex == 22) - || (codexi == 22 && codex == 32) - || (codexi == 13 && codex == 23)) { + if (((codexi == 21) && (codex == 22)) + || ((codexi == 22) && (codex == 32)) + || ((codexi == 13) && (codex == 23))) { err = decodeNameless(dec, i); break; } } - else { - int codexi = coDex(i); - if (codexi == codex || (codex == 22 && codexi == 21)) { + else { + if (codexi == codex || ((codex == 22) && (codexi == 21))) { err = decodeGrid(dec, i, 0); // *** make sure decode fits somewhere *** @@ -1575,16 +1576,14 @@ static int decoderEngine(decodeRec *dec) { } } } - else if (r == 1) { - int codexi = coDex(i); + else if (r == 1) { if (codex == codexi + 10 && headerLetter(i) == *s) { err = decodeGrid(dec, i, 1); break; } } - else { //r>1 - int codexi = coDex(i); - if (codex == 23 && codexi == 22 || codex == 33 && codexi == 23) { + else { //r>1 + if (((codex == 23) && (codexi == 22)) || ((codex == 33) && (codexi == 23))) { err = decodeAutoHeader(dec, i); break; } @@ -1642,7 +1641,7 @@ static int decoderEngine(decodeRec *dec) { dec->result.lon = (bmaxx - MICROMETER) / 1000000.0; } // keep in encompassing territory - dec->coord32.lat = (int) floor(dec->result.lat * 1000000); + dec->coord32.lat = (int) floor(dec->result.lat * 1000000); // @@@ not needed dec->coord32.lon = (int) floor(dec->result.lon * 1000000); } // FORCE_RECODE #endif @@ -1653,6 +1652,34 @@ static int decoderEngine(decodeRec *dec) { } + +// PI +#define _PI 3.14159265358979323846 + +// Radius of Earth. +#define EARTH_RADIUS_X_METERS 6378137 +#define EARTH_RADIUS_Y_METERS 6356752 + +// Circumference of Earth. +#define EARTH_CIRCUMFERENCE_X (EARTH_RADIUS_X_METERS * 2 * _PI) +#define EARTH_CIRCUMFERENCE_Y (EARTH_RADIUS_Y_METERS * 2 * _PI) + +// Meters per degree latitude is fixed. For longitude: use factor * cos(midpoint of two degree latitudes). +#define METERS_PER_DEGREE_LAT (EARTH_CIRCUMFERENCE_Y / 360.0) +#define METERS_PER_DEGREE_LON (EARTH_CIRCUMFERENCE_X / 360.0) + +double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double lonDeg2) { + if (lonDeg1 < 0 && lonDeg2 > 1) { lonDeg1 += 360; } + if (lonDeg2 < 0 && lonDeg1 > 1) { lonDeg2 += 360; } + { + const double dy = (latDeg2 - latDeg1) * METERS_PER_DEGREE_LAT; + const double dx = (lonDeg2 - lonDeg1) * METERS_PER_DEGREE_LON * cos((latDeg1 + latDeg2) * _PI / 360.0); + return sqrt(dx * dx + dy * dy); + } +} + + + #ifdef SUPPORT_FOREIGN_ALPHABETS // WARNING - these alphabets have NOT yet been released as standard! use at your own risk! check www.mapcode.com for details. @@ -1748,8 +1775,8 @@ char *convertToRoman(char *asciibuf, int maxlen, const UWORD *s) { } -static UWORD *encode_utf16(UWORD *unibuf, int maxlen, const char *mapcode, - int language) // convert mapcode to language (0=roman 1=greek 2=cyrillic 3=hebrew) +static UWORD *encode_utf16(UWORD *unibuf, const int maxlen, const char *mapcode, + const int language) // convert mapcode to language (0=roman 1=greek 2=cyrillic 3=hebrew) { UWORD *w = unibuf; const UWORD *e = w + maxlen - 1; @@ -1854,7 +1881,7 @@ int compareWithMapcodeFormat(const char *s, int fullcode) { } else if (*s == ' ' || *s == '\t') { token = TOKENSEP; } else { - signed char c = decode_chars[(unsigned char) *s]; + const signed char c = decode_chars[(unsigned char) *s]; if (c < 0) { // vowel or illegal? token = TOKENVOWEL; vowels++; // assume vowel (-2,-3,-4) @@ -1892,9 +1919,9 @@ int compareWithMapcodeFormat(const char *s, int fullcode) { // pass point to an array of pointers (at least 42), will be made to point to result strings... // returns nr of results; -static int encodeLatLonToMapcodes_internal(char **v, Mapcodes *mapcodes, double lat, double lon, int tc, - int stop_with_one_result, int requiredEncoder, - int extraDigits) +static int encodeLatLonToMapcodes_internal(char **v, Mapcodes *mapcodes, double lat, double lon, + const int tc, const int stop_with_one_result, + const int requiredEncoder, const int extraDigits) { encodeRec enc; enc.mapcodes = mapcodes; @@ -1927,10 +1954,11 @@ static int encodeLatLonToMapcodes_internal(char **v, Mapcodes *mapcodes, double int HOR = 1; int i = 0; // pointer into redivar for (; ;) { - int v2 = redivar[i++]; + const int v2 = redivar[i++]; HOR = 1 - HOR; if (v2 >= 0 && v2 < 1024) { // leaf? - int j, nr = v2; + int j; + const int nr = v2; for (j = 0; j <= nr; j++) { int ctry = (j == nr ? ccode_earth : redivar[i + j]); encoderEngine(ctry, &enc, stop_with_one_result, extraDigits, requiredEncoder, -1); @@ -1939,7 +1967,7 @@ static int encodeLatLonToMapcodes_internal(char **v, Mapcodes *mapcodes, double break; } else { - int coord = (HOR ? enc.coord32.lon : enc.coord32.lat); + const int coord = (HOR ? enc.coord32.lon : enc.coord32.lat); if (coord > v2) { i = redivar[i]; } @@ -1988,7 +2016,7 @@ char *getTerritoryIsoName(char *result, int territoryCode, if (territoryCode < 1 || territoryCode > MAX_MAPCODE_TERRITORY_CODE) { *result = 0; } else { - int p = ParentLetter(territoryCode - 1); + const int p = ParentLetter(territoryCode - 1); char iso3[4]; const char *ei = get_entity_iso3(iso3, territoryCode - 1); if (*ei >= '0' && *ei <= '9') { ei++; } @@ -2006,7 +2034,7 @@ char *getTerritoryIsoName(char *result, int territoryCode, // returns negative if tc is not a code that has a parent country int getParentCountryOf(int tc) { - int parentccode = ParentTerritoryOf(tc - 1); // returns parent ccode or -1 + const int parentccode = ParentTerritoryOf(tc - 1); // returns parent ccode or -1 if (parentccode >= 0) { return parentccode + 1; } return -1; } @@ -2014,7 +2042,7 @@ int getParentCountryOf(int tc) { // returns tc if tc is a country, parent country if tc is a state, -1 if tc is invalid int getCountryOrParentCountry(int tc) { if (tc > 0 && tc < MAX_MAPCODE_TERRITORY_CODE) { - int tp = getParentCountryOf(tc); + const int tp = getParentCountryOf(tc); if (tp > 0) { return tp; } return tc; } @@ -2040,10 +2068,11 @@ int binfindmatch(int parentcode, const char *str) { memcpy(tmp, str, 4); } tmp[4] = 0; + makeupper(tmp); { // binary-search the result const alphaRec *p; alphaRec t; - t.alphaCode = strupr(tmp); + t.alphaCode = tmp; t.ccode = parentcode; p = (const alphaRec *) bsearch(&t, alphaSearch, NRTERREC, sizeof(alphaRec), cmp_alphacode); @@ -2060,7 +2089,7 @@ int binfindmatch(int parentcode, const char *str) { int convertTerritoryIsoNameToCode(const char *string, int optional_tc) // optional_tc: pass 0 or negative if unknown { - int ccode = optional_tc - 1; + const int ccode = optional_tc - 1; if (string == NULL) { return -1; } while (*string > 0 && *string <= 32) { string++; } // skip leading whitespace @@ -2087,7 +2116,7 @@ int convertTerritoryIsoNameToCode(const char *string, int optional_tc) // option else // there is a ccode, there is no hyphen in the string, and the string is as most 3 chars { char tmp[12]; - int tc = getCountryOrParentCountry(optional_tc); + const int tc = getCountryOrParentCountry(optional_tc); strcpy(tmp, convertTerritoryCodeToIsoName(tc, 1)); // short parent country code strcat(tmp, "-"); @@ -2134,7 +2163,7 @@ UWORD *convertToAlphabet(UWORD *unibuf, int maxlength, const char *mapcode, int // straight-copy everything up to and including first space { - char *e = strchr(mapcode, ' '); + const char *e = strchr(mapcode, ' '); if (e) { while (mapcode <= e) { if (unibuf == lastspot) { // buffer fully filled? @@ -2194,7 +2223,7 @@ const UWORD *encodeToAlphabet(const char *mapcode, int alphabet) // 0=roman, 2=c int encodeLatLonToSingleMapcode(char *result, double lat, double lon, int tc, int extraDigits) { char *v[2]; Mapcodes rlocal; - int ret = encodeLatLonToMapcodes_internal(v, &rlocal, lat, lon, tc, 1, debugStopAt, extraDigits); + const int ret = encodeLatLonToMapcodes_internal(v, &rlocal, lat, lon, tc, 1, debugStopAt, extraDigits); *result = 0; if (ret <= 0) { // no solutions? return -1; diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index d03f1b2..055cec0 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -18,7 +18,7 @@ extern "C" { #endif -#define mapcode_cversion "2.1.2" +#define mapcode_cversion "2.1.3" #define UWORD unsigned short int // 2-byte unsigned integer. @@ -224,6 +224,12 @@ int getCountryOrParentCountry(int territoryCode); */ int getParentCountryOf(int territoryCode); +/** + * Returns the distance in meters between two coordinates (latitude/longitude pairs) + * CAVEAT: only works for coordinates that are within a few miles from each other. + */ +double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double lonDeg2); + /** * Alphabets: */ diff --git a/unitttest/unittest.c b/unitttest/unittest.c index 730592a..35545a4 100644 --- a/unitttest/unittest.c +++ b/unitttest/unittest.c @@ -98,30 +98,6 @@ static void alphabet_tests() { } } -// Returns distance in meters between two coordinates (that are not more than a mile apart) -static double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double lonDeg2) { - double dx, dy, worstParallel = 0; // assume equator - if (latDeg1 > latDeg2) { - if (latDeg1 < 0) { - worstParallel = latDeg2; - } else if (latDeg2 > 0) { - worstParallel = latDeg1; - } - } - else { - if (latDeg2 < 0) { - worstParallel = latDeg1; - } else if (latDeg1 > 0) { - worstParallel = latDeg2; - } - } - dy = (latDeg2 - latDeg1); - if (lonDeg1 < 0 && lonDeg2 > 1) { lonDeg1 += 360; } - if (lonDeg2 < 0 && lonDeg1 > 1) { lonDeg2 += 360; } - dx = (lonDeg2 - lonDeg1) * cos((3.141592653589793238462643383279 / 180.0) * worstParallel); - return sqrt(dx * dx + dy * dy) * (1000000.0 / 9); -} - // static void printGeneratedMapcodes(const Mapcodes *mapcodes) { @@ -501,16 +477,57 @@ static void re_encode_tests() { } +void distance_tests() +{ + if (strcmp(mapcode_cversion,"2.1.3") >=0) { + int i; + double coordpairs[] = { + // lat1, lon1, lat2, lon2, expected distance * 100000 + 1,1,1,1,0, + 0,0,0,1,11131949079, + 89,0,89,1,194279300, + 3,0,3,1,11116693130, + -3,0,-3,1,11116693130, + -3,-179.5,-3,179.5,11116693130, + -3,179.5,-3,-179.5,11116693130, + 3,8,3,9,11116693130, + 3,-8,3,-9,11116693130, + 3,-0.5,3,0.5,11116693130, + 54,5,54.000001,5,11095, + 54,5,54,5.000001,6543, + 54,5,54.000001,5.000001,12880, + 90,0,90,50,0, + 0.11,0.22,0.12,0.2333,185011466, + -1 + }; + + for(i=0;coordpairs[i]!=-1;i+=5) { + const double distance = distanceInMeters( + coordpairs[i],coordpairs[i+1], + coordpairs[i+2],coordpairs[i+3]); + nrTests++; + if ( floor(0.5+(100000.0 * distance)) != coordpairs[i+4] ) { + nrErrors++; + printf("*** ERROR *** distanceInMeters %d failed: %f\n",i,distance);; + } + } + } +} + + void main() { #ifdef XSIDE3 const char *mapcode_dataversion = "undefined"; #endif - printf("Mapcode C Library Unit test 2.1.2\n"); + printf("Mapcode C Library Unit test 2.1.3\n"); printf("Library version %s (Data version %s)\n", mapcode_cversion, mapcode_dataversion); printf("-----------------------------------------------------------\nAlphabet tests\n"); alphabet_tests(); + printf("-----------------------------------------------------------\nDistance tests\n"); + distance_tests(); + printf("-----------------------------------------------------------\nTerritory tests\n"); printf("%d territories\n", MAX_CCODE); test_territories();