Beispiel #1
0
static void generateAndOutputMapcodes(double lat, double lon, int iShowError, int extraDigits, int useXYZ) {

    enum Territory context = TERRITORY_NONE;

    while (lon > 180.0) {
        lon -= 360.0;
    }
    while (lon < -180.0) {
        lon += 360.0;
    }
    while (lat > 90.0) {
        lat -= 180.0;
    }
    while (lat < -90.0) {
        lat += 180.0;
    }

    Mapcodes mapcodes;
    const int nrResults = encodeLatLonToMapcodes(&mapcodes, lat, lon, context, extraDigits);
    if (nrResults <= 0) {
        if (iShowError) {
            fprintf(stderr, "error: cannot encode lat=%.20g, lon=%.20g)\n", lat, lon);
            exit(NORMAL_ERROR);
        }
    }

    if (useXYZ) {
        double x;
        double y;
        double z;
        convertLatLonToXYZ(lat, lon, &x, &y, &z);
        printf("%d %.20g %.20g %.20g %.20g %.20g\n", nrResults, lat, lon, x, y, z);
    } else {
        printf("%d %.20g %.20g\n", nrResults, lat, lon);
    }
    for (int j = 0; j < nrResults; ++j) {
        const char *foundMapcode = mapcodes.mapcode[j];

        // Output result line.
        printf("%s\n", foundMapcode);

        // Self-checking code to see if encoder produces this Mapcode for the lat/lon.
        if (selfCheckEnabled) {
            selfCheckLatLonToMapcode(lat, lon, foundMapcode, extraDigits);
            selfCheckMapcodeToLatLon(foundMapcode, lat, lon);
        }
    }

    // Add empty line.
    printf("\n");

    if (nrResults > largestNrOfResults) {
        largestNrOfResults = nrResults;
        latLargestNrOfResults = lat;
        lonLargestNrOfResults = lon;
    }
    totalNrOfResults += nrResults;
}
Beispiel #2
0
/**
 * This is the main() method which is called from the command-line.
 * Return code 0 means success. Any other values means some sort of error occurred.
 */
