From f52bfb225bad2d4caf5593b9def9beb1dea8b4a1 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 26 Nov 2021 16:35:28 -1000 Subject: [PATCH 01/15] Implement DCW collections DCW collections are named groups of countries (or states) that can be requested via their abbreviation or full name. DCW will ship with some standard collections but users can add their own in ~/.gmt/dcw-collections.txt which has the format # ANy number of comments tag: abbreviation [full name] XX,YY,ZZ,WW,... where the XX etc are valid DCW tags. Any number of tag/list sequences are allowed. --- src/gmt_dcw.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 120 insertions(+), 10 deletions(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 349e050380a..8aa441fe05a 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -34,6 +34,8 @@ * * We expect to find the file dcw-gmt.nc, dcw-countries.txt, and dcw-states.txt * in one of the dirs accessible to GMT or pointed to by the default DIR_DCW. + * We also look for dcw-collections.txt for named groups of items (2.0.2 or later) + * both in the DCW directory and the users .gmt directory. * See separate subversion project DCW for the maintenance of the raw files that * are used to build the netCDF file [svn://gmtserver.soest.hawai.edu/DCW]. */ @@ -62,6 +64,12 @@ struct GMT_DCW_COUNTRY_STATE { /* Information per country with state */ char country[4]; /* 2/3-char country code ISO 3166-1 (e.g., BR, US) for countries with states */ }; +struct GMT_DCW_COLLECTION { /* Information of collections representing multiple selections */ + char region[GMT_LEN16]; /* Up to 16-character collections, e.g., SCAND for Scandinavia */ + char name[GMT_LEN64]; /* Up to 64-character optional full name, e.g., Scandinavia */ + char *list; /* Comma-separated list of entities making up the collection */ +}; + /* For version 2.0.0 we follow https://en.wikipedia.org/wiki/ISO_3166-2:CN and use 2-char instead of int codes */ #define DCW_N_CHINA_PROVINCES 34 struct GMT_DCW_CHINA_CODES { @@ -133,15 +141,17 @@ GMT_LOCAL bool gmtdcw_get_path (struct GMT_CTRL *GMT, char *name, char *suffix, return (false); } -GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY **C, struct GMT_DCW_STATE **S, struct GMT_DCW_COUNTRY_STATE **CS, unsigned int dim[]) { +GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY **C, struct GMT_DCW_STATE **S, struct GMT_DCW_COUNTRY_STATE **CS, struct GMT_DCW_COLLECTION **U, unsigned int dim[]) { /* Open and read list of countries and states and return via two struct and one char arrays plus dimensions in dim */ size_t n_alloc = 300; - unsigned int k, n; + unsigned int k, kc, n, collection; + int ns; char path[PATH_MAX] = {""}, line[BUFSIZ] = {""}; FILE *fp = NULL; struct GMT_DCW_COUNTRY *Country = NULL; struct GMT_DCW_STATE *State = NULL; struct GMT_DCW_COUNTRY_STATE *Country_State = NULL; + struct GMT_DCW_COLLECTION *Collection = NULL; if (!gmtdcw_get_path (GMT, "dcw-countries", ".txt", path)) return GMT_NOTSET; @@ -203,10 +213,65 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** *CS = Country_State; } + /* Get collections (which may not be present, so allow for missing files */ + + n_alloc = 100; /* Reset to a smaller size */ + Collection = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_DCW_COLLECTION); + k = 0; + for (collection = 0; collection < 2; collection++) { /* Read both system and user collections */ + if (collection == 0) { /* Look for DCW system file (which requires 2.0.2 or higher) */ + if (!gmtdcw_get_path (GMT, "dcw-collections", ".txt", path)) + continue; + } + else { /* Look for user groups in the user dir, if present */ + if (!gmtlib_getuserpath (GMT, "dcw-collections.txt", path)) + continue; + } + /* Here we got the requested path */ + kc = 0; /* Number of collections in this set */ + if ((fp = fopen (path, "r")) == NULL) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to open file %s [permission trouble?]\n", path); + gmt_M_free (GMT, Country); + return GMT_NOTSET; + } + /* File format is sequences like this: + # Scandinavia + tag: SCA Scandinavia + NO,SE,DK + */ + while (gmt_fgets (GMT, line, BUFSIZ, fp)) { + if (line[0] == '#') continue; /* Skip comments */ + if (strncmp (line, "tag: ", 5U)) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s is not in correct format - exit\n", path); + gmt_M_free (GMT, Country); + gmt_M_free (GMT, Collection); + return GMT_NOTSET; + } + ns = sscanf (&line[5], "%s %[^\n]", Collection[k].region, Collection[k].name); + gmt_fgets (GMT, line, BUFSIZ, fp); /* Get record with the list */ + gmt_chop (line); + Collection[k].list = strdup (line); + k++; kc++; + if (k == n_alloc) { + n_alloc += 100; + Collection = gmt_M_memory (GMT, Collection, n_alloc, struct GMT_DCW_COLLECTION); + } + } + fclose (fp); + dim[3+collection] = kc; + } + if (k == 0) /* No such files at all */ + gmt_M_free (GMT, Collection); + else + Collection = gmt_M_memory (GMT, Collection, k, struct GMT_DCW_COLLECTION); + +all_done: + *C = Country; *S = State; + *U = Collection; - GMT_Report (GMT->parent, GMT_MSG_DEBUG, "DCW: Found %u countries, %u countries with states, and %u states\n", dim[0], dim[2], dim[1]); + GMT_Report (GMT->parent, GMT_MSG_DEBUG, "DCW: Found %u countries, %u countries with states, and %u states, %d DCW collections and %d user collections\n", dim[0], dim[2], dim[1], dim[3], dim[4]); return 0; } @@ -262,6 +327,17 @@ GMT_LOCAL bool gmtdcw_country_has_states (char *code, struct GMT_DCW_COUNTRY_STA return (false); } +GMT_LOCAL int gmtdcw_found_collection (char *code, struct GMT_DCW_COLLECTION *GMT_DCW_collection, unsigned int *dim) { + int k, nc = dim[3] + dim[4]; /* Total number of collections */ + if (nc == 0) return GMT_NOTSET; /* No collections available */ + + for (k = 0; k < nc; k++) { + if (strcmp (code, GMT_DCW_collection[k].region) == 0 || (GMT_DCW_collection[k].name[0] && strcmp (code, GMT_DCW_collection[k].name) == 0)) + return (k); /* Found a matching collection */ + } + return (GMT_NOTSET); /* Not found */ +} + /*----------------------------------------------------------| * Public functions that are part of the GMT Devel library | *----------------------------------------------------------| @@ -301,11 +377,11 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL */ int item, ks, retval, ncid, xvarid, yvarid, id; int64_t first, last; - size_t np, max_np = 0U, n_alloc; + size_t np, max_np = 0U, n_alloc = 300; uint64_t k, seg, n_segments; unsigned int n_items = 0, r_item = 0, pos = 0, kk, tbl = 0, j = 0, *order = NULL; unsigned short int *dx = NULL, *dy = NULL; - unsigned int GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, n_bodies[3] = {0, 0, 0}, special = 0; + unsigned int GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, n_bodies[5] = {0, 0, 0, 0, 0}, special = 0; bool done, want_state, outline, fill = false, is_Antarctica = false, hole, new_CN_codes = false; char TAG[GMT_LEN16] = {""}, dim[GMT_LEN16] = {""}, xname[GMT_LEN16] = {""}; char yname[GMT_LEN16] = {""}, code[GMT_LEN16] = {""}, state[GMT_LEN16] = {""}; @@ -320,6 +396,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL struct GMT_RECORD *Out = NULL; struct GMT_DCW_COUNTRY *GMT_DCW_country = NULL; struct GMT_DCW_STATE *GMT_DCW_state = NULL; + struct GMT_DCW_COLLECTION *GMT_DCW_collection = NULL; struct GMT_FILL *sfill = NULL; struct GMT_PEN *spen = NULL; @@ -339,7 +416,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL wesn[YLO] = +9999.0; wesn[YHI] = -9999.0; /* Initialize so we can shrink it below */ } - if (gmtdcw_load_lists (GMT, &GMT_DCW_country, &GMT_DCW_state, NULL, n_bodies)) /* Something went wrong */ + if (gmtdcw_load_lists (GMT, &GMT_DCW_country, &GMT_DCW_state, NULL, &GMT_DCW_collection, n_bodies)) /* Something went wrong */ return NULL; GMT_DCW_COUNTRIES = n_bodies[0]; @@ -347,7 +424,6 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL qsort ((void *)GMT_DCW_country, (size_t)GMT_DCW_COUNTRIES, sizeof (struct GMT_DCW_COUNTRY), gmtdcw_comp_countries); /* Sort on country code */ - n_alloc = n_bodies[0] + n_bodies[1]; /* Presumably max items considered */ order = gmt_M_memory (GMT, NULL, n_alloc, unsigned int); for (j = 0; j < F->n_items; j++) { pos = 0; @@ -359,28 +435,55 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL strcat (list, GMT_DCW_country[k].code); order[n_items] = j; /* So we know which color/pen to apply for this item */ n_items++; + if (n_items == n_alloc) { + n_alloc += 100; + order = gmt_M_memory (GMT, order, n_alloc, unsigned int); + } } if (n_items) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Continent code expanded from %s to %s [%d countries]\n", F->item[j]->codes, list, n_items); else GMT_Report (GMT->parent, GMT_MSG_WARNING, "Continent code %s unrecognized\n", code); } + else if ((ks = gmtdcw_found_collection (code, GMT_DCW_collection, n_bodies)) != GMT_NOTSET) { + /* Got a collection we wish to add */ + unsigned n_list = gmt_count_char (GMT, GMT_DCW_collection[ks].list, ',') + 1; /* Number of items in collection */ + if (n_items) strcat (list, ","); + strcat (list, GMT_DCW_collection[ks].list); + for (k = 0; k < n_list; k++) { + order[n_items++] = j; /* So we know which color/pen to apply for all items in this collection */ + if (n_items == n_alloc) { + n_alloc += 100; + order = gmt_M_memory (GMT, order, n_alloc, unsigned int); + } + } + GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Collection code expanded from %s to %s [%d countries]\n", F->item[j]->codes, GMT_DCW_collection[ks].list, n_list); + } else { /* Just append this single one */ if (n_items) strcat (list, ","); strcat (list, code); order[n_items] = j; /* So we know which color/pen to apply for this item */ n_items++; + if (n_items == n_alloc) { + n_alloc += 100; + order = gmt_M_memory (GMT, order, n_alloc, unsigned int); + } } } } - if (n_items) + if (n_items) { GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Requested %d DCW items: %s\n", n_items, list); + order = gmt_M_memory (GMT, order, n_items, unsigned int); + } else { GMT_Report (GMT->parent, GMT_MSG_DEBUG, "No countries selected\n"); gmt_M_free (GMT, order); gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); + for (k = 0; k < dim[3]+dim[4]; k++) + gmt_M_str_free (GMT_DCW_collection[k].list); + gmt_M_free (GMT, GMT_DCW_collection); return NULL; } @@ -663,6 +766,9 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_nc_close (GMT, ncid); gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); + for (k = 0; k < n_bodies[3]+n_bodies[4]; k++) + gmt_M_str_free (GMT_DCW_collection[k].list); + gmt_M_free (GMT, GMT_DCW_collection); gmt_M_free (GMT, Out); if (mode & GMT_DCW_REGION) { @@ -722,18 +828,19 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { /* Write to stdout the available countries [and optionally states], then make calling program exit */ - unsigned int list_mode, i, j, k, kk, GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, GMT_DCW_N_COUNTRIES_WITH_STATES = 0, n_bodies[3] = {0, 0, 0}; + unsigned int list_mode, i, j, k, kk, GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, GMT_DCW_N_COUNTRIES_WITH_STATES = 0, n_bodies[5] = {0, 0, 0, 0, 0}; bool search = false; char string[GMT_LEN128] = {""}; struct GMT_DCW_COUNTRY *GMT_DCW_country = NULL; struct GMT_DCW_STATE *GMT_DCW_state = NULL; struct GMT_DCW_COUNTRY_STATE *GMT_DCW_country_with_state = NULL; + struct GMT_DCW_COLLECTION *GMT_DCW_collection = NULL; struct GMT_RECORD *Out = NULL; struct GMTAPI_CTRL *API = GMT->parent; list_mode = F->mode; if ((list_mode & GMT_DCW_LIST) == 0) return 0; - if (gmtdcw_load_lists (GMT, &GMT_DCW_country, &GMT_DCW_state, &GMT_DCW_country_with_state, n_bodies)) return 0; /* Something went wrong */ + if (gmtdcw_load_lists (GMT, &GMT_DCW_country, &GMT_DCW_state, &GMT_DCW_country_with_state, &GMT_DCW_collection, n_bodies)) return 0; /* Something went wrong */ GMT_DCW_COUNTRIES = n_bodies[0]; GMT_DCW_STATES = n_bodies[1]; GMT_DCW_N_COUNTRIES_WITH_STATES = n_bodies[2]; @@ -796,6 +903,9 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); gmt_M_free (GMT, GMT_DCW_country_with_state); + for (k = 0; k < n_bodies[3]+n_bodies[4]; k++) + gmt_M_str_free (GMT_DCW_collection[k].list); + gmt_M_free (GMT, GMT_DCW_collection); return ((list_mode & GMT_DCW_LIST)); } From 7c22817604d38fd7f870eeae867ff5bd2dd60828 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 26 Nov 2021 16:58:28 -1000 Subject: [PATCH 02/15] Relax -R for DCW collections --- src/gmt_dcw.c | 6 +++--- src/gmt_init.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 8aa441fe05a..69f2f4cddf0 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -35,7 +35,7 @@ * We expect to find the file dcw-gmt.nc, dcw-countries.txt, and dcw-states.txt * in one of the dirs accessible to GMT or pointed to by the default DIR_DCW. * We also look for dcw-collections.txt for named groups of items (2.0.2 or later) - * both in the DCW directory and the users .gmt directory. + * in the DCW directory and dcw-groups.txt in users .gmt directory. * See separate subversion project DCW for the maintenance of the raw files that * are used to build the netCDF file [svn://gmtserver.soest.hawai.edu/DCW]. */ @@ -224,7 +224,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** continue; } else { /* Look for user groups in the user dir, if present */ - if (!gmtlib_getuserpath (GMT, "dcw-collections.txt", path)) + if (!gmtlib_getuserpath (GMT, "dcw-groups.txt", path)) continue; } /* Here we got the requested path */ @@ -271,7 +271,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** *S = State; *U = Collection; - GMT_Report (GMT->parent, GMT_MSG_DEBUG, "DCW: Found %u countries, %u countries with states, and %u states, %d DCW collections and %d user collections\n", dim[0], dim[2], dim[1], dim[3], dim[4]); + GMT_Report (GMT->parent, GMT_MSG_DEBUG, "DCW: Found %u countries, %u countries with states, %u states, %d DCW collections and %d user groups\n", dim[0], dim[2], dim[1], dim[3], dim[4]); return 0; } diff --git a/src/gmt_init.c b/src/gmt_init.c index 990c1646f00..7d9a528420e 100644 --- a/src/gmt_init.c +++ b/src/gmt_init.c @@ -8816,7 +8816,7 @@ int gmt_parse_R_option (struct GMT_CTRL *GMT, char *arg) { strncpy (string, &item[1], GMT_BUFSIZ-1); GMT->current.io.geo.range = (item[0] == 'g') ? GMT_IS_0_TO_P360_RANGE : GMT_IS_M180_TO_P180_RANGE; } - else if ((isupper ((int)item[0]) && isupper ((int)item[1])) || item[0] == '=' || strchr (item, ',')) { + else if (isupper ((int)item[0]) || item[0] == '=' || strchr (item, ',')) { /* Region specified via country codes with optional round off/extension, e.g., -RNO+r1 or -R=EU */ struct GMT_DCW_SELECT info; GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Got country code for region (%s)\n", item); From b7f99e5fa514a80af73a06b901e7058a12c97191 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 27 Nov 2021 15:45:59 -1000 Subject: [PATCH 03/15] Add +n for listing collections --- doc/rst/source/coast.rst | 8 ++-- doc/rst/source/explain_-Rgeo.rst_ | 3 +- src/gmt_dcw.c | 69 ++++++++++++++++++++----------- src/gmt_dcw.h | 2 +- src/gmt_init.c | 8 ++-- 5 files changed, 58 insertions(+), 32 deletions(-) diff --git a/doc/rst/source/coast.rst b/doc/rst/source/coast.rst index a5ebc23869f..b09450ecb5d 100644 --- a/doc/rst/source/coast.rst +++ b/doc/rst/source/coast.rst @@ -111,7 +111,7 @@ Optional Arguments .. _-E: -**-E**\ *code1,code2,...*\ [**+l**\|\ **L**][**+c**\|\ **C**][**+g**\ *fill*][**+p**\ *pen*][**+z**] +**-E**\ *code1,code2,...*\ [**+l**\|\ **L**\|\ **n**][**+c**\|\ **C**][**+g**\ *fill*][**+p**\ *pen*][**+z**] Select painting, clipping or dumping country polygons from the Digital Chart of the World. This is another dataset independent of GSHHG and hence the **-A** and **-D** options do not apply. Append one or more comma-separated countries using the @@ -119,11 +119,13 @@ Optional Arguments To select a state of a country (if available), append .state, e.g, US.TX for Texas. To specify a whole continent, prepend = to any of the continent codes AF (Africa), AN (Antarctica), AS (Asia), EU (Europe), OC (Oceania), - NA (North America), or SA (South America). Append **+l** to + NA (North America), or SA (South America). To specify a collection give collection code or full name. Append **+l** to just list the countries and their codes [no data extraction or plotting takes place]. Use **+L** to see states/territories for Argentina, Australia, Brazil, Canada, China, India, Russia and the US. - Finally, you can append **+l**\|\ **+L** to **-E**\ =\ *continent* or **-E**\ *code* to only list + You can append **+l**\|\ **+L** to **-E**\ =\ *continent* or **-E**\ *code* to only list countries in that continent or country; repeat if more than one continent or country is requested. + Finally, use **+n** to list the named collections of items available, and use **-E**\ *code* to only list + collections that contains the listed codes. To set up clip paths based on your selection, append **+c** or **+C** for inside or outside (area between selection and the map boundary) clipping, respectively. To plot instead, append **+p**\ *pen* to draw polygon outlines [no outline] and diff --git a/doc/rst/source/explain_-Rgeo.rst_ b/doc/rst/source/explain_-Rgeo.rst_ index eeda5b071c1..09e9dfd2d16 100644 --- a/doc/rst/source/explain_-Rgeo.rst_ +++ b/doc/rst/source/explain_-Rgeo.rst_ @@ -25,7 +25,8 @@ `ISO 3166-1 alpha-2 convention `_. To select a state within a country (if available), append .state, e.g, US.TX for Texas. To specify a whole continent, prepend **=** to any of the continent codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), - **NA** (North America), or **SA** (South America). The following modifiers can be appended: + **NA** (North America), or **SA** (South America). Finally, append any collection abbreviations or full names + for the region of the collection. The following modifiers can be appended: - **+r** to adjust the region boundaries to be multiples of the steps indicated by *inc*, *xinc*/*yinc*, or *winc*/*einc*/*sinc*/*ninc* [default is no adjustment]. For example, **-R**\ *FR*\ **+r**\ 1 will select the diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 69f2f4cddf0..7f533c009a5 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -45,6 +45,7 @@ #define DCW_GET_COUNTRY 1 /* Extract countries only */ #define DCW_GET_COUNTRY_AND_STATE 2 /* Extract countries and states */ +#define DCW_GET_COLLECTIONS 4 /* Extract Collections */ #define DCW_DO_OUTLINE 1 /* Draw outline of polygons */ #define DCW_DO_FILL 2 /* Fill the polygons */ @@ -866,32 +867,49 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { Out = gmt_new_record (GMT, NULL, string); /* Since we only need to worry about text in this module */ - for (i = k = 0; i < GMT_DCW_COUNTRIES; i++) { - if (search) { /* Listed continent(s) */ - bool found = false; - for (kk = 0; kk < F->n_items; kk++) { - if (F->item[kk]->codes[0] == '=') { - if (strstr (F->item[kk]->codes, GMT_DCW_country[i].continent)) - found = true; + if (list_mode & DCW_GET_COLLECTIONS) { + int k, nc = n_bodies[3] + n_bodies[4]; /* Total number of collections */ + for (i = 0; i < nc; i++) { + if (search) { /* Listed collection(s) */ + bool found = false; + for (kk = 0; !found && kk < F->n_items; kk++) { + if (strstr (GMT_DCW_collection[i].list, F->item[kk]->codes)) + found = true; /* This item is part of this collection */ } - else if (strncmp (F->item[kk]->codes, GMT_DCW_country[i].code, 2U) == 0) - found = true; + if (!found) continue; } - if (!found) continue; - } - if (F->n_items == 0 && (i == 0 || strcmp (GMT_DCW_country[i].continent, GMT_DCW_country[i-1].continent)) ) { - sprintf (string, "%s [%s]", GMT_DCW_continents[k++], GMT_DCW_country[i].continent); - GMT_Put_Record (API, GMT_WRITE_DATA, Out); - } - if ((list_mode & 2) == 0) { - sprintf (string, "%s\t%s", GMT_DCW_country[i].code, GMT_DCW_country[i].name); + sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].name, GMT_DCW_collection[j].region, GMT_DCW_collection[i].list); GMT_Put_Record (API, GMT_WRITE_DATA, Out); } - if ((list_mode & 2) && gmtdcw_country_has_states (GMT_DCW_country[i].code, GMT_DCW_country_with_state, GMT_DCW_N_COUNTRIES_WITH_STATES)) { - for (j = 0; j < GMT_DCW_STATES; j++) { - if (!strcmp (GMT_DCW_country[i].code, GMT_DCW_state[j].country)) { - sprintf (string, "%s.%s\t%s", GMT_DCW_country[i].code, GMT_DCW_state[j].code, GMT_DCW_state[j].name); - GMT_Put_Record (API, GMT_WRITE_DATA, Out); + } + else { + for (i = k = 0; i < GMT_DCW_COUNTRIES; i++) { + if (search) { /* Listed continent(s) */ + bool found = false; + for (kk = 0; kk < F->n_items; kk++) { + if (F->item[kk]->codes[0] == '=') { + if (strstr (F->item[kk]->codes, GMT_DCW_country[i].continent)) + found = true; + } + else if (strncmp (F->item[kk]->codes, GMT_DCW_country[i].code, 2U) == 0) + found = true; + } + if (!found) continue; + } + if (F->n_items == 0 && (i == 0 || strcmp (GMT_DCW_country[i].continent, GMT_DCW_country[i-1].continent)) ) { + sprintf (string, "%s [%s]", GMT_DCW_continents[k++], GMT_DCW_country[i].continent); + GMT_Put_Record (API, GMT_WRITE_DATA, Out); + } + if ((list_mode & DCW_GET_COUNTRY_AND_STATE) == 0) { + sprintf (string, "%s\t%s", GMT_DCW_country[i].code, GMT_DCW_country[i].name); + GMT_Put_Record (API, GMT_WRITE_DATA, Out); + } + if ((list_mode & DCW_GET_COUNTRY_AND_STATE) && gmtdcw_country_has_states (GMT_DCW_country[i].code, GMT_DCW_country_with_state, GMT_DCW_N_COUNTRIES_WITH_STATES)) { + for (j = 0; j < GMT_DCW_STATES; j++) { + if (!strcmp (GMT_DCW_country[i].code, GMT_DCW_state[j].country)) { + sprintf (string, "%s.%s\t%s", GMT_DCW_country[i].code, GMT_DCW_state[j].code, GMT_DCW_state[j].name); + GMT_Put_Record (API, GMT_WRITE_DATA, Out); + } } } } @@ -929,6 +947,7 @@ void gmt_DCW_option (struct GMTAPI_CTRL *API, char option, unsigned int plot) { GMT_Usage (API, 3, "+l Just list the countries and their codes [no %s takes place].", action2[plot]); GMT_Usage (API, 3, "+L List states/territories for Argentina, Australia, Brazil, Canada, China, India, Russia and the US. " "Select =+l|L to only list countries from that continent or +L for that country(repeatable)."); + GMT_Usage (API, 3, "+n List collections/groups, their codes and list of items [no %s takes place].", action2[plot]); if (plot == 1) GMT_Usage (API, 3, "+p Draw outline using given [none]."); GMT_Usage (API, 3, "+z Add -Z to multisegment headers if extracting polygons."); @@ -983,6 +1002,10 @@ unsigned int gmt_DCW_parse (struct GMT_CTRL *GMT, char option, char *args, struc F->mode = DCW_GET_COUNTRY_AND_STATE; F->mode |= GMT_DCW_LIST; break; + case 'n': /* Collections list */ + F->mode = DCW_GET_COLLECTIONS; + F->mode |= GMT_DCW_LIST; + break; case 'c': /* Set up a clip path around the selections instead */ F->mode |= GMT_DCW_CLIP_IN; break; @@ -1024,7 +1047,7 @@ unsigned int gmt_DCW_parse (struct GMT_CTRL *GMT, char option, char *args, struc GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Cannot mix clipping and setting header codes!\n", option); n_errors++; } - if (this_item->codes[0] == '\0' && !(F->mode & (DCW_GET_COUNTRY+DCW_GET_COUNTRY_AND_STATE))) { /* Did not give +l or +L, and no codes */ + if (this_item->codes[0] == '\0' && !(F->mode & (DCW_GET_COUNTRY+DCW_GET_COUNTRY_AND_STATE+DCW_GET_COLLECTIONS))) { /* Did not give +l, +L, or +n and no codes */ GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No country codes given\n", option); n_errors++; } diff --git a/src/gmt_dcw.h b/src/gmt_dcw.h index 6cf2b1dd058..08eaaff07c4 100644 --- a/src/gmt_dcw.h +++ b/src/gmt_dcw.h @@ -31,7 +31,7 @@ #ifndef GMT_DCW_H #define GMT_DCW_H -#define DCW_OPT "[+l|L][+c|C][+g][+p][+z]" +#define DCW_OPT "[+l|L|n][+c|C][+g][+p][+z]" enum GMT_DCW_modes { GMT_DCW_REGION = 1, diff --git a/src/gmt_init.c b/src/gmt_init.c index 7d9a528420e..a4d2f1c7381 100644 --- a/src/gmt_init.c +++ b/src/gmt_init.c @@ -14389,9 +14389,9 @@ GMT_LOCAL unsigned int gmtinit_strip_R_from_E_in_pscoast (struct GMT_CTRL *GMT, /* Separate out any region-specific parts from one or more -E arguments and * pass those separately to a new -R instead (if -R not given). * Return code is bitflags: - * 0 : No _r|R or +l|L given, most likely just setting countries and implicitly -R + * 0 : No _r|R or +l|L|n given, most likely just setting countries and implicitly -R * 1 : Found a region-modifying modifier +r or +R - * 2 : Found a list-request +l or +L. Not plotting or region desired. + * 2 : Found a list-request +l, +L, or +n. Not plotting or region desired. */ char p[GMT_LEN256] = {""}, *c = NULL; char e_code[GMT_LEN256] = {""}, r_opt[GMT_LEN128] = {""}; @@ -14408,7 +14408,7 @@ GMT_LOCAL unsigned int gmtinit_strip_R_from_E_in_pscoast (struct GMT_CTRL *GMT, if (c) { /* Now process the modifiers */ c[0] = '+'; /* Unhide the modifiers */ pos = 0; /* Initialize position counter for this string */ - while (gmt_getmodopt (GMT, 'E', c, "lLcCgprRwz", &pos, p, &n_errors) && n_errors == 0) { + while (gmt_getmodopt (GMT, 'E', c, "lLncCgprRwz", &pos, p, &n_errors) && n_errors == 0) { switch (p[0]) { case 'r': case 'R': if (r_opt[0] == 0) { /* Only set this once */ @@ -14416,7 +14416,7 @@ GMT_LOCAL unsigned int gmtinit_strip_R_from_E_in_pscoast (struct GMT_CTRL *GMT, } break; case 'w': break; /* Do nothing with defunct +w that was never documented anyway */ - case 'l': case 'L': + case 'l': case 'L': case 'n': answer |= 2; /* Intentionally fall through - set this flag then fall through on purpose to default */ default: strcat (e_code, "+"); strcat (e_code, p); break; /* Append as is */ From abab40ade8500741e95770630f4959fd5c00e0f4 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 29 Nov 2021 16:13:09 -1000 Subject: [PATCH 04/15] Add named regions --- doc/rst/source/coast.rst | 21 +++++- src/gmt_dcw.c | 149 ++++++++++++++++++++++++++++----------- 2 files changed, 125 insertions(+), 45 deletions(-) diff --git a/doc/rst/source/coast.rst b/doc/rst/source/coast.rst index b09450ecb5d..dd230c580fe 100644 --- a/doc/rst/source/coast.rst +++ b/doc/rst/source/coast.rst @@ -119,12 +119,12 @@ Optional Arguments To select a state of a country (if available), append .state, e.g, US.TX for Texas. To specify a whole continent, prepend = to any of the continent codes AF (Africa), AN (Antarctica), AS (Asia), EU (Europe), OC (Oceania), - NA (North America), or SA (South America). To specify a collection give collection code or full name. Append **+l** to + NA (North America), or SA (South America). To specify a collection or named region, give either the code or full name. Append **+l** to just list the countries and their codes [no data extraction or plotting takes place]. Use **+L** to see states/territories for Argentina, Australia, Brazil, Canada, China, India, Russia and the US. You can append **+l**\|\ **+L** to **-E**\ =\ *continent* or **-E**\ *code* to only list countries in that continent or country; repeat if more than one continent or country is requested. - Finally, use **+n** to list the named collections of items available, and use **-E**\ *code* to only list + Finally, use **+n** to list the named collections or regions, and use **-E**\ *code* to only list collections that contains the listed codes. To set up clip paths based on your selection, append **+c** or **+C** for inside or outside (area between selection and the map boundary) clipping, respectively. To plot instead, @@ -273,6 +273,23 @@ Optional Arguments .. module_common_ends +Notes +----- + +The **-E** option can be expanded to take the user's own custom collections +and named regions. Users can create their own dcw-collections.txt file and +place it in their GMT user directory (typically ~/.gmt). The format of the +file is the same as the file distributed with DCW:: + + # Arbitrary comments and blank lines anywhere + # My France-Italy union + tag: FRIT FrancoItalic Union + list: FR,IT + # Stay away from those eels! + tag: SARG Sargasso Sea + region: 70W/40W/20N/35N + + Examples -------- diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 7f533c009a5..7332b71931c 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -35,7 +35,7 @@ * We expect to find the file dcw-gmt.nc, dcw-countries.txt, and dcw-states.txt * in one of the dirs accessible to GMT or pointed to by the default DIR_DCW. * We also look for dcw-collections.txt for named groups of items (2.0.2 or later) - * in the DCW directory and dcw-groups.txt in users .gmt directory. + * in the DCW directory and in the users .gmt directory. * See separate subversion project DCW for the maintenance of the raw files that * are used to build the netCDF file [svn://gmtserver.soest.hawai.edu/DCW]. */ @@ -48,6 +48,8 @@ #define DCW_GET_COLLECTIONS 4 /* Extract Collections */ #define DCW_DO_OUTLINE 1 /* Draw outline of polygons */ #define DCW_DO_FILL 2 /* Fill the polygons */ +#define DCW_NAMED_LIST 0 /* A lsit of DCW items that is given a name */ +#define DCW_NAMED_WESN 1 /* A w/e/s/n area that is given a name */ struct GMT_DCW_COUNTRY { /* Information per country */ char continent[4]; /* 2-char continent code (EU, NA, SA, AF, AU, AN) */ @@ -66,9 +68,12 @@ struct GMT_DCW_COUNTRY_STATE { /* Information per country with state */ }; struct GMT_DCW_COLLECTION { /* Information of collections representing multiple selections */ + unsigned int type; /* 0 is DCW list, 1 is named w/e/s/n */ char region[GMT_LEN16]; /* Up to 16-character collections, e.g., SCAND for Scandinavia */ char name[GMT_LEN64]; /* Up to 64-character optional full name, e.g., Scandinavia */ + char *wesn_string; /* The original w/e/s/n string in the collections file */ char *list; /* Comma-separated list of entities making up the collection */ + double wesn[4]; /* Extent of named regions */ }; /* For version 2.0.0 we follow https://en.wikipedia.org/wiki/ISO_3166-2:CN and use 2-char instead of int codes */ @@ -145,9 +150,10 @@ GMT_LOCAL bool gmtdcw_get_path (struct GMT_CTRL *GMT, char *name, char *suffix, GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY **C, struct GMT_DCW_STATE **S, struct GMT_DCW_COUNTRY_STATE **CS, struct GMT_DCW_COLLECTION **U, unsigned int dim[]) { /* Open and read list of countries and states and return via two struct and one char arrays plus dimensions in dim */ size_t n_alloc = 300; - unsigned int k, kc, n, collection; - int ns; + unsigned int j, k, kc, ns, collection, n_errors; + int nf; char path[PATH_MAX] = {""}, line[BUFSIZ] = {""}; + char w[GMT_LEN16] = {""}, e[GMT_LEN16] = {""}, s[GMT_LEN16] = {""}, n[GMT_LEN16] = {""}; FILE *fp = NULL; struct GMT_DCW_COUNTRY *Country = NULL; struct GMT_DCW_STATE *State = NULL; @@ -187,11 +193,11 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** return GMT_NOTSET; } State = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_DCW_STATE); - k = 0; n = 1; + k = 0; ns = 1; while ( gmt_fgets (GMT, line, BUFSIZ, fp)) { if (line[0] == '#') continue; /* Skip comments */ sscanf (line, "%s %s %[^\n]", State[k].country, State[k].code, State[k].name); - if (k && strcmp (State[k].country, State[k-1].country)) n++; /* New country with states */ + if (k && strcmp (State[k].country, State[k-1].country)) ns++; /* New country with states */ k++; if (k == n_alloc) { n_alloc += 100; @@ -204,12 +210,12 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** /* Get list of countries with states */ - dim[2] = n; /* Number of countries with states */ + dim[2] = ns; /* Number of countries with states */ if (CS) { /* Wants list returned */ - Country_State = gmt_M_memory (GMT, NULL, n, struct GMT_DCW_COUNTRY_STATE); + Country_State = gmt_M_memory (GMT, NULL, ns, struct GMT_DCW_COUNTRY_STATE); gmt_M_memcpy (Country_State[0].country, State[0].country, 4, char); - for (k = n = 1; k < dim[1]; k++) { - if (strcmp (State[k].country, State[k-1].country)) gmt_M_memcpy (Country_State[n++].country, State[k].country, 4, char); + for (k = ns = 1; k < dim[1]; k++) { + if (strcmp (State[k].country, State[k-1].country)) gmt_M_memcpy (Country_State[ns++].country, State[k].country, 4, char); } *CS = Country_State; } @@ -224,8 +230,8 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** if (!gmtdcw_get_path (GMT, "dcw-collections", ".txt", path)) continue; } - else { /* Look for user groups in the user dir, if present */ - if (!gmtlib_getuserpath (GMT, "dcw-groups.txt", path)) + else { /* Look for a similar user file in the user dir, if present */ + if (!gmtlib_getuserpath (GMT, "dcw-collections.txt", path)) continue; } /* Here we got the requested path */ @@ -242,16 +248,45 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** */ while (gmt_fgets (GMT, line, BUFSIZ, fp)) { if (line[0] == '#') continue; /* Skip comments */ - if (strncmp (line, "tag: ", 5U)) { + if (gmt_is_a_blank_line (line)) continue; + if (strncmp (line, "tag:", 4U)) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s is not in correct format - exit\n", path); gmt_M_free (GMT, Country); gmt_M_free (GMT, Collection); return GMT_NOTSET; } - ns = sscanf (&line[5], "%s %[^\n]", Collection[k].region, Collection[k].name); - gmt_fgets (GMT, line, BUFSIZ, fp); /* Get record with the list */ + nf = sscanf (&line[5], "%s %[^\n]", Collection[k].region, Collection[k].name); + gmt_fgets (GMT, line, BUFSIZ, fp); /* Get record with the list or region */ gmt_chop (line); - Collection[k].list = strdup (line); + if (strncmp (line, "list:", 5U) == 0) { /* Got a list of DCW entities */ + for (j = 5; line[j] == '\t' || line[j] == ' '; j++); /* Wind past leading whitespace */ + Collection[k].list = strdup (&line[j]); + } + else if (strncmp (line, "region:", 7U) == 0) { /* Got w/e/s/n for a named region */ + for (j = 7; line[j] == '\t' || line[j] == ' '; j++); /* Wind past leading whitespace */ + Collection[k].wesn_string = strdup (&line[j]); /* Copy of original w/e/s/n string */ + gmt_strrepc (line, '/', ' '); /* Replace slashes with spaces */ + n_errors = 0; + if (sscanf (&line[j], "%s %s %s %s", w, e, s, n) != 4) n_errors++; + n_errors += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf_arg (GMT, w, GMT_IS_LON, false, &Collection[k].wesn[XLO]), w); + n_errors += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf_arg (GMT, e, GMT_IS_LON, false, &Collection[k].wesn[XHI]), e); + n_errors += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf_arg (GMT, s, GMT_IS_LAT, false, &Collection[k].wesn[YLO]), s); + n_errors += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf_arg (GMT, n, GMT_IS_LAT, false, &Collection[k].wesn[YHI]), n); + if (n_errors) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s has incomplete or malformed w/e/s/n region: %s\n", line); + gmt_M_free (GMT, Country); + gmt_M_free (GMT, Collection); + return GMT_NOTSET; + } + else + Collection[k].type = DCW_NAMED_WESN; + } + else { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s is not in correct format - exit\n", path); + gmt_M_free (GMT, Country); + gmt_M_free (GMT, Collection); + return GMT_NOTSET; + } k++; kc++; if (k == n_alloc) { n_alloc += 100; @@ -380,7 +415,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL int64_t first, last; size_t np, max_np = 0U, n_alloc = 300; uint64_t k, seg, n_segments; - unsigned int n_items = 0, r_item = 0, pos = 0, kk, tbl = 0, j = 0, *order = NULL; + unsigned int n_items = 0, r_item = 0, pos = 0, kk, tbl = 0, j = 0, named_wesn = 0, *order = NULL; unsigned short int *dx = NULL, *dy = NULL; unsigned int GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, n_bodies[5] = {0, 0, 0, 0, 0}, special = 0; bool done, want_state, outline, fill = false, is_Antarctica = false, hole, new_CN_codes = false; @@ -447,18 +482,22 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL GMT_Report (GMT->parent, GMT_MSG_WARNING, "Continent code %s unrecognized\n", code); } else if ((ks = gmtdcw_found_collection (code, GMT_DCW_collection, n_bodies)) != GMT_NOTSET) { - /* Got a collection we wish to add */ - unsigned n_list = gmt_count_char (GMT, GMT_DCW_collection[ks].list, ',') + 1; /* Number of items in collection */ - if (n_items) strcat (list, ","); - strcat (list, GMT_DCW_collection[ks].list); - for (k = 0; k < n_list; k++) { - order[n_items++] = j; /* So we know which color/pen to apply for all items in this collection */ - if (n_items == n_alloc) { - n_alloc += 100; - order = gmt_M_memory (GMT, order, n_alloc, unsigned int); + if (GMT_DCW_collection[ks].type == DCW_NAMED_LIST) { + /* Got a collection we wish to add */ + unsigned n_list = gmt_count_char (GMT, GMT_DCW_collection[ks].list, ',') + 1; /* Number of items in collection */ + if (n_items) strcat (list, ","); + strcat (list, GMT_DCW_collection[ks].list); + for (k = 0; k < n_list; k++) { + order[n_items++] = j; /* So we know which color/pen to apply for all items in this collection */ + if (n_items == n_alloc) { + n_alloc += 100; + order = gmt_M_memory (GMT, order, n_alloc, unsigned int); + } } + GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Collection code expanded from %s to %s [%d countries]\n", F->item[j]->codes, GMT_DCW_collection[ks].list, n_list); } - GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Collection code expanded from %s to %s [%d countries]\n", F->item[j]->codes, GMT_DCW_collection[ks].list, n_list); + else /* Named regions do not correspond to any data code so skipped here */ + named_wesn++; } else { /* Just append this single one */ if (n_items) strcat (list, ","); @@ -473,7 +512,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL } } - if (n_items) { + if (n_items || named_wesn) { GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Requested %d DCW items: %s\n", n_items, list); order = gmt_M_memory (GMT, order, n_items, unsigned int); } @@ -482,8 +521,10 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_M_free (GMT, order); gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); - for (k = 0; k < dim[3]+dim[4]; k++) - gmt_M_str_free (GMT_DCW_collection[k].list); + for (k = 0; k < dim[3]+dim[4]; k++) { + if (GMT_DCW_collection[k].list) gmt_M_str_free (GMT_DCW_collection[k].list); + if (GMT_DCW_collection[k].wesn_string) gmt_M_str_free (GMT_DCW_collection[k].wesn_string); + } gmt_M_free (GMT, GMT_DCW_collection); return NULL; } @@ -585,8 +626,23 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_set_geographic (GMT, GMT_OUT); } - if (mode & GMT_DCW_REGION) /* Just update wesn */ - Z = gmt_M_memory (GMT, NULL, n_items, struct GMT_RANGE); + if (mode & GMT_DCW_REGION) { /* Just update wesn */ + Z = gmt_M_memory (GMT, NULL, n_items+named_wesn, struct GMT_RANGE); + if (named_wesn) { /* Must pick up the wesn of those entities */ + for (j = 0; j < F->n_items; j++) { + pos = 0; + while (gmt_strtok (F->item[j]->codes, ",", &pos, code)) { /* Loop over items */ + if ((ks = gmtdcw_found_collection (code, GMT_DCW_collection, n_bodies)) != GMT_NOTSET && GMT_DCW_collection[ks].type == DCW_NAMED_WESN) { + /* Expand current wesn by this named area */ + Z[r_item].west = GMT_DCW_collection[ks].wesn[XLO]; Z[r_item++].east = GMT_DCW_collection[ks].wesn[XHI]; + if (GMT_DCW_collection[ks].wesn[YLO] < wesn[YLO]) wesn[YLO] = GMT_DCW_collection[ks].wesn[YLO]; + if (GMT_DCW_collection[ks].wesn[YHI] > wesn[YHI]) wesn[YHI] = GMT_DCW_collection[ks].wesn[YHI]; + GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Collection code expanded from %s to %s \n", F->item[j]->codes, GMT_DCW_collection[ks].wesn_string); + } + } + } + } + } pos = item = 0; Out = gmt_new_record (GMT, out, NULL); /* Since we only need to worry about numerics in this module */ @@ -773,7 +829,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_M_free (GMT, Out); if (mode & GMT_DCW_REGION) { - gmt_find_range (GMT, Z, n_items, &wesn[XLO], &wesn[XHI]); + gmt_find_range (GMT, Z, r_item, &wesn[XLO], &wesn[XHI]); gmt_M_free (GMT, Z); GMT->current.io.geo.range = GMT_IGNORE_RANGE; /* Override this setting explicitly */ gmt_extend_region (GMT, wesn, F->adjust, F->inc); @@ -870,15 +926,19 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { if (list_mode & DCW_GET_COLLECTIONS) { int k, nc = n_bodies[3] + n_bodies[4]; /* Total number of collections */ for (i = 0; i < nc; i++) { - if (search) { /* Listed collection(s) */ - bool found = false; - for (kk = 0; !found && kk < F->n_items; kk++) { - if (strstr (GMT_DCW_collection[i].list, F->item[kk]->codes)) - found = true; /* This item is part of this collection */ + if (GMT_DCW_collection[i].type == DCW_NAMED_LIST) { + if (search) { /* Listed collection(s) */ + bool found = false; + for (kk = 0; !found && kk < F->n_items; kk++) { + if (strstr (GMT_DCW_collection[i].list, F->item[kk]->codes)) + found = true; /* This item is part of this collection */ + } + if (!found) continue; } - if (!found) continue; + sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].name, GMT_DCW_collection[i].region, GMT_DCW_collection[i].list); } - sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].name, GMT_DCW_collection[j].region, GMT_DCW_collection[i].list); + else /* Just a named region */ + sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].name, GMT_DCW_collection[i].region, GMT_DCW_collection[i].wesn_string); GMT_Put_Record (API, GMT_WRITE_DATA, Out); } } @@ -921,8 +981,10 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); gmt_M_free (GMT, GMT_DCW_country_with_state); - for (k = 0; k < n_bodies[3]+n_bodies[4]; k++) - gmt_M_str_free (GMT_DCW_collection[k].list); + for (k = 0; k < n_bodies[3]+n_bodies[4]; k++) { + if (GMT_DCW_collection[k].list) gmt_M_str_free (GMT_DCW_collection[k].list); + if (GMT_DCW_collection[k].wesn_string) gmt_M_str_free (GMT_DCW_collection[k].wesn_string); + } gmt_M_free (GMT, GMT_DCW_collection); return ((list_mode & GMT_DCW_LIST)); } @@ -938,7 +1000,8 @@ void gmt_DCW_option (struct GMTAPI_CTRL *API, char option, unsigned int plot) { "Append comma-separated list of ISO 3166 codes for countries to %s, i.e., " ",,... etc., using the 2-character country codes. " "To select a state of a country (if available), append .state, e.g, US.TX for Texas. " - "To select a whole continent, use =AF|AN|AS|EU|OC|NA|SA as . Some modifiers:", usage[plot], action[plot]); + "To select a whole continent, use =AF|AN|AS|EU|OC|NA|SA as . For collections and named regions, " + "append their codes or full name. Some modifiers:", usage[plot], action[plot]); if (plot == 1) { GMT_Usage (API, 3, "+c Set clip paths for the inside area [none]."); GMT_Usage (API, 3, "+C Set clip paths for the outside area [none]."); @@ -947,7 +1010,7 @@ void gmt_DCW_option (struct GMTAPI_CTRL *API, char option, unsigned int plot) { GMT_Usage (API, 3, "+l Just list the countries and their codes [no %s takes place].", action2[plot]); GMT_Usage (API, 3, "+L List states/territories for Argentina, Australia, Brazil, Canada, China, India, Russia and the US. " "Select =+l|L to only list countries from that continent or +L for that country(repeatable)."); - GMT_Usage (API, 3, "+n List collections/groups, their codes and list of items [no %s takes place].", action2[plot]); + GMT_Usage (API, 3, "+n List collections and named regions, their codes and list of items (or region) [no %s takes place].", action2[plot]); if (plot == 1) GMT_Usage (API, 3, "+p Draw outline using given [none]."); GMT_Usage (API, 3, "+z Add -Z to multisegment headers if extracting polygons."); From cddf4f19cd6227fefd4aaf68a4c2da8c5d4a0b3d Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 30 Nov 2021 11:55:25 -1000 Subject: [PATCH 05/15] More checks and filename change --- doc/rst/source/coast.rst | 19 +++++---- src/gmt_dcw.c | 89 +++++++++++++++++++++++++++------------- 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/doc/rst/source/coast.rst b/doc/rst/source/coast.rst index dd230c580fe..2d26fc5a2a9 100644 --- a/doc/rst/source/coast.rst +++ b/doc/rst/source/coast.rst @@ -277,18 +277,23 @@ Notes ----- The **-E** option can be expanded to take the user's own custom collections -and named regions. Users can create their own dcw-collections.txt file and -place it in their GMT user directory (typically ~/.gmt). The format of the -file is the same as the file distributed with DCW:: +and named regions. Users can create a dcw.conf file and place it in their +GMT user directory (typically ~/.gmt). The format of the file is the same +as the dcw-collections.txt file distributed with DCW:: # Arbitrary comments and blank lines anywhere - # My France-Italy union - tag: FRIT FrancoItalic Union + + # The France-Italian union (2042-45) of gallery example 34. + tag: FRIT Franco-Italian Union list: FR,IT - # Stay away from those eels! + # Stay away from those dangerous eels! tag: SARG Sargasso Sea - region: 70W/40W/20N/35N + region: 70W/40W/20N/35N +Each *tag:* record must be immediately followed by either a *list:* or *region:* record. +All tags must be in upper case and be at least 3 characters long. The optional name +must be capitalized. Either the *tag* or the *name* (if available) can be used to make +selections in **-R** or **-E**. Examples -------- diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 7332b71931c..9b696a5a7fc 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -147,10 +147,27 @@ GMT_LOCAL bool gmtdcw_get_path (struct GMT_CTRL *GMT, char *name, char *suffix, return (false); } +GMT_LOCAL bool gmtdcw_not_upper_case (char *text) { + unsigned int k = 0; + while (text[k]) { + if (islower (text[k])) return true; + k++; + } + return false; +} + +GMT_LOCAL void gmtdcw_freestrings (struct GMT_DCW_COLLECTION *Collection, unsigned int n) { + unsigned int k; + for (k = 0; k < n; k++) { + if (Collection[k].list) gmt_M_str_free (Collection[k].list); + if (Collection[k].wesn_string) gmt_M_str_free (Collection[k].wesn_string); + } +} + GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY **C, struct GMT_DCW_STATE **S, struct GMT_DCW_COUNTRY_STATE **CS, struct GMT_DCW_COLLECTION **U, unsigned int dim[]) { /* Open and read list of countries and states and return via two struct and one char arrays plus dimensions in dim */ size_t n_alloc = 300; - unsigned int j, k, kc, ns, collection, n_errors; + unsigned int j, k, ns, collection, n_errors; int nf; char path[PATH_MAX] = {""}, line[BUFSIZ] = {""}; char w[GMT_LEN16] = {""}, e[GMT_LEN16] = {""}, s[GMT_LEN16] = {""}, n[GMT_LEN16] = {""}; @@ -231,15 +248,14 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** continue; } else { /* Look for a similar user file in the user dir, if present */ - if (!gmtlib_getuserpath (GMT, "dcw-collections.txt", path)) + if (!gmtlib_getuserpath (GMT, "dcw.conf", path)) continue; } /* Here we got the requested path */ - kc = 0; /* Number of collections in this set */ + dim[3+collection] = 0; /* Number of collections in this set */ if ((fp = fopen (path, "r")) == NULL) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to open file %s [permission trouble?]\n", path); - gmt_M_free (GMT, Country); - return GMT_NOTSET; + goto bail; } /* File format is sequences like this: # Scandinavia @@ -251,11 +267,23 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** if (gmt_is_a_blank_line (line)) continue; if (strncmp (line, "tag:", 4U)) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s is not in correct format - exit\n", path); - gmt_M_free (GMT, Country); - gmt_M_free (GMT, Collection); - return GMT_NOTSET; + goto bail; } nf = sscanf (&line[5], "%s %[^\n]", Collection[k].region, Collection[k].name); + if (gmtdcw_not_upper_case (Collection[k].region)) { + gmt_str_toupper (Collection[k].region); + GMT_Report (GMT->parent, GMT_MSG_WARNING, "Collection tag not in upper case format - converted to %s\n", (Collection[k].region)); + } + if (nf == 2) { /* Gave a name */ + if (isdigit (Collection[k].name[0])) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection name must start with (capital) letter (%s)\n", (Collection[k].name)); + goto bail; + } + else if (!isupper (Collection[k].name[0])) { + Collection[k].name[0] = toupper (Collection[k].name[0]); + GMT_Report (GMT->parent, GMT_MSG_WARNING, "Collection name not capitalized - converted to %s\n", (Collection[k].name)); + } + } gmt_fgets (GMT, line, BUFSIZ, fp); /* Get record with the list or region */ gmt_chop (line); if (strncmp (line, "list:", 5U) == 0) { /* Got a list of DCW entities */ @@ -272,29 +300,27 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** n_errors += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf_arg (GMT, e, GMT_IS_LON, false, &Collection[k].wesn[XHI]), e); n_errors += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf_arg (GMT, s, GMT_IS_LAT, false, &Collection[k].wesn[YLO]), s); n_errors += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf_arg (GMT, n, GMT_IS_LAT, false, &Collection[k].wesn[YHI]), n); + if (Collection[k].wesn[YHI] <= Collection[k].wesn[YLO]) n_errors++; + if (Collection[k].wesn[XHI] <= Collection[k].wesn[XLO]) n_errors++; if (n_errors) { - GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s has incomplete or malformed w/e/s/n region: %s\n", line); - gmt_M_free (GMT, Country); - gmt_M_free (GMT, Collection); - return GMT_NOTSET; + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s has incomplete or malformed w/e/s/n region: %s\n", Collection[k].wesn_string); + goto bail; } else Collection[k].type = DCW_NAMED_WESN; } else { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s is not in correct format - exit\n", path); - gmt_M_free (GMT, Country); - gmt_M_free (GMT, Collection); - return GMT_NOTSET; + goto bail; } - k++; kc++; + k++; + dim[3+collection]++; if (k == n_alloc) { n_alloc += 100; Collection = gmt_M_memory (GMT, Collection, n_alloc, struct GMT_DCW_COLLECTION); } } fclose (fp); - dim[3+collection] = kc; } if (k == 0) /* No such files at all */ gmt_M_free (GMT, Collection); @@ -309,6 +335,15 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** GMT_Report (GMT->parent, GMT_MSG_DEBUG, "DCW: Found %u countries, %u countries with states, %u states, %d DCW collections and %d user groups\n", dim[0], dim[2], dim[1], dim[3], dim[4]); return 0; + +bail: + gmt_M_free (GMT, Country); + gmt_M_free (GMT, State); + gmt_M_free (GMT, Country_State); + gmtdcw_freestrings (Collection, dim[3]+dim[4]); + gmt_M_free (GMT, Collection); + + return GMT_NOTSET; } GMT_LOCAL int gmtdcw_comp_countries (const void *p1, const void *p2) { @@ -521,10 +556,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_M_free (GMT, order); gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); - for (k = 0; k < dim[3]+dim[4]; k++) { - if (GMT_DCW_collection[k].list) gmt_M_str_free (GMT_DCW_collection[k].list); - if (GMT_DCW_collection[k].wesn_string) gmt_M_str_free (GMT_DCW_collection[k].wesn_string); - } + gmtdcw_freestrings (GMT_DCW_collection, dim[3]+dim[4]); gmt_M_free (GMT, GMT_DCW_collection); return NULL; } @@ -823,8 +855,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_nc_close (GMT, ncid); gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); - for (k = 0; k < n_bodies[3]+n_bodies[4]; k++) - gmt_M_str_free (GMT_DCW_collection[k].list); + gmtdcw_freestrings (GMT_DCW_collection, n_bodies[3]+n_bodies[4]); gmt_M_free (GMT, GMT_DCW_collection); gmt_M_free (GMT, Out); @@ -925,6 +956,9 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { if (list_mode & DCW_GET_COLLECTIONS) { int k, nc = n_bodies[3] + n_bodies[4]; /* Total number of collections */ + gmt_set_tableheader (GMT, GMT_OUT, true); + sprintf (string, "TAG\tName\t\tCodes|Region"); + GMT_Put_Record (API, GMT_WRITE_TABLE_HEADER, string); for (i = 0; i < nc; i++) { if (GMT_DCW_collection[i].type == DCW_NAMED_LIST) { if (search) { /* Listed collection(s) */ @@ -935,10 +969,10 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { } if (!found) continue; } - sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].name, GMT_DCW_collection[i].region, GMT_DCW_collection[i].list); + sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].region, GMT_DCW_collection[i].name, GMT_DCW_collection[i].list); } else /* Just a named region */ - sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].name, GMT_DCW_collection[i].region, GMT_DCW_collection[i].wesn_string); + sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].region, GMT_DCW_collection[i].name, GMT_DCW_collection[i].wesn_string); GMT_Put_Record (API, GMT_WRITE_DATA, Out); } } @@ -981,10 +1015,7 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { gmt_M_free (GMT, GMT_DCW_country); gmt_M_free (GMT, GMT_DCW_state); gmt_M_free (GMT, GMT_DCW_country_with_state); - for (k = 0; k < n_bodies[3]+n_bodies[4]; k++) { - if (GMT_DCW_collection[k].list) gmt_M_str_free (GMT_DCW_collection[k].list); - if (GMT_DCW_collection[k].wesn_string) gmt_M_str_free (GMT_DCW_collection[k].wesn_string); - } + gmtdcw_freestrings (GMT_DCW_collection, n_bodies[3]+n_bodies[4]); gmt_M_free (GMT, GMT_DCW_collection); return ((list_mode & GMT_DCW_LIST)); } From 2deb9aa5ece42ffaa7ac8350e5d38d931342102a Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 30 Nov 2021 15:36:44 -1000 Subject: [PATCH 06/15] Remove duplicate Antarctica check Also allow lower-case named regions. --- src/gmt_dcw.c | 4 ++-- src/gmt_init.c | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 9b696a5a7fc..2e0198ec6ff 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -403,7 +403,7 @@ GMT_LOCAL int gmtdcw_found_collection (char *code, struct GMT_DCW_COLLECTION *GM if (nc == 0) return GMT_NOTSET; /* No collections available */ for (k = 0; k < nc; k++) { - if (strcmp (code, GMT_DCW_collection[k].region) == 0 || (GMT_DCW_collection[k].name[0] && strcmp (code, GMT_DCW_collection[k].name) == 0)) + if (strcasecmp (code, GMT_DCW_collection[k].region) == 0 || (GMT_DCW_collection[k].name[0] && strcasecmp (code, GMT_DCW_collection[k].name) == 0)) return (k); /* Found a matching collection */ } return (GMT_NOTSET); /* Not found */ @@ -864,7 +864,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL gmt_M_free (GMT, Z); GMT->current.io.geo.range = GMT_IGNORE_RANGE; /* Override this setting explicitly */ gmt_extend_region (GMT, wesn, F->adjust, F->inc); - if (is_Antarctica) { /* Must override to include pole and full longitude range */ + if (is_Antarctica) { /* Must override to include south pole and full longitude range */ wesn[YLO] = -90.0; /* Since it is a South polar cap */ wesn[XLO] = 0.0; wesn[XHI] = 360.0; diff --git a/src/gmt_init.c b/src/gmt_init.c index a4d2f1c7381..8181e17d873 100644 --- a/src/gmt_init.c +++ b/src/gmt_init.c @@ -8816,8 +8816,8 @@ int gmt_parse_R_option (struct GMT_CTRL *GMT, char *arg) { strncpy (string, &item[1], GMT_BUFSIZ-1); GMT->current.io.geo.range = (item[0] == 'g') ? GMT_IS_0_TO_P360_RANGE : GMT_IS_M180_TO_P180_RANGE; } - else if (isupper ((int)item[0]) || item[0] == '=' || strchr (item, ',')) { - /* Region specified via country codes with optional round off/extension, e.g., -RNO+r1 or -R=EU */ + else if (isalpha ((int)item[0]) || item[0] == '=' || strchr (item, ',')) { + /* Region specified via country codes or named regions with optional round off/extension, e.g., -RNO+r1 or -R=EU or -R"Middle East" */ struct GMT_DCW_SELECT info; GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Got country code for region (%s)\n", item); gmt_M_memset (&info, 1, struct GMT_DCW_SELECT); /* To ensure it is all NULL, 0 */ @@ -8825,11 +8825,6 @@ int gmt_parse_R_option (struct GMT_CTRL *GMT, char *arg) { (void) gmt_DCW_operation (GMT, &info, GMT->common.R.wesn, GMT_DCW_REGION); /* Get region */ gmt_DCW_free (GMT, &info); if (fabs (GMT->common.R.wesn[XLO]) > 1000.0) return (GMT_MAP_NO_REGION); - if (strstr (item, "=AN")) { /* Antarctica is the only polar cap polygon so w,e,s must be reset */ - GMT->common.R.wesn[XLO] = -180.0; - GMT->common.R.wesn[XHI] = +180.0; - GMT->common.R.wesn[YLO] = -90.0; - } if (GMT->common.R.wesn[XLO] < 0.0 && GMT->common.R.wesn[XHI] > 0.0) GMT->current.io.geo.range = GMT_IS_M180_TO_P180_RANGE; else From 0455c7374e11455abf677681e3c8353a02411375 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 1 Dec 2021 09:11:00 -1000 Subject: [PATCH 07/15] Relax case checking Also return real error of parsing fails. --- doc/rst/source/coast.rst | 5 ++-- src/gmt_dcw.c | 49 ++++++++++++++-------------------------- src/pscoast.c | 6 +++-- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/doc/rst/source/coast.rst b/doc/rst/source/coast.rst index 2d26fc5a2a9..6b921cc25a4 100644 --- a/doc/rst/source/coast.rst +++ b/doc/rst/source/coast.rst @@ -291,9 +291,8 @@ as the dcw-collections.txt file distributed with DCW:: region: 70W/40W/20N/35N Each *tag:* record must be immediately followed by either a *list:* or *region:* record. -All tags must be in upper case and be at least 3 characters long. The optional name -must be capitalized. Either the *tag* or the *name* (if available) can be used to make -selections in **-R** or **-E**. +All tags should be at least 3 characters long. Either the *tag* or the *name* (if available) +can be used to make selections in **-R** or **-E**. Examples -------- diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 2e0198ec6ff..8f3c1a66cef 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -147,15 +147,6 @@ GMT_LOCAL bool gmtdcw_get_path (struct GMT_CTRL *GMT, char *name, char *suffix, return (false); } -GMT_LOCAL bool gmtdcw_not_upper_case (char *text) { - unsigned int k = 0; - while (text[k]) { - if (islower (text[k])) return true; - k++; - } - return false; -} - GMT_LOCAL void gmtdcw_freestrings (struct GMT_DCW_COLLECTION *Collection, unsigned int n) { unsigned int k; for (k = 0; k < n; k++) { @@ -260,7 +251,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** /* File format is sequences like this: # Scandinavia tag: SCA Scandinavia - NO,SE,DK + list: NO,SE,DK or region: w/e/s/n */ while (gmt_fgets (GMT, line, BUFSIZ, fp)) { if (line[0] == '#') continue; /* Skip comments */ @@ -270,43 +261,37 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** goto bail; } nf = sscanf (&line[5], "%s %[^\n]", Collection[k].region, Collection[k].name); - if (gmtdcw_not_upper_case (Collection[k].region)) { - gmt_str_toupper (Collection[k].region); - GMT_Report (GMT->parent, GMT_MSG_WARNING, "Collection tag not in upper case format - converted to %s\n", (Collection[k].region)); + if (nf == 2 && !isalpha (Collection[k].name[0])) { /* Gave a name that do not start with a letter */ + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection name must start with a letter (%s)\n", (Collection[k].name)); + goto bail; } - if (nf == 2) { /* Gave a name */ - if (isdigit (Collection[k].name[0])) { - GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection name must start with (capital) letter (%s)\n", (Collection[k].name)); - goto bail; - } - else if (!isupper (Collection[k].name[0])) { - Collection[k].name[0] = toupper (Collection[k].name[0]); - GMT_Report (GMT->parent, GMT_MSG_WARNING, "Collection name not capitalized - converted to %s\n", (Collection[k].name)); - } + /* Get record with the list or region */ + if (gmt_fgets (GMT, line, BUFSIZ, fp) == NULL) { /* Read error */ + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to read list or region for collection %s\n", (Collection[k].region)); + goto bail; } - gmt_fgets (GMT, line, BUFSIZ, fp); /* Get record with the list or region */ - gmt_chop (line); + gmt_chop (line); /* Eliminate trailing \r \n stuff */ if (strncmp (line, "list:", 5U) == 0) { /* Got a list of DCW entities */ - for (j = 5; line[j] == '\t' || line[j] == ' '; j++); /* Wind past leading whitespace */ + for (j = 5; line[j] == '\t' || line[j] == ' '; j++); /* Wind past optional leading whitespace */ Collection[k].list = strdup (&line[j]); } else if (strncmp (line, "region:", 7U) == 0) { /* Got w/e/s/n for a named region */ - for (j = 7; line[j] == '\t' || line[j] == ' '; j++); /* Wind past leading whitespace */ + for (j = 7; line[j] == '\t' || line[j] == ' '; j++); /* Wind past optional leading whitespace */ Collection[k].wesn_string = strdup (&line[j]); /* Copy of original w/e/s/n string */ - gmt_strrepc (line, '/', ' '); /* Replace slashes with spaces */ + gmt_strrepc (line, '/', ' '); /* Replace slashes with spaces to ease parsing */ n_errors = 0; if (sscanf (&line[j], "%s %s %s %s", w, e, s, n) != 4) n_errors++; n_errors += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf_arg (GMT, w, GMT_IS_LON, false, &Collection[k].wesn[XLO]), w); n_errors += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf_arg (GMT, e, GMT_IS_LON, false, &Collection[k].wesn[XHI]), e); n_errors += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf_arg (GMT, s, GMT_IS_LAT, false, &Collection[k].wesn[YLO]), s); n_errors += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf_arg (GMT, n, GMT_IS_LAT, false, &Collection[k].wesn[YHI]), n); - if (Collection[k].wesn[YHI] <= Collection[k].wesn[YLO]) n_errors++; + if (Collection[k].wesn[YHI] <= Collection[k].wesn[YLO]) n_errors++; /* Basic sanity checks of the region values */ if (Collection[k].wesn[XHI] <= Collection[k].wesn[XLO]) n_errors++; if (n_errors) { - GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s has incomplete or malformed w/e/s/n region: %s\n", Collection[k].wesn_string); + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Collection file %s has incomplete, malformed or incorrect w/e/s/n region: %s\n", Collection[k].wesn_string); goto bail; } - else + else /* Set the collection type */ Collection[k].type = DCW_NAMED_WESN; } else { @@ -322,7 +307,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** } fclose (fp); } - if (k == 0) /* No such files at all */ + if (k == 0) /* No collection files at all */ gmt_M_free (GMT, Collection); else Collection = gmt_M_memory (GMT, Collection, k, struct GMT_DCW_COLLECTION); @@ -928,7 +913,7 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { list_mode = F->mode; if ((list_mode & GMT_DCW_LIST) == 0) return 0; - if (gmtdcw_load_lists (GMT, &GMT_DCW_country, &GMT_DCW_state, &GMT_DCW_country_with_state, &GMT_DCW_collection, n_bodies)) return 0; /* Something went wrong */ + if (gmtdcw_load_lists (GMT, &GMT_DCW_country, &GMT_DCW_state, &GMT_DCW_country_with_state, &GMT_DCW_collection, n_bodies)) return GMT_NOTSET; /* Something went wrong */ GMT_DCW_COUNTRIES = n_bodies[0]; GMT_DCW_STATES = n_bodies[1]; GMT_DCW_N_COUNTRIES_WITH_STATES = n_bodies[2]; diff --git a/src/pscoast.c b/src/pscoast.c index 31ce80be42d..1781b881e9b 100644 --- a/src/pscoast.c +++ b/src/pscoast.c @@ -537,8 +537,10 @@ static int parse (struct GMT_CTRL *GMT, struct PSCOAST_CTRL *Ctrl, struct GMT_OP } if ((error = gmt_DCW_list (GMT, &(Ctrl->E.info)))) { /* This is either success or failure... */ - if (error != GMT_DCW_LIST) n_errors++; /* Not good */ - else return NOT_REALLY_AN_ERROR; /* If +l|L was given we list countries and return a fake error that will be replaced by 0 */ + if (error != GMT_DCW_LIST) + return (1); /* Not good */ + else + return NOT_REALLY_AN_ERROR; /* If +l|L was given we list countries and return a fake error that will be replaced by 0 */ } if (!GMT->common.J.active) { /* So without -J we can only do -M or report region only */ From 8a395a7fed66b69c605561447c8f7aa5b34ca0b4 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 1 Dec 2021 12:41:01 -1000 Subject: [PATCH 08/15] Allow country and continent names --- doc/rst/source/coast.rst | 13 +++---- doc/rst/source/explain_-Rgeo.rst_ | 13 +++---- src/gmt_dcw.c | 57 ++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/doc/rst/source/coast.rst b/doc/rst/source/coast.rst index 6b921cc25a4..648166cf2a7 100644 --- a/doc/rst/source/coast.rst +++ b/doc/rst/source/coast.rst @@ -114,18 +114,19 @@ Optional Arguments **-E**\ *code1,code2,...*\ [**+l**\|\ **L**\|\ **n**][**+c**\|\ **C**][**+g**\ *fill*][**+p**\ *pen*][**+z**] Select painting, clipping or dumping country polygons from the Digital Chart of the World. This is another dataset independent of GSHHG and hence the **-A** and **-D** options do not apply. - Append one or more comma-separated countries using the - `2-character ISO 3166-1 alpha-2 convention `_. + Append one or more comma-separated countries using either the + `2-character ISO 3166-1 alpha-2 convention `_ + (e.g., NO for Norway) or the full country name (e.g., Norway). To select a state of a country (if available), append .state, e.g, US.TX for Texas. To specify a - whole continent, prepend = to any of the continent codes AF (Africa), - AN (Antarctica), AS (Asia), EU (Europe), OC (Oceania), - NA (North America), or SA (South America). To specify a collection or named region, give either the code or full name. Append **+l** to + whole continent, prepend = to any of the continent codes AF (Africa), AN (Antarctica), AS (Asia), EU (Europe), OC (Oceania), + NA (North America), or SA (South America), or spell out the full names. To specify a collection or named region, + give either the code or the full name. Append **+l** to just list the countries and their codes [no data extraction or plotting takes place]. Use **+L** to see states/territories for Argentina, Australia, Brazil, Canada, China, India, Russia and the US. You can append **+l**\|\ **+L** to **-E**\ =\ *continent* or **-E**\ *code* to only list countries in that continent or country; repeat if more than one continent or country is requested. Finally, use **+n** to list the named collections or regions, and use **-E**\ *code* to only list - collections that contains the listed codes. + collections that contains the listed codes. All names are case-insensitive. To set up clip paths based on your selection, append **+c** or **+C** for inside or outside (area between selection and the map boundary) clipping, respectively. To plot instead, append **+p**\ *pen* to draw polygon outlines [no outline] and diff --git a/doc/rst/source/explain_-Rgeo.rst_ b/doc/rst/source/explain_-Rgeo.rst_ index 09e9dfd2d16..e267efa0a40 100644 --- a/doc/rst/source/explain_-Rgeo.rst_ +++ b/doc/rst/source/explain_-Rgeo.rst_ @@ -21,12 +21,13 @@ #. **-R**\ *code1,code2,...*\ [**+e**\ \|\ **r**\ \|\ **R**\ *incs*]. This indirectly supplies the region by consulting the DCW (Digital Chart of the World) database and derives the bounding regions for one or more - countries given by the codes. Simply append one or more comma-separated countries using the two-character - `ISO 3166-1 alpha-2 convention `_. To select a state within a - country (if available), append .state, e.g, US.TX for Texas. To specify a whole continent, prepend **=** to any - of the continent codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), - **NA** (North America), or **SA** (South America). Finally, append any collection abbreviations or full names - for the region of the collection. The following modifiers can be appended: + countries given by the codes. Simply append one or more comma-separated countries using either the two-character + `ISO 3166-1 alpha-2 convention `_ or the full country name. + To select a state within a country (if available), append .state, e.g, US.TX for Texas, or alternatively just + specify the full state name (e.g., Texas). To specify a whole continent, prepend **=** to any of the continent + codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), **NA** (North America), + or **SA** (South America), or spell out the full continent name. Finally, append any collection abbreviations or full + names for the extent of the collection or named region. All codes are case-insensitive. The following modifiers can be appended: - **+r** to adjust the region boundaries to be multiples of the steps indicated by *inc*, *xinc*/*yinc*, or *winc*/*einc*/*sinc*/*ninc* [default is no adjustment]. For example, **-R**\ *FR*\ **+r**\ 1 will select the diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 8f3c1a66cef..046d311a1d4 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -86,6 +86,7 @@ struct GMT_DCW_CHINA_CODES { /* Compile in read-only structures and arrays with the information */ static char *GMT_DCW_continents[GMT_DCW_N_CONTINENTS] = {"Africa", "Antarctica", "Asia", "Europe", "Oceania", "North America", "South America", "Miscellaneous"}; +static char *GMT_DCW_contcodes[GMT_DCW_N_CONTINENTS] = {"AF", "AN", "AS", "EU", "OC", "NA", "SA", ""}; /* Local functions only visible inside this file */ @@ -354,6 +355,38 @@ GMT_LOCAL int gmtdcw_find_country (char *code, struct GMT_DCW_COUNTRY *list, int return (low); } +GMT_LOCAL bool gmtdcw_got_name (char *name, struct GMT_DCW_COUNTRY *clist, int nc, struct GMT_DCW_STATE *slist, int ns, char *ISO) { + /* Slow linear search to find ISO that matches the given country name */ + unsigned int k; + for (k = 0; k < nc; k++) { + if (strcasecmp (name, clist[k].name) == 0) { + strncpy (ISO, clist[k].code, 2); ISO[2] = '\0'; /* Terminate string */ + return (true); + } + } + /* Got here because no such country. Try states */ + for (k = 0; k < ns; k++) { + if (strcasecmp (name, slist[k].name) == 0) { + sprintf (ISO, "%s.%s%c", slist[k].country, slist[k].code, '\0'); + return (true); + } + } + return (false); /* Not found */ +} + +GMT_LOCAL bool gmtdcw_got_continent (char *name, char *continents[], char *contcodes[], int nc, char *ISO) { + /* Slow linear search to find ISO that matches the given country name */ + unsigned int k; + if (strlen (name) < 4) return (false); /* Cannot be a continent name since Asia is the shortest */ + for (k = 0; k < nc; k++) { + if (strcasecmp (name, continents[k]) == 0) { + sprintf (ISO, "=%s", contcodes[k]); ISO[3] = '\0'; /* Terminate string */ + return (true); + } + } + return (false); +} + GMT_LOCAL int gmtdcw_find_state (struct GMT_CTRL *GMT, char *scode, char *ccode, struct GMT_DCW_STATE *slist, int ns, bool check) { /* Return state id given country and state codes using a linear search */ int i; @@ -394,6 +427,15 @@ GMT_LOCAL int gmtdcw_found_collection (char *code, struct GMT_DCW_COLLECTION *GM return (GMT_NOTSET); /* Not found */ } +GMT_LOCAL bool gmtdcw_not_ISO (char *code) { + /* Return true if not XX or XX.YY */ + unsigned int L = strlen (code); + if (L > 5) return true; /* Cannot be ISO code */ + if (L == 2) return false; /* Must be ISO code */ + if (L != 5) return true; /* Unless it is XX.YY it is not ISO */ + return (code[2] != '.'); /* True if not XX.YY */ +} + /*----------------------------------------------------------| * Public functions that are part of the GMT Devel library | *----------------------------------------------------------| @@ -443,7 +485,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL char yname[GMT_LEN16] = {""}, code[GMT_LEN16] = {""}, state[GMT_LEN16] = {""}; char msg[GMT_BUFSIZ] = {""}, path[PATH_MAX] = {""}, list[GMT_BUFSIZ] = {""}; char version[GMT_LEN32] = {""}, gmtversion[GMT_LEN32] = {""}, source[GMT_LEN256] = {""}, title[GMT_LEN256] = {""}; - char label[GMT_LEN256] = {""}, header[GMT_LEN256] = {""}; + char label[GMT_LEN256] = {""}, header[GMT_LEN256] = {""}, ISO[GMT_LEN8] = {""}; double west, east, south, north, xscl, yscl, out[2], *lon = NULL, *lat = NULL; struct GMT_RANGE *Z = NULL; struct GMT_DATASET *D = NULL; @@ -484,6 +526,9 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL for (j = 0; j < F->n_items; j++) { pos = 0; while (gmt_strtok (F->item[j]->codes, ",", &pos, code)) { /* Loop over items */ + if (gmtdcw_got_continent (code, GMT_DCW_continents, GMT_DCW_contcodes, GMT_DCW_N_CONTINENTS, ISO)) /* Got continent name, replace with =?? code to be parsed below */ + strcpy (code, ISO); + if (code[0] == '=') { /* Must expand a continent into all member countries */ for (k = 0; k < GMT_DCW_COUNTRIES; k++) { if (strncmp (GMT_DCW_country[k].continent, &code[1], 2)) continue; /* Not this one */ @@ -520,6 +565,8 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL named_wesn++; } else { /* Just append this single one */ + if (gmtdcw_got_name (code, GMT_DCW_country, GMT_DCW_COUNTRIES, GMT_DCW_state, GMT_DCW_STATES, ISO)) /* Check if we got the name of a country or state, then replace with ISO code */ + strcpy (code, ISO); if (n_items) strcat (list, ","); strcat (list, code); order[n_items] = j; /* So we know which color/pen to apply for this item */ @@ -1013,11 +1060,11 @@ void gmt_DCW_option (struct GMTAPI_CTRL *API, char option, unsigned int plot) { GMT_Usage (API, 1, "\n-%c%s", option, DCW_OPT); GMT_Usage (API, -2, "%s for specified list of countries. " "Based on closed polygons from the Digital Chart of the World (DCW). " - "Append comma-separated list of ISO 3166 codes for countries to %s, i.e., " + "Append comma-separated list of ISO 3166 codes (or full names) for countries to %s, i.e., " ",,... etc., using the 2-character country codes. " - "To select a state of a country (if available), append .state, e.g, US.TX for Texas. " - "To select a whole continent, use =AF|AN|AS|EU|OC|NA|SA as . For collections and named regions, " - "append their codes or full name. Some modifiers:", usage[plot], action[plot]); + "To select a state of a country (if available), append .state, e.g, US.TX for Texas, or just the state name. " + "To select a whole continent, use =AF|AN|AS|EU|OC|NA|SA as or give full name. For collections and named regions, " + "append their codes or full name. All names are case-insensitive. Available modifiers:", usage[plot], action[plot]); if (plot == 1) { GMT_Usage (API, 3, "+c Set clip paths for the inside area [none]."); GMT_Usage (API, 3, "+C Set clip paths for the outside area [none]."); From e6617c2c2ea5f3475d61955c1cd1b5130ea6ebeb Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 1 Dec 2021 13:14:05 -1000 Subject: [PATCH 09/15] Update explain_-R.rst_ --- doc/rst/source/explain_-R.rst_ | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/rst/source/explain_-R.rst_ b/doc/rst/source/explain_-R.rst_ index c8e9af3c59f..49f9e4d5285 100644 --- a/doc/rst/source/explain_-R.rst_ +++ b/doc/rst/source/explain_-R.rst_ @@ -34,13 +34,15 @@ The **-R** option defines the map region or data domain of interest. It may be s the nature of the calling module, this mechanism will also set grid spacing and possibly the grid registration (see :ref:`cookbook/options:Grid registration: The **-r** option`\ ). -#. **-R**\ *code1,code2,...*\ [**+e**\ \|\ **r**\ \|\ **R**\ *incs*]. This indirectly supplies the region by consulting - the DCW (Digital Chart of the World) database and derives the bounding regions for one or more countries given by - the codes. Simply append one or more comma-separated countries using the two-character - `ISO 3166-1 alpha-2 convention `_. - To select a state within a country (if available), append .state, e.g, US.TX for Texas. To specify a whole continent, - prepend **=** to any of the continent codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), - **OC** (Oceania), **NA** (North America), or **SA** (South America). The following modifiers can be appended: +#. **-R**\ *code1,code2,...*\ [**+e**\ \|\ **r**\ \|\ **R**\ *incs*]. This indirectly supplies the region by + consulting the DCW (Digital Chart of the World) database and derives the bounding regions for one or more + countries given by the codes. Simply append one or more comma-separated countries using either the two-character + `ISO 3166-1 alpha-2 convention `_ or the full country name. + To select a state within a country (if available), append .state, e.g, US.TX for Texas, or alternatively just + specify the full state name (e.g., Texas). To specify a whole continent, prepend **=** to any of the continent + codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), **NA** (North America), + or **SA** (South America), or spell out the full continent name. Finally, append any collection abbreviations or full + names for the extent of the collection or named region. All codes are case-insensitive. The following modifiers can be appended: - **+r** to adjust the region boundaries to be multiples of the steps indicated by *inc*, *xinc*/*yinc*, or *winc*/*einc*/*sinc*/*ninc* [default is no adjustment]. For example, **-R**\ *FR*\ **+r**\ 1 will select the From 5906ce33baa4dde531b330e6392fc349d5516beb Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 1 Dec 2021 13:29:48 -1000 Subject: [PATCH 10/15] update docs --- doc/rst/source/explain_-R.rst_ | 13 +++++++------ doc/rst/source/explain_-Rgeo.rst_ | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/doc/rst/source/explain_-R.rst_ b/doc/rst/source/explain_-R.rst_ index 49f9e4d5285..9ac1a744451 100644 --- a/doc/rst/source/explain_-R.rst_ +++ b/doc/rst/source/explain_-R.rst_ @@ -37,12 +37,13 @@ The **-R** option defines the map region or data domain of interest. It may be s #. **-R**\ *code1,code2,...*\ [**+e**\ \|\ **r**\ \|\ **R**\ *incs*]. This indirectly supplies the region by consulting the DCW (Digital Chart of the World) database and derives the bounding regions for one or more countries given by the codes. Simply append one or more comma-separated countries using either the two-character - `ISO 3166-1 alpha-2 convention `_ or the full country name. - To select a state within a country (if available), append .state, e.g, US.TX for Texas, or alternatively just - specify the full state name (e.g., Texas). To specify a whole continent, prepend **=** to any of the continent - codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), **NA** (North America), - or **SA** (South America), or spell out the full continent name. Finally, append any collection abbreviations or full - names for the extent of the collection or named region. All codes are case-insensitive. The following modifiers can be appended: + `ISO 3166-1 alpha-2 convention `_ (e.g., NO) or the full + country name (e.g., Norway). To select a state within a country (if available), append .state (e.g, US.TX), + or the full state name (e.g., Texas). To specify a whole continent, prepend **=** to any of the continent codes + **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), **NA** (North America), + or **SA** (South America), or spell out the full continent name. Finally, append any collection abbreviations + or full names for the extent of the collection or named region. All names are case-insensitive. + The following modifiers can be appended: - **+r** to adjust the region boundaries to be multiples of the steps indicated by *inc*, *xinc*/*yinc*, or *winc*/*einc*/*sinc*/*ninc* [default is no adjustment]. For example, **-R**\ *FR*\ **+r**\ 1 will select the diff --git a/doc/rst/source/explain_-Rgeo.rst_ b/doc/rst/source/explain_-Rgeo.rst_ index e267efa0a40..5b3153fbcd1 100644 --- a/doc/rst/source/explain_-Rgeo.rst_ +++ b/doc/rst/source/explain_-Rgeo.rst_ @@ -22,12 +22,13 @@ #. **-R**\ *code1,code2,...*\ [**+e**\ \|\ **r**\ \|\ **R**\ *incs*]. This indirectly supplies the region by consulting the DCW (Digital Chart of the World) database and derives the bounding regions for one or more countries given by the codes. Simply append one or more comma-separated countries using either the two-character - `ISO 3166-1 alpha-2 convention `_ or the full country name. - To select a state within a country (if available), append .state, e.g, US.TX for Texas, or alternatively just - specify the full state name (e.g., Texas). To specify a whole continent, prepend **=** to any of the continent - codes **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), **NA** (North America), - or **SA** (South America), or spell out the full continent name. Finally, append any collection abbreviations or full - names for the extent of the collection or named region. All codes are case-insensitive. The following modifiers can be appended: + `ISO 3166-1 alpha-2 convention `_ (e.g., NO) or the full + country name (e.g., Norway). To select a state within a country (if available), append .state (e.g, US.TX), + or the full state name (e.g., Texas). To specify a whole continent, prepend **=** to any of the continent codes + **AF** (Africa), **AN** (Antarctica), **AS** (Asia), **EU** (Europe), **OC** (Oceania), **NA** (North America), + or **SA** (South America), or spell out the full continent name. Finally, append any collection abbreviations + or full names for the extent of the collection or named region. All names are case-insensitive. + The following modifiers can be appended: - **+r** to adjust the region boundaries to be multiples of the steps indicated by *inc*, *xinc*/*yinc*, or *winc*/*einc*/*sinc*/*ninc* [default is no adjustment]. For example, **-R**\ *FR*\ **+r**\ 1 will select the From 4fc13644d2f9d2fefe1b1c1b1440a8489838c72e Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 1 Dec 2021 17:33:46 -1000 Subject: [PATCH 11/15] More space for long coordinates --- src/gmt_dcw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 046d311a1d4..e4dc521d602 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -162,7 +162,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** unsigned int j, k, ns, collection, n_errors; int nf; char path[PATH_MAX] = {""}, line[BUFSIZ] = {""}; - char w[GMT_LEN16] = {""}, e[GMT_LEN16] = {""}, s[GMT_LEN16] = {""}, n[GMT_LEN16] = {""}; + char w[GMT_LEN32] = {""}, e[GMT_LEN32] = {""}, s[GMT_LEN32] = {""}, n[GMT_LEN32] = {""}; FILE *fp = NULL; struct GMT_DCW_COUNTRY *Country = NULL; struct GMT_DCW_STATE *State = NULL; From bb3150553a89a58d1db8ab3e901772b0954176e0 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 4 Dec 2021 16:34:17 -1000 Subject: [PATCH 12/15] Fix reallocation issue --- src/gmt_dcw.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index e4dc521d602..fd7e2191a17 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -158,7 +158,7 @@ GMT_LOCAL void gmtdcw_freestrings (struct GMT_DCW_COLLECTION *Collection, unsign GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY **C, struct GMT_DCW_STATE **S, struct GMT_DCW_COUNTRY_STATE **CS, struct GMT_DCW_COLLECTION **U, unsigned int dim[]) { /* Open and read list of countries and states and return via two struct and one char arrays plus dimensions in dim */ - size_t n_alloc = 300; + size_t n_alloc = GMT_LEN256; unsigned int j, k, ns, collection, n_errors; int nf; char path[PATH_MAX] = {""}, line[BUFSIZ] = {""}; @@ -183,7 +183,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** sscanf (line, "%s %s %[^\n]", Country[k].continent, Country[k].code, Country[k].name); k++; if (k == n_alloc) { - n_alloc += 100; + n_alloc += GMT_LEN128; Country = gmt_M_memory (GMT, Country, n_alloc, struct GMT_DCW_COUNTRY); } } @@ -209,7 +209,7 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** if (k && strcmp (State[k].country, State[k-1].country)) ns++; /* New country with states */ k++; if (k == n_alloc) { - n_alloc += 100; + n_alloc += GMT_LEN128; State = gmt_M_memory (GMT, State, n_alloc, struct GMT_DCW_STATE); } } @@ -231,7 +231,6 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** /* Get collections (which may not be present, so allow for missing files */ - n_alloc = 100; /* Reset to a smaller size */ Collection = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_DCW_COLLECTION); k = 0; for (collection = 0; collection < 2; collection++) { /* Read both system and user collections */ @@ -302,8 +301,11 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** k++; dim[3+collection]++; if (k == n_alloc) { - n_alloc += 100; + size_t old_n_alloc = n_alloc; + n_alloc += GMT_LEN128; Collection = gmt_M_memory (GMT, Collection, n_alloc, struct GMT_DCW_COLLECTION); + /* Reallocation does not initialize new memory to NULL so we must do so here manually */ + gmt_M_memset (&(Collection[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_DCW_COLLECTION); /* Set to NULL/0 */ } } fclose (fp); @@ -475,7 +477,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL */ int item, ks, retval, ncid, xvarid, yvarid, id; int64_t first, last; - size_t np, max_np = 0U, n_alloc = 300; + size_t np, max_np = 0U, n_alloc = GMT_LEN128; uint64_t k, seg, n_segments; unsigned int n_items = 0, r_item = 0, pos = 0, kk, tbl = 0, j = 0, named_wesn = 0, *order = NULL; unsigned short int *dx = NULL, *dy = NULL; @@ -537,7 +539,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL order[n_items] = j; /* So we know which color/pen to apply for this item */ n_items++; if (n_items == n_alloc) { - n_alloc += 100; + n_alloc += GMT_LEN128; order = gmt_M_memory (GMT, order, n_alloc, unsigned int); } } @@ -555,7 +557,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL for (k = 0; k < n_list; k++) { order[n_items++] = j; /* So we know which color/pen to apply for all items in this collection */ if (n_items == n_alloc) { - n_alloc += 100; + n_alloc += GMT_LEN128; order = gmt_M_memory (GMT, order, n_alloc, unsigned int); } } @@ -572,7 +574,7 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL order[n_items] = j; /* So we know which color/pen to apply for this item */ n_items++; if (n_items == n_alloc) { - n_alloc += 100; + n_alloc += GMT_LEN128; order = gmt_M_memory (GMT, order, n_alloc, unsigned int); } } From b63950fd065b56a87aeb0fb6a5cc8a2bfd2dfd69 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 12 Jan 2022 13:26:30 -1000 Subject: [PATCH 13/15] Need longer strings now --- src/gmt_dcw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index a66f4f296a2..40f38aa0a8e 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -952,7 +952,7 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { /* Write to stdout the available countries [and optionally states], then make calling program exit */ unsigned int list_mode, i, j, k, kk, GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, GMT_DCW_N_COUNTRIES_WITH_STATES = 0, n_bodies[5] = {0, 0, 0, 0, 0}; bool search = false; - char string[GMT_LEN128] = {""}; + char string[GMT_LEN512] = {""}; struct GMT_DCW_COUNTRY *GMT_DCW_country = NULL; struct GMT_DCW_STATE *GMT_DCW_state = NULL; struct GMT_DCW_COUNTRY_STATE *GMT_DCW_country_with_state = NULL; From 0652b2a7c405b093ea84368acf16bd4c3405b3d0 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 12 Jan 2022 18:09:39 -1000 Subject: [PATCH 14/15] Update gmt_dcw.c --- src/gmt_dcw.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 40f38aa0a8e..3e977637645 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -86,7 +86,6 @@ struct GMT_DCW_CHINA_CODES { /* Compile in read-only structures and arrays with the information */ static char *GMT_DCW_continents[GMT_DCW_N_CONTINENTS] = {"Africa", "Antarctica", "Asia", "Europe", "Oceania", "North America", "South America", "Miscellaneous"}; -static char *GMT_DCW_contcodes[GMT_DCW_N_CONTINENTS] = {"AF", "AN", "AS", "EU", "OC", "NA", "SA", ""}; /* Local functions only visible inside this file */ @@ -315,8 +314,6 @@ GMT_LOCAL int gmtdcw_load_lists (struct GMT_CTRL *GMT, struct GMT_DCW_COUNTRY ** else Collection = gmt_M_memory (GMT, Collection, k, struct GMT_DCW_COLLECTION); -all_done: - *C = Country; *S = State; *U = Collection; @@ -359,7 +356,7 @@ GMT_LOCAL int gmtdcw_find_country (char *code, struct GMT_DCW_COUNTRY *list, int GMT_LOCAL bool gmtdcw_got_name (char *name, struct GMT_DCW_COUNTRY *clist, int nc, struct GMT_DCW_STATE *slist, int ns, char *ISO) { /* Slow linear search to find ISO that matches the given country name */ - unsigned int k; + int k; for (k = 0; k < nc; k++) { if (strcasecmp (name, clist[k].name) == 0) { strncpy (ISO, clist[k].code, 2); ISO[2] = '\0'; /* Terminate string */ @@ -376,19 +373,6 @@ GMT_LOCAL bool gmtdcw_got_name (char *name, struct GMT_DCW_COUNTRY *clist, int n return (false); /* Not found */ } -GMT_LOCAL bool gmtdcw_got_continent (char *name, char *continents[], char *contcodes[], int nc, char *ISO) { - /* Slow linear search to find ISO that matches the given country name */ - unsigned int k; - if (strlen (name) < 4) return (false); /* Cannot be a continent name since Asia is the shortest */ - for (k = 0; k < nc; k++) { - if (strcasecmp (name, continents[k]) == 0) { - sprintf (ISO, "=%s", contcodes[k]); ISO[3] = '\0'; /* Terminate string */ - return (true); - } - } - return (false); -} - GMT_LOCAL int gmtdcw_find_state (struct GMT_CTRL *GMT, char *scode, char *ccode, struct GMT_DCW_STATE *slist, int ns, bool check) { /* Return state id given country and state codes using a linear search */ int i; @@ -429,15 +413,6 @@ GMT_LOCAL int gmtdcw_found_collection (char *code, struct GMT_DCW_COLLECTION *GM return (GMT_NOTSET); /* Not found */ } -GMT_LOCAL bool gmtdcw_not_ISO (char *code) { - /* Return true if not XX or XX.YY */ - unsigned int L = strlen (code); - if (L > 5) return true; /* Cannot be ISO code */ - if (L == 2) return false; /* Must be ISO code */ - if (L != 5) return true; /* Unless it is XX.YY it is not ISO */ - return (code[2] != '.'); /* True if not XX.YY */ -} - /*----------------------------------------------------------| * Public functions that are part of the GMT Devel library | *----------------------------------------------------------| @@ -528,8 +503,6 @@ struct GMT_DATASET * gmt_DCW_operation (struct GMT_CTRL *GMT, struct GMT_DCW_SEL for (j = 0; j < F->n_items; j++) { pos = 0; while (gmt_strtok (F->item[j]->codes, ",", &pos, code)) { /* Loop over items */ - if (gmtdcw_got_continent (code, GMT_DCW_continents, GMT_DCW_contcodes, GMT_DCW_N_CONTINENTS, ISO)) /* Got continent name, replace with =?? code to be parsed below */ - strcpy (code, ISO); if (code[0] == '=') { /* Must expand a continent into all member countries */ for (k = 0; k < GMT_DCW_COUNTRIES; k++) { @@ -989,7 +962,7 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { Out = gmt_new_record (GMT, NULL, string); /* Since we only need to worry about text in this module */ if (list_mode & DCW_GET_COLLECTIONS) { - int k, nc = n_bodies[3] + n_bodies[4]; /* Total number of collections */ + unsigned int nc = n_bodies[3] + n_bodies[4]; /* Total number of collections */ gmt_set_tableheader (GMT, GMT_OUT, true); sprintf (string, "TAG\tName\t\tCodes|Region"); GMT_Put_Record (API, GMT_WRITE_TABLE_HEADER, string); From 5d3ab57a9e440a71069c8a02847b69f9910bf4d0 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 14 Jan 2022 10:36:20 -1000 Subject: [PATCH 15/15] Update gmt_dcw.c --- src/gmt_dcw.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/gmt_dcw.c b/src/gmt_dcw.c index 3e977637645..590662c979b 100644 --- a/src/gmt_dcw.c +++ b/src/gmt_dcw.c @@ -925,7 +925,7 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { /* Write to stdout the available countries [and optionally states], then make calling program exit */ unsigned int list_mode, i, j, k, kk, GMT_DCW_COUNTRIES = 0, GMT_DCW_STATES = 0, GMT_DCW_N_COUNTRIES_WITH_STATES = 0, n_bodies[5] = {0, 0, 0, 0, 0}; bool search = false; - char string[GMT_LEN512] = {""}; + char string[GMT_LEN512] = {""}, *target = NULL, *region = NULL; struct GMT_DCW_COUNTRY *GMT_DCW_country = NULL; struct GMT_DCW_STATE *GMT_DCW_state = NULL; struct GMT_DCW_COUNTRY_STATE *GMT_DCW_country_with_state = NULL; @@ -967,19 +967,21 @@ unsigned int gmt_DCW_list (struct GMT_CTRL *GMT, struct GMT_DCW_SELECT *F) { sprintf (string, "TAG\tName\t\tCodes|Region"); GMT_Put_Record (API, GMT_WRITE_TABLE_HEADER, string); for (i = 0; i < nc; i++) { - if (GMT_DCW_collection[i].type == DCW_NAMED_LIST) { - if (search) { /* Listed collection(s) */ - bool found = false; - for (kk = 0; !found && kk < F->n_items; kk++) { - if (strstr (GMT_DCW_collection[i].list, F->item[kk]->codes)) - found = true; /* This item is part of this collection */ - } - if (!found) continue; + if (GMT_DCW_collection[i].type == DCW_NAMED_LIST) + target = region = GMT_DCW_collection[i].list; + else { + target = GMT_DCW_collection[i].name; + region = GMT_DCW_collection[i].wesn_string; + } + if (search) { /* Listed collection(s) */ + bool found = false; + for (kk = 0; !found && kk < F->n_items; kk++) { + if (strstr (target, F->item[kk]->codes)) + found = true; /* This item is part of this collection */ } - sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].region, GMT_DCW_collection[i].name, GMT_DCW_collection[i].list); + if (!found) continue; } - else /* Just a named region */ - sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].region, GMT_DCW_collection[i].name, GMT_DCW_collection[i].wesn_string); + sprintf (string, "%s\t%s\t%s", GMT_DCW_collection[i].region, GMT_DCW_collection[i].name, region); GMT_Put_Record (API, GMT_WRITE_DATA, Out); } }