int main(const int argc, const char **argv) {
    // Assume no extra digits (unless overridden later.
    int extraDigits = 0;

    // If XYZ is added to -b, -r or -g, print x, y, z coordinates
    int useXYZ = 0;

    // Provide usage message if no arguments specified.
    const char *appName = argv[0];
    selfCheckEnabled = (strstr(appName, "debug") != 0);
    if (selfCheckEnabled) {
        fprintf(stderr, "(debug mode: self checking enabled)\n");
    }
    if (argc < 2) {
        usage(appName);
        return NORMAL_ERROR;
    }

    // First argument: command.
    const char *cmd = argv[1];
    if ((strcmp(cmd, "-d") == 0) || (strcmp(cmd, "--decode") == 0)) {

        // ------------------------------------------------------------------
        // Decode: [-d | --decode] <default-territory> <mapcode> [<mapcode> ...]
        // ------------------------------------------------------------------
        if (argc < 4) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }

        const char *defaultTerritory = argv[2];
        double lat;
        double lon;

        // Get the territory context.
        int context = convertTerritoryIsoNameToCode(defaultTerritory, 0);

        // Decode every Mapcode.
        for (int i = 3; i < argc; ++i) {

            // Decode the Mapcode to a lat/lon.
            const char *mapcode = argv[i];
            int err = decodeMapcodeToLatLon(&lat, &lon, mapcode, context);
            if (err != 0) {
                fprintf(stderr, "error: cannot decode '%s %s'\n", defaultTerritory, mapcode);
                return NORMAL_ERROR;
            }

            // Output the decoded lat/lon.
            printf("%.12g %.12g\n", lat, lon);

            // Self-checking code to see if encoder produces this Mapcode for the lat/lon.
            if (selfCheckEnabled) {
                const char *suffix = strstr(mapcode, "-");
                extraDigits = 0;
                if (suffix != 0) {
                    extraDigits = (int) (strlen(suffix) - 1);
                }
                selfCheckLatLonToMapcode(lat, lon, defaultTerritory, mapcode, extraDigits);
            }
        }
    }
    else if ((strcmp(cmd, "-e") == 0) || (strcmp(cmd, "-e0") == 0) ||
             (strcmp(cmd, "-e1") == 0) || (strcmp(cmd, "-e2") == 0) ||
             (strcmp(cmd, "-e3") == 0) || (strcmp(cmd, "-e4") == 0) ||
             (strcmp(cmd, "-e5") == 0) || (strcmp(cmd, "-e6") == 0) ||
             (strcmp(cmd, "-e7") == 0) || (strcmp(cmd, "-e8") == 0) ||
             (strcmp(cmd, "--encode") == 0) || (strcmp(cmd, "--encode0") == 0) ||
             (strcmp(cmd, "--encode1") == 0) || (strcmp(cmd, "--encode2") == 0) ||
             (strcmp(cmd, "--encode3") == 0) || (strcmp(cmd, "--encode4") == 0) ||
             (strcmp(cmd, "--encode5") == 0) || (strcmp(cmd, "--encode5") == 0) ||
             (strcmp(cmd, "--encode7") == 0) || (strcmp(cmd, "--encode8") == 0)) {

        // ------------------------------------------------------------------
        // Encode: [-e[0-8] | --encode[0-8]] <lat:-90..90> <lon:-180..180> [territory]>
        // ------------------------------------------------------------------
        if ((argc != 4) && (argc != 5)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        if ((!isdigit(*argv[2]) && (*argv[2] != '-')) || (!isdigit(*argv[3]) && (*argv[3] != '-'))) {
            fprintf(stderr, "error: latitude and longitude must be numeric\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        const double lat = atof(argv[2]);
        const double lon = atof(argv[3]);

        if (strstr(cmd, "-e1") || strstr(cmd, "--encode1")) {
            extraDigits = 1;
        }
        else if (strstr(cmd, "-e2") || strstr(cmd, "--encode2")) {
            extraDigits = 2;
        }
        else if (strstr(cmd, "-e3") || strstr(cmd, "--encode3")) {
            extraDigits = 3;
        }
        else if (strstr(cmd, "-e4") || strstr(cmd, "--encode4")) {
            extraDigits = 4;
        }
        else if (strstr(cmd, "-e5") || strstr(cmd, "--encode5")) {
            extraDigits = 5;
        }
        else if (strstr(cmd, "-e6") || strstr(cmd, "--encode6")) {
            extraDigits = 6;
        }
        else if (strstr(cmd, "-e7") || strstr(cmd, "--encode7")) {
            extraDigits = 7;
        }
        else if (strstr(cmd, "-e8") || strstr(cmd, "--encode8")) {
            extraDigits = 8;
        }
        else {
            extraDigits = 0;
        }

        // Get territory context.
        int context = 0;
        const char *defaultTerritory = "AAA";
        if (argc == 5) {
            context = convertTerritoryIsoNameToCode(argv[4], 0);
            defaultTerritory = argv[4];
        }

        // Encode the lat/lon to a set of Mapcodes.
        char *results[2 * MAX_NR_OF_MAPCODE_RESULTS];
        const int nrResults = encodeLatLonToMapcodes_Deprecated(results, lat, lon, context, extraDigits);
        if (nrResults <= 0) {
            fprintf(stderr, "error: cannot encode lat=%.12g, lon=%.12g (default territory=%s)\n",
                    lat, lon, defaultTerritory);
            return NORMAL_ERROR;
        }

        // Output the Mapcode.
        for (int i = 0; i < nrResults; ++i) {
            const char *foundMapcode = results[(i * 2)];
            const char *foundTerritory = results[(i * 2) + 1];
            printf("%s %s\n", foundTerritory, foundMapcode);

            // Self-checking code to see if decoder produces the lat/lon for all of these Mapcodes.
            if (selfCheckEnabled) {
                selfCheckMapcodeToLatLon(foundTerritory, foundMapcode, lat, lon);
            }
        }
    }
    else if ((strcmp(cmd, "-b") == 0) || (strcmp(cmd, "-bXYZ") == 0) ||
             (strcmp(cmd, "--boundaries") == 0) || (strcmp(cmd, "--boundariesXYZ") == 0)) {

        // ------------------------------------------------------------------
        // Generate a test set based on the Mapcode boundaries.
        // ------------------------------------------------------------------
        if ((argc < 2) || (argc > 3)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        if (argc == 3) {
            extraDigits = atoi(argv[2]);
            if ((extraDigits < 0) || (extraDigits > 8)) {
                fprintf(stderr, "error: parameter extraDigits must be in [0..8]\n\n");
                usage(appName);
                return NORMAL_ERROR;
            }
        }
        useXYZ = (strstr(cmd, "XYZ") != 0);

        resetStatistics(NR_BOUNDARY_RECS);
        for (int i = 0; i < totalNrOfPoints; ++i) {
            double minLon;
            double maxLon;
            double minLat;
            double maxLat;
            double lat;
            double lon;

            const mminforec *mm = boundaries(i);
            minLon = ((double) mm->minx) / 1.0E6;
            maxLon = ((double) mm->maxx) / 1.0E6;
            minLat = ((double) mm->miny) / 1.0E6;
            maxLat = ((double) mm->maxy) / 1.0E6;

            // Try center.
            lat = (maxLat - minLat) / 2.0;
            lon = (maxLon - minLon) / 2.0;
            generateAndOutputMapcodes(lat, lon, 0, extraDigits, useXYZ);

            // Try corners.
            generateAndOutputMapcodes(minLat, minLon, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(minLat, maxLon, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat, minLon, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat, maxLon, 0, extraDigits, useXYZ);

            // Try JUST inside.
            const double d = 0.000001;
            generateAndOutputMapcodes(minLat + d, minLon + d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(minLat + d, maxLon - d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat - d, minLon + d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat - d, maxLon - d, 0, extraDigits, useXYZ);

            // Try JUST outside.
            generateAndOutputMapcodes(minLat - d, minLon - d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(minLat - d, maxLon + d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat + d, minLon - d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat + d, maxLon + d, 0, extraDigits, useXYZ);

            if ((i % SHOW_PROGRESS) == 0) {
                showProgress(i);
            }
        }
        outputStatistics();
    }
    else if ((strcmp(cmd, "-g") == 0) || (strcmp(cmd, "-gXYZ") == 0) ||
             (strcmp(cmd, "--grid") == 0) || (strcmp(cmd, "--gridXYZ") == 0) ||
             (strcmp(cmd, "-r") == 0) || (strcmp(cmd, "-rXYZ") == 0) ||
             (strcmp(cmd, "--random") == 0) || (strcmp(cmd, "--randomXYZ") == 0)) {

        // ------------------------------------------------------------------
        // Generate grid test set:    [-g | --grid]   <nrOfPoints> [<extradigits>]
        // Generate uniform test set: [-r | --random] <nrOfPoints> [<seed>]
        // ------------------------------------------------------------------
        if ((argc < 3) || (argc > 5)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        int nrOfPoints = atoi(argv[2]);
        if (nrOfPoints < 1) {
            fprintf(stderr, "error: total number of points to generate must be >= 1\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        if (argc >= 4) {
            extraDigits = atoi(argv[3]);
            if ((extraDigits < 0) || (extraDigits > 8)) {
                fprintf(stderr, "error: parameter extraDigits must be in [0..8]\n\n");
                usage(appName);
                return NORMAL_ERROR;
            }
        }
        int random = (strcmp(cmd, "-r") == 0) || (strcmp(cmd, "--random") == 0);
        if (random) {
            if (argc == 5) {
                const int seed = atoi(argv[4]);
                srand((unsigned int) seed);
            }
            else {
                srand((unsigned int) time(0));
            }
        }
        useXYZ = (strstr(cmd, "XYZ") != 0);

        // Statistics.
        resetStatistics(nrOfPoints);

        int gridX = 0;
        int gridY = 0;
        int line = my_round(sqrt((double) totalNrOfPoints));
        for (int i = 0; i < totalNrOfPoints; ++i) {
            double lat;
            double lon;
            double unit1;
            double unit2;

            if (random) {
                unit1 = ((double) rand()) / RAND_MAX;
                unit2 = ((double) rand()) / RAND_MAX;
            }
            else {
                unit1 = ((double) gridX) / line;
                unit2 = ((double) gridY) / line;

                if (gridX < line) {
                    ++gridX;
                }
                else {
                    gridX = 0;
                    ++gridY;
                }
            }

            unitToLatLonDeg(unit1, unit2, &lat, &lon);
            generateAndOutputMapcodes(lat, lon, 1, extraDigits, useXYZ);

            if ((i % SHOW_PROGRESS) == 0) {
                showProgress(i);
            }
        }
        outputStatistics();
    }
    else {

        // ------------------------------------------------------------------
        // Usage.
        // ------------------------------------------------------------------
        usage(appName);
        return NORMAL_ERROR;
    }
    return 0;
}
Beispiel #3
0
static void generateAndOutputMapcodes(double lat, double lon, int iShowError, int extraDigits, int useXYZ) {

    char *results[2 * MAX_NR_OF_MAPCODE_RESULTS];
    int context = 0;

    while (lon > 180.0) {
        lon -= 360.0;
    }
    while (lon < -180.0) {
        lon += 360.0;
    }
    while (lat > 90.0) {
        lat -= 180.0;
    }
    while (lat < -90.0) {
        lat += 180.0;
    }

#ifdef LIMIT_TO_MICRODEGREES
    {
        // Need to truncate lat/lon to microdegrees.
        long lon32 = lon * 1000000.0;
        long lat32 = lat * 1000000.0;
        lon = (lon32 / 1000000.0);
        lat = (lat32 / 1000000.0);
    }
#endif

    const int nrResults = encodeLatLonToMapcodes_Deprecated(results, lat, lon, context, extraDigits);
    if (nrResults <= 0) {
        if (iShowError) {
            fprintf(stderr, "error: cannot encode lat=%.12g, lon=%.12g)\n", lat, lon);
            exit(NORMAL_ERROR);
        }
    }

    if (useXYZ) {
        double x;
        double y;
        double z;
        convertLatLonToXYZ(lat, lon, &x, &y, &z);
        printf("%d %.12g %.12g %.12g %.12g %.12g\n", nrResults, lat, lon, x, y, z);
    }
    else {
        printf("%d %.12g %.12g\n", nrResults, lat, lon);
    }
    for (int j = 0; j < nrResults; ++j) {
        const char *foundMapcode = results[(j * 2)];
        const char *foundTerritory = results[(j * 2) + 1];

        // Output result line.
        printf("%s %s\n", foundTerritory, foundMapcode);

        // Self-checking code to see if encoder produces this Mapcode for the lat/lon.
        if (selfCheckEnabled) {
            selfCheckLatLonToMapcode(lat, lon, foundTerritory, foundMapcode, extraDigits);
            selfCheckMapcodeToLatLon(foundTerritory, foundMapcode, lat, lon);
        }
    }

    // Add empty line.
    printf("\n");

    if (nrResults > largestNrOfResults) {
        largestNrOfResults = nrResults;
        latLargestNrOfResults = lat;
        lonLargestNrOfResults = lon;
    }
    totalNrOfResults += nrResults;
}
Beispiel #4
0
/**
 * This is the main() method which is called from the command-line.
 * Return code 0 means success. Any other values means some sort of error occurred.
 */
int main(const int argc, const char **argv) {
    // Assume no extra digits (unless overridden later.
    int extraDigits = 0;

    // If XYZ is added to -b, -r or -g, print x, y, z coordinates
    int useXYZ = 0;

    // Provide usage message if no arguments specified.
    const char *appName = argv[0];
    selfCheckEnabled = (strstr(appName, "debug") != 0);
    if (selfCheckEnabled) {
        fprintf(stderr, "(debug mode: self checking enabled)\n");
    }
    if (argc < 2) {
        usage(appName);
        return NORMAL_ERROR;
    }

    // First argument: command.
    const char *cmd = argv[1];
    if ((strcmp(cmd, "-d") == 0) || (strcmp(cmd, "--decode") == 0)) {

        // ------------------------------------------------------------------
        // Decode: [-d | --decode] <default-territory> <mapcode> [<mapcode> ...]
        // ------------------------------------------------------------------
        if (argc < 4) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }

        const char *defaultTerritory = argv[2];
        double lat;
        double lon;

        // Get the territory context.
        enum Territory context = getTerritoryCode(defaultTerritory, TERRITORY_NONE);

        // Decode every Mapcode.
        for (int i = 3; i < argc; ++i) {

            // Decode the Mapcode to a lat/lon.
            const char *mapcode = argv[i];
            int err = decodeMapcodeToLatLonUtf8(&lat, &lon, mapcode, context, NULL);
            if (err != 0) {
                fprintf(stderr, "error: cannot decode '%s %s'\n", defaultTerritory, mapcode);
                return NORMAL_ERROR;
            }

            // Output the decoded lat/lon.
            printf("%.20g %.20g\n", lat, lon);

            // Self-checking code to see if encoder produces this Mapcode for the lat/lon.
            if (selfCheckEnabled) {
                const char *suffix = strstr(mapcode, "-");
                extraDigits = 0;
                if (suffix != 0) {
                    extraDigits = (int) (strlen(suffix) - 1);
                }
                selfCheckLatLonToMapcode(lat, lon, mapcode, extraDigits);
            }
        }
    } else if ((strcmp(cmd, "-e") == 0) || (strcmp(cmd, "-e0") == 0) ||
               (strcmp(cmd, "-e1") == 0) || (strcmp(cmd, "-e2") == 0) ||
               (strcmp(cmd, "-e3") == 0) || (strcmp(cmd, "-e4") == 0) ||
               (strcmp(cmd, "-e5") == 0) || (strcmp(cmd, "-e6") == 0) ||
               (strcmp(cmd, "-e7") == 0) || (strcmp(cmd, "-e8") == 0) ||
               (strcmp(cmd, "--encode") == 0) || (strcmp(cmd, "--encode0") == 0) ||
               (strcmp(cmd, "--encode1") == 0) || (strcmp(cmd, "--encode2") == 0) ||
               (strcmp(cmd, "--encode3") == 0) || (strcmp(cmd, "--encode4") == 0) ||
               (strcmp(cmd, "--encode5") == 0) || (strcmp(cmd, "--encode5") == 0) ||
               (strcmp(cmd, "--encode7") == 0) || (strcmp(cmd, "--encode8") == 0)) {

        // ------------------------------------------------------------------
        // Encode: [-e[0-8] | --encode[0-8]] <lat:-90..90> <lon:-180..180> [territory]>
        // ------------------------------------------------------------------
        if ((argc != 4) && (argc != 5)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        if ((!isdigit(*argv[2]) && (*argv[2] != '-')) || (!isdigit(*argv[3]) && (*argv[3] != '-'))) {
            fprintf(stderr, "error: latitude and longitude must be numeric\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        const double lat = atof(argv[2]);
        const double lon = atof(argv[3]);

        if (strstr(cmd, "-e1") || strstr(cmd, "--encode1")) {
            extraDigits = 1;
        } else if (strstr(cmd, "-e2") || strstr(cmd, "--encode2")) {
            extraDigits = 2;
        } else if (strstr(cmd, "-e3") || strstr(cmd, "--encode3")) {
            extraDigits = 3;
        } else if (strstr(cmd, "-e4") || strstr(cmd, "--encode4")) {
            extraDigits = 4;
        } else if (strstr(cmd, "-e5") || strstr(cmd, "--encode5")) {
            extraDigits = 5;
        } else if (strstr(cmd, "-e6") || strstr(cmd, "--encode6")) {
            extraDigits = 6;
        } else if (strstr(cmd, "-e7") || strstr(cmd, "--encode7")) {
            extraDigits = 7;
        } else if (strstr(cmd, "-e8") || strstr(cmd, "--encode8")) {
            extraDigits = 8;
        } else {
            extraDigits = 0;
        }

        // Get territory context.
        enum Territory context = TERRITORY_NONE;
        const char *defaultTerritory = "AAA";
        if (argc == 5) {
            context = getTerritoryCode(argv[4], TERRITORY_NONE);
            defaultTerritory = argv[4];
        }

        // Encode the lat/lon to a set of Mapcodes.
        Mapcodes mapcodes;
        const int nrResults = encodeLatLonToMapcodes(&mapcodes, lat, lon, context, extraDigits);
        if (nrResults <= 0) {
            fprintf(stderr, "error: cannot encode lat=%.20g, lon=%.20g (default territory=%s)\n",
                    lat, lon, defaultTerritory);
            return NORMAL_ERROR;
        }

        // Output the Mapcode.
        for (int i = 0; i < nrResults; ++i) {
            const char *foundMapcode = mapcodes.mapcode[i];
            printf("%s\n", foundMapcode);

            // Self-checking code to see if decoder produces the lat/lon for all of these Mapcodes.
            if (selfCheckEnabled) {
                selfCheckMapcodeToLatLon(foundMapcode, lat, lon);
            }
        }
    } else if ((strcmp(cmd, "-t") == 0) ||
               (strcmp(cmd, "--territories") == 0)) {

        // ------------------------------------------------------------------
        // Generate a test set based on the Mapcode territories
        // ------------------------------------------------------------------
        if ((argc < 2) || (argc > 2)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        printf("ccode,territorycodes(pipe-separated),alphabets(pipe-seperated),names(pipe-separated)\n");
        for (int i = _TERRITORY_MIN + 1; i < _TERRITORY_MAX; ++i) {
            const enum Territory ccode = (enum Territory) i;
            char territoryName[MAX_MAPCODE_RESULT_LEN];
            printf("%d,", INDEX_OF_TERRITORY(i));

            // Use internal knowledge of ALPHA_SEARCH to show aliases of territoryName.
            printf("%s", getTerritoryIsoName(territoryName, ccode, 0));
            for (int a = 0; a < NR_TERRITORY_RECS; a++) {
                if (ALPHA_SEARCH[a].territory == ccode) {
                    char fullcode[16];
                    strcpy(fullcode, ALPHA_SEARCH[a].alphaCode);
                    if (fullcode[0] >= '0' && fullcode[0] <= '9') {
                        static const char *parents2 = "US,IN,CA,AU,MX,BR,RU,CN,";
                        int p = (fullcode[0] - '0');
                        memcpy(fullcode, &parents2[p * 3 - 3], 2);
                        fullcode[2] = '-';
                        strcpy(fullcode + 3, ALPHA_SEARCH[a].alphaCode + 1);
                    }
                    if (strcmp(fullcode, territoryName) != 0) {
                        printf("|%s", fullcode);
                    }
                }
            }
            printf(",");

            // Print alphabets.
            const TerritoryAlphabets *territoryAlphabets = getAlphabetsForTerritory(ccode);
            for (int j = 0; j < territoryAlphabets->count; j++) {
                if (j > 0) {
                    printf("|");
                }
                printf("%d", territoryAlphabets->alphabet[j]);
            }
            printf(",");

            // Use internal knowledge of TERRITORY_FULL_NAME to show aliases of full territory name.
            char *names = strdup(TERRITORY_FULL_NAME[INDEX_OF_TERRITORY(ccode)]);
            char *s = names;
            while (s) {
                if (s != names) {
                    printf("|");
                }
                char *e = strstr(s, " (");
                if (e) {
                    *e = 0;
                    if (e[-1] == ')') {
                        e[-1] = 0;
                    }
                    printf("%s", s);
                    s = e + 2;
                } else {
                    e = s + strlen(s);
                    if (e[-1] == ')') {
                        e[-1] = 0;
                    }
                    printf("%s", s);
                    s = NULL;
                }
            }
            printf("\n");
        }
    } else if ((strcmp(cmd, "-a") == 0) ||
               (strcmp(cmd, "--alphabets") == 0)) {

        // ------------------------------------------------------------------
        // Generate a test set based on the Mapcode territories
        // ------------------------------------------------------------------
        static const char *mapcodeForCSV[] = {
                // all characters
                "89.EU",
                "00.0A",
                "BCDF.GHJK",
                "LMNP.QRST",
                "VWXY.Z123",
                "4567.890B",
                // all forms
                "pq.xy",
                "pq.xyz",
                "pqx.yz",
                "pq.rxyz",
                "pqr.xyz",
                "pqrx.yz",
                "pqr.sxyz",
                "pqrs.xyz",
                "pqrs.txyz",
                "pqrst.vxyz",
                // all adjad forms
                "p1.xy",
                "pq.2y",
                "3q.x4",
                "5q.6y",
                "pq.1yz",
                "pq1.yz",
                "p2.x3z",
                "p2x.3z",
                "pq.1xy2",
                "pq1.xy2",
                "pq1x.y2",
                "p3.rx4z",
                "p3r.x4z",
                "p3rx.4z",
                "5q.r6y7",
                "5qr.6y7",
                "5qr6.y7",
                "pq1.sx2z",
                "pq1s.x2z",
                "p3r.s4yz",
                "p3rs.4yz",
                "5qr.6xy7",
                "5qr6.xy7",
                "8q9.sx0z",
                "8q9s.x0z",
                "1qr2.tx3z",
                "p4rs.5xy6",
                "p7r8.t9y0",
                "pq1st.2xy3",
                "p4rs5.vx6z",
                "7qr8t.v9yz",
                "p1r2t.3x4z",
                "5q6s7.v8y9",
                // non-mapcode
                "^0123456789!@#$^&*()/:;[]{}<>?|~%",
                "abcdefghijklmnopqrstuvwxyz",
                "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
                // special case for digit-like characters
                "OI.xx",
                "oi.xx",
                "oi.xx-oooooooo",
                "oi.xx-iiiiiiii",
                "PQ.RS-01234567",
                "PQ.RS-890",
                NULL
        };
        if ((argc < 2) || (argc > 2)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }

        printf("alphabetNr,MapcodeInRoman,MapcodeInAlphabet\n");
        for (enum Alphabet alphabet = ALPHABET_ROMAN;
             alphabet < _ALPHABET_MAX; alphabet = (enum Alphabet) (alphabet + 1)) {
            int variant;
            for (variant = 0; variant <= 2; variant++) {
                int m;
                for (m = 0; mapcodeForCSV[m] != NULL; m++) {
                    int i;
                    char asciiString[128];
                    // build a mapcode variant
                    char mapcode[128];
                    strcpy(mapcode, mapcodeForCSV[m]);
                    strcat(mapcode, (variant == 1) ? "-bc" : (variant == 2) ? "-DFGHJKLM" : "");
                    for (i = 0; mapcode[i]; ++i) {
                        mapcode[i] = (char) toupper((int) mapcode[i]);
                    }
                    // convert to alphabet, and back to roman
                    convertMapcodeToAlphabetUtf8(asciiString, mapcode, alphabet);
                    // output a line of csv (in utf8 format)
                    printf("%d,%s,%s\n", alphabet, mapcode, asciiString);
                }
            }
        }
    } else if ((strcmp(cmd, "-b") == 0) || (strcmp(cmd, "-bXYZ") == 0) ||
               (strcmp(cmd, "--boundaries") == 0) || (strcmp(cmd, "--boundariesXYZ") == 0)) {

        // ------------------------------------------------------------------
        // Generate a test set based on the Mapcode boundaries.
        // ------------------------------------------------------------------
        if ((argc < 2) || (argc > 3)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        if (argc == 3) {
            extraDigits = atoi(argv[2]);
            if ((extraDigits < 0) || (extraDigits > 8)) {
                fprintf(stderr, "error: parameter extraDigits must be in [0..8]\n\n");
                usage(appName);
                return NORMAL_ERROR;
            }
        }
        useXYZ = (strstr(cmd, "XYZ") != 0);

        resetStatistics(MAPCODE_BOUNDARY_MAX);
        for (int i = 0; i < totalNrOfPoints; ++i) {
            double minLon;
            double maxLon;
            double minLat;
            double maxLat;
            double lat;
            double lon;

            const TerritoryBoundary *mm = TERRITORY_BOUNDARY(i);
            minLon = ((double) mm->minx) / 1.0E6;
            maxLon = ((double) mm->maxx) / 1.0E6;
            minLat = ((double) mm->miny) / 1.0E6;
            maxLat = ((double) mm->maxy) / 1.0E6;

            // Try center.
            lat = (maxLat - minLat) / 2.0;
            lon = (maxLon - minLon) / 2.0;
            generateAndOutputMapcodes(lat, lon, 0, extraDigits, useXYZ);

            // Try corners.
            generateAndOutputMapcodes(minLat, minLon, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(minLat, maxLon, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat, minLon, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat, maxLon, 0, extraDigits, useXYZ);

            // Try JUST inside.
            const double d = 0.000001;
            generateAndOutputMapcodes(minLat + d, minLon + d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(minLat + d, maxLon - d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat - d, minLon + d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat - d, maxLon - d, 0, extraDigits, useXYZ);

            // Try JUST outside.
            generateAndOutputMapcodes(minLat - d, minLon - d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(minLat - d, maxLon + d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat + d, minLon - d, 0, extraDigits, useXYZ);
            generateAndOutputMapcodes(maxLat + d, maxLon + d, 0, extraDigits, useXYZ);

            if ((i % SHOW_PROGRESS) == 0) {
                showProgress(i);
            }
        }
        outputStatistics();
    } else if ((strcmp(cmd, "-g") == 0) || (strcmp(cmd, "-gXYZ") == 0) ||
               (strcmp(cmd, "--grid") == 0) || (strcmp(cmd, "--gridXYZ") == 0) ||
               (strcmp(cmd, "-r") == 0) || (strcmp(cmd, "-rXYZ") == 0) ||
               (strcmp(cmd, "--random") == 0) || (strcmp(cmd, "--randomXYZ") == 0)) {

        // ------------------------------------------------------------------
        // Generate grid test set:    [-g | --grid]   <nrOfPoints> [<extradigits>]
        // Generate uniform test set: [-r | --random] <nrOfPoints> [<seed>]
        // ------------------------------------------------------------------
        if ((argc < 3) || (argc > 5)) {
            fprintf(stderr, "error: incorrect number of arguments\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        int nrOfPoints = atoi(argv[2]);
        if (nrOfPoints < 1) {
            fprintf(stderr, "error: total number of points to generate must be >= 1\n\n");
            usage(appName);
            return NORMAL_ERROR;
        }
        if (argc >= 4) {
            extraDigits = atoi(argv[3]);
            if ((extraDigits < 0) || (extraDigits > 8)) {
                fprintf(stderr, "error: parameter extraDigits must be in [0..8]\n\n");
                usage(appName);
                return NORMAL_ERROR;
            }
        }
        int random = (strcmp(cmd, "-r") == 0) || (strcmp(cmd, "--random") == 0);
        if (random) {
            if (argc == 5) {
                const int seed = atoi(argv[4]);
                srand((unsigned int) seed);
            } else {
                srand((unsigned int) time(0));
            }
        }
        useXYZ = (strstr(cmd, "XYZ") != 0);

        // Statistics.
        resetStatistics(nrOfPoints);

        int gridX = 0;
        int gridY = 0;
        int line = my_round(sqrt((double) totalNrOfPoints));
        for (int i = 0; i < totalNrOfPoints; ++i) {
            double lat;
            double lon;
            double unit1;
            double unit2;

            if (random) {
                unit1 = ((double) rand()) / RAND_MAX;
                unit2 = ((double) rand()) / RAND_MAX;
            } else {
                unit1 = ((double) gridX) / line;
                unit2 = ((double) gridY) / line;

                if (gridX < line) {
                    ++gridX;
                } else {
                    gridX = 0;
                    ++gridY;
                }
            }

            unitToLatLonDeg(unit1, unit2, &lat, &lon);
            generateAndOutputMapcodes(lat, lon, 1, extraDigits, useXYZ);

            if ((i % SHOW_PROGRESS) == 0) {
                showProgress(i);
            }
        }
        outputStatistics();
    } else {

        // ------------------------------------------------------------------
        // Usage.
        // ------------------------------------------------------------------
        usage(appName);
        return NORMAL_ERROR;
    }
    return 0;
}