/*-------------------------------------------------------------------- * $Id: gmtselect.c,v 1.5.4.4 2002/02/27 17:58:55 pwessel Exp $ * * Copyright (c) 1991-2002 by P. Wessel and W. H. F. Smith * See COPYING file for copying and redistribution conditions. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Contact info: gmt.soest.hawaii.edu *--------------------------------------------------------------------*/ /* * gmtselect is a general-purpose spatial filter. Data pass or fail based * on one or more conditions. 5 conditions may be set: * * 1. Only data inside a rectangular area may pass * 2. Only data within a certain distance from given points may pass * 3. Only data within a certain distance from given lines may pass * 4. Only data within given polygons may pass * 5. Only data within the coastline may pass * * Distances are calculated in the users units using Euclidian geometry * unless a map projection and region (-R -J) are used. Then, distances * are calculated using spherical geometry and converted to km, and any * distances given in options or via headers are assumed to be in km. * * Any one of these conditions may be negated for the opposite result * Both binary and ascii data files are accomodated * * Author: Paul Wessel * Date: 25-AUG-1998 * Version: 3.1 * 3.2, 15-MAR-1999 * 3.3, 13-APR-1999. Added quick check for inside a square before checking * for inside a circle (suggested by Joaquim Luis) * Also added option -Cf for faster, approximate distances * 3.3.2 8-SEPT-1999. Sorting -C points in x to speed up search as * suggested by Joaquim Luis. * 3.3.4 17-FEB-2000. Polygons containing either S or N pole will work correctly * 3.3.5 10-JUL-2000 Added plain -L for periodicity * Version: 3.4.1 */ #include "gmt.h" #define DEG_TO_KM (6371.0087714 * D2R) char *shore_resolution[5] = {"full", "high", "intermediate", "low", "crude"}; struct GMT_SHORE c; struct LINES { double *lon, *lat; double dist; int np; int polar; }; struct DATA { int i; double x; double y; double d; } *data; double dist_to_point (double lon, double lat, double *xp, double *yp, int np, int *id); double GMT_cartesian_dist (double x0, double y0, double x1, double y1); double GMT_flatearth_dist (double x0, double y0, double x1, double y1); double GMT_km_dist (double x0, double y0, double x1, double y1); int points_init (char *file, double **xp, double **yp, double **dp, double dist, BOOLEAN greenwich); int lines_init (char *file, struct LINES **p, double dist, BOOLEAN greenwich); int near_a_point_cart (double x, double y, double *xp, double *yp, double *dp, int np); int near_a_point (double x, double y, double *xp, double *yp, double *dp, int np); int near_a_line (double lon, double lat, struct LINES *p, int np); int compare_x(const void *point_1, const void *point_2); PFD distance_func; main (int argc, char **argv) { int i, j, k, fno, n_read = 0, n_pass = 0, n_files = 0, n_args, n_points, n_lines, n_pols; int n_fields, n_expected_fields, out_edge_in[5], ind, bin, last_bin = -1; int min_level = 0, max_level = MAX_LEVEL, np[2], base = 3, wd[2], id, this_node; int side, is_inside = 1, row, col; BOOLEAN error = FALSE, limit_line_distance = FALSE, limit_polygon = FALSE, limit_point_distance = FALSE, limit_point_distance_cart = FALSE; BOOLEAN want_inside[5] = {TRUE,TRUE,TRUE,TRUE,TRUE}, nofile = TRUE, done = FALSE, first = TRUE, inside, greenwich = FALSE; BOOLEAN flat_earth = FALSE, do_project = FALSE, binary = FALSE, limit_coast = FALSE, dry_wet_only; double west = 0.0, east = 0.0, south = 0.0, north = 0.0, xx, yy, min_area = 0.0; double *xp, *yp, *dp, pt_dist = 0.0, ln_dist = 0.0, *in, west_border, east_border; double xmin, xmax, ymin, ymax, lon, r, sa, ca; char buffer[BUFSIZ], *pt_file, *ln_file, *pol_file, *ptr, res = 'l'; FILE *fp = NULL; struct LINES *pol, *line; struct POL *p[2]; pt_file = ln_file = pol_file = CNULL; memset ((void *)out_edge_in, 0, (size_t)(5 * sizeof (int))); /* Default for "wet" areas = 0 (outside) */ out_edge_in[1] = out_edge_in[3] = 1; /* Default for "dry" areas = 1 (inside) */ argc = GMT_begin (argc, argv); for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch (argv[i][1]) { /* Common parameters */ case 'H': case 'J': case 'R': case 'V': case ':': case '\0': error += GMT_get_common_args (argv[i], &west, &east, &south, &north); break; /* Supplemental parameters */ case 'b': error += GMT_io_selection (&argv[i][2]); break; case 'A': j = sscanf (&argv[i][2], "%lf/%d/%d", &min_area, &min_level, &max_level); if (j == 1) min_level = 0, max_level = MAX_LEVEL; break; case 'C': limit_point_distance = TRUE; k = 2; if (argv[i][2] == 'f') flat_earth = TRUE, k = 3; for (j = k; argv[i][j] && argv[i][j] != '/'; j++); if (!argv[i][j]) { fprintf (stderr, "%s: GMT SYNTAX ERROR -C: Expects -C[f]dist/file\n", GMT_program); error++; } else { pt_file = &argv[i][j+1]; pt_dist = atof (&argv[i][k]); } break; case 'D': res = argv[i][2]; base = GMT_set_resolution (&res, 'D'); break; case 'L': if (argv[i][2]) { /* Set line options */ limit_line_distance = TRUE; for (j = 2; argv[i][j] && argv[i][j] != '/'; j++); if (!argv[i][j]) { fprintf (stderr, "%s: GMT SYNTAX ERROR -L: Expects -Ldist/file\n", GMT_program); error++; } else { ln_file = &argv[i][j+1]; ln_dist = atof (&argv[i][2]); } } else /* Flag to process geographic data */ GMT_geographic_in = GMT_geographic_out = TRUE; break; case 'F': limit_polygon = TRUE; pol_file = &argv[i][2]; break; case 'I': for (j = 2; argv[i][j]; j++) { switch (argv[i][j]) { case 'r': want_inside[0] = FALSE; break; case 'c': want_inside[1] = FALSE; break; case 'l': want_inside[2] = FALSE; break; case 'f': want_inside[3] = FALSE; break; case 's': want_inside[4] = FALSE; break; default: fprintf (stderr, "%s: GMT SYNTAX ERROR -I: Expects -Icflrs\n", GMT_program); error++; break; } } break; case 'M': /* Multiple line segments input */ GMT_multisegment (&argv[i][2]); break; case 'N': limit_coast = TRUE; strcpy (buffer, &argv[i][2]); if (buffer[strlen(buffer)-1] == 'o') { /* Edge is considered outside */ is_inside = 2; buffer[strlen(buffer)-1] = 0; } ptr = strtok (buffer, "/"); j = 0; while (j < 5 && ptr) { switch (ptr[0]) { case 's': /* Skip points in this level */ out_edge_in[j] = 0; break; case 'k': out_edge_in[j] = 1; break; default: fprintf (stderr, "%s: GMT SYNTAX ERROR -N option: Bad modifier (use s or k)\n", GMT_program); error++; } ptr = strtok (CNULL, "/"); j++; } if (!(j == 2 || j == 5)) { fprintf (stderr, "%s: GMT SYNTAX ERROR -N option: Specify 2 or 5 arguments\n", GMT_program); exit (EXIT_FAILURE); } dry_wet_only = (j == 2); break; default: error = TRUE; GMT_default_error (argv[i][1]); break; } } else n_files++; } if (argc == 1 || GMT_quick) { fprintf (stderr, "gmtselect %s - Select data subsets based on multiple spatial criteria\n\n", GMT_VERSION); fprintf (stderr, "usage: gmtselect [-A[//]] [-C[f]/] [-D]\n"); fprintf (stderr, "\t[-F] [-J] [-L[/]] [-H[]] [-I[cflrs]\n"); fprintf (stderr, "\t[-M[]] [-N[o]] [-R] [-V] [-:] [-bi[s][]] [-bo[s][]]\n\n"); if (GMT_quick) exit (EXIT_FAILURE); fprintf (stderr, "\tinfiles (in ASCII or binary) have 2 or more columns with (x,y) or (y,x) in first columns.\n"); fprintf (stderr, "\t If no file(s) is given, standard input is read.\n"); fprintf (stderr, "\n\tOPTIONS:\n"); fprintf (stderr, "\t-A coastline features smaller than (in km^2) or of levels (0-4) outside the min-max levels\n"); fprintf (stderr, "\t will be skipped [0/4] (see pscoast for details)]. Ignored unless -N is set\n"); fprintf (stderr, "\t-C pass locations that are within of any point in ASCII \n"); fprintf (stderr, "\t Give 0 distance if 3rd column of ptfile has individual distances.\n"); fprintf (stderr, "\t Distances are in users units [or km if -R -J are used].\n"); fprintf (stderr, "\t Prepend f to use approximate (flat earth) rather than exact great circle distances.\n"); fprintf (stderr, "\t-D Choose one of the following resolutions: (Ignored unless -N is set)\n"); fprintf (stderr, "\t f - full resolution (may be very slow for large regions)\n"); fprintf (stderr, "\t h - high resolution (may be slow for large regions)\n"); fprintf (stderr, "\t i - intermediate resolution\n"); fprintf (stderr, "\t l - low resolution [Default]\n"); fprintf (stderr, "\t c - crude resolution, for tasks that need crude continent outlines only\n"); fprintf (stderr, "\t-L with no arguments indicates that x is longitude and periodic in 360.\n"); fprintf (stderr, "\t With arguments, pass locations that are within of any line in ASCII \n"); fprintf (stderr, "\t Give 0 distance if 2nd column of segment headers have individual distances.\n"); fprintf (stderr, "\t Distances are in users units [or km if -R -J are used].\n"); fprintf (stderr, "\t-F pass locations that are inside the polygons in the ASCII file\n"); GMT_explain_option ('H'); fprintf (stderr, "\t-I Used to reverse the tests, i.e. pass locations outside the region\n"); fprintf (stderr, "\t Supply a combination of cflr where each flag means:\n"); fprintf (stderr, "\t c will pass locations beyond the minimum distance to the points in -C\n"); fprintf (stderr, "\t f will pass locations outside the polygons in -F\n"); fprintf (stderr, "\t l will pass locations beyond the minimum distance to the lines in -L\n"); fprintf (stderr, "\t r will pass locations outside the region given in -R [and -J]\n"); fprintf (stderr, "\t s will pass locations that otherwise would be skipped in -N\n"); GMT_explain_option ('J'); GMT_explain_option ('M'); fprintf (stderr, "\t-N set if a point outside or inside a geographic feature should be s(kipped) or k(ept) (requires -R).\n"); fprintf (stderr, "\t Append o to let feature boundary be considered outside [Default is inside].\n"); fprintf (stderr, "\t Specify this information with s or k using 1 of 2 formats:\n"); fprintf (stderr, "\t -N/.\n"); fprintf (stderr, "\t -N////.\n"); fprintf (stderr, "\t k means keep and s means skip [Default is s/k/s/k/s (i.e., s/k)]\n"); GMT_explain_option ('R'); GMT_explain_option ('V'); GMT_explain_option (':'); GMT_explain_option ('i'); GMT_explain_option ('n'); fprintf (stderr, "\t Default is 2 input columns\n"); GMT_explain_option ('o'); GMT_explain_option ('.'); exit (EXIT_FAILURE); } if (limit_coast && !project_info.region_supplied) { fprintf (stderr, "%s: GMT SYNTAX ERROR: Must specify -R option when -N is set\n", GMT_program); error++; } if (GMT_geographic_in && !project_info.region_supplied) { fprintf (stderr, "%s: GMT SYNTAX ERROR: Must specify -R option when plain -L is set\n", GMT_program); error++; } if (limit_coast && !MAPPING) { fprintf (stderr, "%s: GMT SYNTAX ERROR: Must specify a map projection when -N is set\n", GMT_program); error++; } if (GMT_io.binary[0] && gmtdefs.io_header) { fprintf (stderr, "%s: GMT SYNTAX ERROR. Binary input data cannot have header -H\n", GMT_program); error++; } if (GMT_io.binary[0] && GMT_io.ncol[0] == 0) GMT_io.ncol[0] = 2; if (GMT_io.binary[0] && GMT_io.ncol[0] < 2) { fprintf (stderr, "%s: GMT SYNTAX ERROR. Binary input data (-bi) must have at least 2 columns\n", GMT_program); error++; } if (error) exit (EXIT_FAILURE); GMT_put_history (argc, argv); /* Update .gmtcommands */ if (!MAPPING && limit_point_distance) limit_point_distance_cart = TRUE; if (GMT_io.binary[0] && gmtdefs.verbose) { char *type[2] = {"double", "single"}; fprintf (stderr, "%s: Expects %d-column %s-precision binary data\n", GMT_program, GMT_io.ncol[0], type[GMT_io.single_precision[0]]); } #ifdef SET_IO_MODE GMT_setmode (1); #endif if (project_info.region_supplied) { if (project_info.projection < 0) { /* Supply dummy linear proj */ project_info.projection = project_info.xyz_projection[0] = project_info.xyz_projection[1] = LINEAR; project_info.pars[0] = project_info.pars[1] = 1.0; } do_project = TRUE; if (MAPPING) { if (west < 0.0 && east < 0.0) { west += 360.0; east += 360.0; } greenwich = (west < 0.0 && east > 0.0); } GMT_map_setup (west, east, south, north); } if (limit_coast) { if (dry_wet_only) { out_edge_in[3] = out_edge_in[1]; out_edge_in[2] = out_edge_in[4] = out_edge_in[0]; } if (GMT_init_shore (res, &c, west, east, south, north)) { fprintf (stderr, "%s: %s resolution shoreline data base not installed\n", GMT_program, shore_resolution[base]); exit (EXIT_FAILURE); } west_border = floor (project_info.w / c.bsize) * c.bsize; east_border = ceil (project_info.e / c.bsize) * c.bsize; wd[0] = 1; wd[1] = -1; np[0] = np[1] = 0; } binary = (GMT_io.binary[0] || GMT_io.binary[1]); distance_func = (PFD) ((MAPPING) ? ((flat_earth) ? GMT_flatearth_dist : GMT_km_dist) : GMT_cartesian_dist); if (limit_point_distance) { n_points = points_init (pt_file, &xp, &yp, &dp, pt_dist, greenwich); /* Copy xp into struct data, sort, and copy back */ data = (struct DATA *) GMT_memory (VNULL, (size_t)n_points, sizeof(struct DATA), GMT_program); for (k = 0; k < n_points; k++) { data[k].i = k; data[k].x = xp[k]; data[k].y = yp[k]; data[k].d = dp[k]; } /* Sort on x */ qsort ((void *)data, (size_t)n_points, sizeof (struct DATA), compare_x); for (k = 0; k < n_points; k++) { /* Put back the new order */ xp[k] = data[k].x; yp[k] = data[k].y; dp[k] = data[k].d; } GMT_free ((void *)data); } if (limit_line_distance) n_lines = lines_init (ln_file, &line, ln_dist, greenwich); if (limit_polygon) { n_pols = lines_init (pol_file, &pol, -9999.0, greenwich); if (do_project) { /* Convert all the polygons */ for (i = 0; i < n_pols; i++) { if (pol[i].polar) { /* Polygon is a polar cap, use simple polar (r-theta) transformation */ for (j = 0; j < pol[i].np; j++) { r = 90.0 - fabs (pol[i].lat[j]); sincos (pol[i].lon[j] * D2R, &sa, &ca); pol[i].lon[j] = r * ca; pol[i].lat[j] = r * sa; } } else { for (j = 0; j < pol[i].np; j++) { GMT_geo_to_xy (pol[i].lon[j], pol[i].lat[j], &xx, &yy); pol[i].lon[j] = xx; pol[i].lat[j] = yy; } } } } } /* Now we are ready to take on some input values */ if (n_files > 0) nofile = FALSE; else n_files = 1; n_args = (argc > 1) ? argc : 2; for (fno = 1; !done && fno < n_args; fno++) { /* Loop over input files, if any */ if (!nofile && argv[fno][0] == '-') continue; if (nofile) { /* Just read standard input */ fp = GMT_stdin; done = TRUE; if (gmtdefs.verbose) fprintf (stderr, "%s: Reading from standard input\n", GMT_program); #ifdef SET_IO_MODE GMT_setmode (0); #endif } else if ((fp = GMT_fopen (argv[fno], GMT_io.r_mode)) == NULL) { fprintf (stderr, "%s: Cannot open file %s\n", GMT_program, argv[fno]); continue; } if (!nofile && gmtdefs.verbose) fprintf (stderr, "%s: Working on file %s\n", GMT_program, argv[fno]); if (gmtdefs.io_header) { for (i = 0; i < gmtdefs.n_header_recs; i++) { GMT_fgets (buffer, BUFSIZ, fp); if (first) fprintf (GMT_stdout, "%s", buffer); } first = FALSE; } n_expected_fields = (GMT_io.ncol[0]) ? GMT_io.ncol[0] : 2; while ((n_fields = GMT_input (fp, &n_expected_fields, &in)) >= 0 && ! (GMT_io.status & GMT_IO_EOF)) { /* Not yet EOF */ while (GMT_io.status & GMT_IO_SEGMENT_HEADER && !(GMT_io.status & GMT_IO_EOF)) { GMT_write_segmentheader (GMT_stdout, n_expected_fields); n_fields = GMT_input (fp, &n_expected_fields, &in); } if ((GMT_io.status & GMT_IO_EOF)) continue; /* At EOF */ if (GMT_io.status & GMT_IO_MISMATCH) { fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d\n", GMT_program, n_fields, n_expected_fields, n_read); exit (EXIT_FAILURE); } n_read++; lon = in[0]; if (project_info.region_supplied) { inside = !GMT_map_outside (lon, in[1]); if (inside != want_inside[0]) continue; } if (limit_point_distance_cart) { inside = near_a_point_cart (lon, in[1], xp, yp, dp, n_points); if (inside != want_inside[1]) continue; } else if (limit_point_distance) { inside = near_a_point (lon, in[1], xp, yp, dp, n_points); if (inside != want_inside[1]) continue; } if (limit_line_distance) { inside = near_a_line (lon, in[1], line, n_lines); if (inside != want_inside[2]) continue; } if (limit_polygon) { if (do_project) GMT_geo_to_xy (lon, in[1], &xx, &yy); else { xx = lon; yy = in[1]; } i = 0; inside = FALSE; while (i < n_pols && !inside) { if (pol[i].polar == -1 && in[1] <= 0.0) { /* Point on S hemisphere, check */ r = 90.0 + in[1]; sincos (lon * D2R, &sa, &ca); inside = (GMT_non_zero_winding (r * ca, r * sa, pol[i].lon, pol[i].lat, pol[i].np) >= is_inside); } else if (pol[i].polar == +1 && in[1] >= 0.0) { /* Point on N hemisphere, check */ r = 90.0 - in[1]; sincos (lon * D2R, &sa, &ca); inside = (GMT_non_zero_winding (r * ca, r * sa, pol[i].lon, pol[i].lat, pol[i].np) >= is_inside); } else inside = (GMT_non_zero_winding (xx, yy, pol[i].lon, pol[i].lat, pol[i].np) >= is_inside); i++; } if (inside != want_inside[3]) continue; } if (limit_coast) { xx = lon; while (xx < 0.0) xx += 360.0; row = ((int)floor ((90.0 - in[1]) / c.bsize)); if (row >= c.bin_ny) row = c.bin_ny - 1; /* Presumably only kicks in for south pole */ col = (int)floor (xx / c.bsize); bin = row * c.bin_nx + col; if (bin != last_bin) { /* Do this upon entering new bin */ ind = 0; while (ind < c.nb && c.bins[ind] != bin) ind++; /* Set ind to right bin */ if (ind == c.nb) continue; /* Bin not among the chosen ones */ last_bin = bin; GMT_get_shore_bin (ind, &c, min_area, min_level, max_level); /* Must use polygons. Go in both directions to cover both land and sea */ for (id = 0; id < 2; id++) { GMT_free_polygons (p[id], np[id]); if (np[id]) GMT_free ((void *)p[id]); np[id] = GMT_assemble_shore (&c, wd[id], min_level, TRUE, greenwich, west_border, east_border, &p[id]); np[id] = GMT_prep_polygons (&p[id], np[id], greenwich, FALSE, 0.0, -1); } } if (c.ns == 0) { /* No lines go through, check node level */ this_node = MIN (MIN (c.node_level[0], c.node_level[1]) , MIN (c.node_level[2], c.node_level[3])); } else { this_node = 0; GMT_geo_to_xy (lon, in[1], &xx, &yy); for (id = 0; id < 2; id++) { for (k = 0; k < np[id]; k++) { if (p[id][k].n == 0) continue; /* Find min/max of polygon */ xmin = xmax = p[id][k].lon[0]; ymin = ymax = p[id][k].lat[0]; for (i = 1; i < p[id][k].n; i++) { if (p[id][k].lon[i] < xmin) xmin = p[id][k].lon[i]; if (p[id][k].lon[i] > xmax) xmax = p[id][k].lon[i]; if (p[id][k].lat[i] < ymin) ymin = p[id][k].lat[i]; if (p[id][k].lat[i] > ymax) ymax = p[id][k].lat[i]; } if (yy < ymin || yy > ymax) continue; if (xx < xmin || xx > xmax) continue; /* Must compare with polygon */ if ((side = GMT_non_zero_winding (xx, yy, p[id][k].lon, p[id][k].lat, p[id][k].n)) < is_inside) continue; /* Outside polygon */ /* Here, point is inside, we must assign value */ if (p[id][k].level > this_node) this_node = p[id][k].level; } } } inside = out_edge_in[this_node]; if (inside != want_inside[4]) continue; } /* Here, we have passed all test and the point is output */ if (binary) GMT_output (GMT_stdout, n_expected_fields, in); else fprintf (GMT_stdout, "%s", GMT_io.current_record); n_pass++; if (gmtdefs.verbose && n_read%5000 == 0) fprintf (stderr, "%s: Read %d records\r", GMT_program, n_read); } if (fp != GMT_stdin) GMT_fclose (fp); } if (gmtdefs.verbose) fprintf (stderr, "%s: Read %d records, passed %d records\n", GMT_program, n_read, n_pass); if (limit_point_distance) { GMT_free ((void *)xp); GMT_free ((void *)yp); GMT_free ((void *)dp); } GMT_end (argc, argv); } int points_init (char *file, double **xp, double **yp, double **dp, double dist, BOOLEAN greenwich) { FILE *fp; double *x, *y, *d, *in; int i = 0, n_alloc = GMT_CHUNK, n_fields, n_expected_fields = BUFSIZ; x = (double *) GMT_memory (VNULL, (size_t)n_alloc, sizeof (double), GMT_program); y = (double *) GMT_memory (VNULL, (size_t)n_alloc, sizeof (double), GMT_program); d = (double *) GMT_memory (VNULL, (size_t)n_alloc, sizeof (double), GMT_program); if ((fp = GMT_fopen (file, "r")) == NULL) { fprintf (stderr, "%s: Cannot open file %s\n", GMT_program, file); exit (EXIT_FAILURE); } while ((n_fields = GMT_input_ascii (fp, &n_expected_fields, &in)) >= 0 && ! (GMT_io.status & GMT_IO_EOF)) { /* Not yet EOF */ while (GMT_io.status & GMT_IO_SEGMENT_HEADER && !(GMT_io.status & GMT_IO_EOF)) { n_fields = GMT_input_ascii (fp, &n_expected_fields, &in); } if ((GMT_io.status & GMT_IO_EOF)) continue; /* At EOF */ if (GMT_io.status & GMT_IO_MISMATCH) { fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d in file %s\n", GMT_program, n_fields, n_expected_fields, i, file); exit (EXIT_FAILURE); } if (n_expected_fields < 2) { fprintf (stderr, "%s: Failure to read file %s near line %d\n", GMT_program, file, i); exit (EXIT_FAILURE); } x[i] = in[0]; y[i] = in[1]; d[i] = (n_expected_fields >= 3 && dist == 0.0) ? in[2] : dist; if (MAPPING) { if (greenwich && x[i] > 180.0) x[i] -= 360.0; if (!greenwich && x[i] < 0.0) x[i] += 360.0; } i++; if (i == n_alloc) { n_alloc += GMT_CHUNK; x = (double *) GMT_memory ((void *)x, (size_t)n_alloc, sizeof (double), GMT_program); y = (double *) GMT_memory ((void *)y, (size_t)n_alloc, sizeof (double), GMT_program); d = (double *) GMT_memory ((void *)d, (size_t)n_alloc, sizeof (double), GMT_program); } } GMT_fclose (fp); if (i == 0) { fprintf (stderr, "%s: File %s is empty!\n", GMT_program, file); exit (EXIT_FAILURE); } x = (double *) GMT_memory ((void *)x, (size_t)i, sizeof (double), GMT_program); y = (double *) GMT_memory ((void *)y, (size_t)i, sizeof (double), GMT_program); d = (double *) GMT_memory ((void *)d, (size_t)i, sizeof (double), GMT_program); *xp = x; *yp = y; *dp = d; return (i); } int lines_init (char *file, struct LINES **p, double dist, BOOLEAN greenwich) { FILE *fp; struct LINES *e; int i = -1, j = 0, n, i_alloc = GMT_CHUNK, n_read = 0, j_alloc = GMT_CHUNK; int n_fields, n_expected_fields = BUFSIZ; BOOLEAN poly = FALSE, check_cap, save; double d, dlon, lon_sum, *in; if (fabs (dist + 9999.0) < GMT_CONV_LIMIT) poly = TRUE; /* A polygon */ check_cap = (poly && MAPPING); e = (struct LINES *) GMT_memory (VNULL, (size_t)i_alloc, sizeof (struct LINES), GMT_program); if ((fp = GMT_fopen (file, "r")) == NULL) { fprintf (stderr, "%s: Cannot open file %s\n", GMT_program, file); exit (EXIT_FAILURE); } save = GMT_io.multi_segments; /* Must set this to TRUE temporarily */ GMT_io.multi_segments = TRUE; n_fields = GMT_input_ascii (fp, &n_expected_fields, &in); if (GMT_io.status & GMT_IO_EOF) { fprintf (stderr, "%s: File %s is empty!\n", GMT_program, file); exit (EXIT_FAILURE); } if (!(GMT_io.status & GMT_IO_SEGMENT_HEADER)) { /* Quick check up front that it is a -M kind of file */ fprintf (stderr, "%s: Files for -F or -L must be in multisegment format!\n", GMT_program); exit (EXIT_FAILURE); } while (n_fields >= 0 && !(GMT_io.status & GMT_IO_EOF)) { /* Not yet EOF */ while (GMT_io.status & GMT_IO_SEGMENT_HEADER && !(GMT_io.status & GMT_IO_EOF)) { /* To use different line-distances for each segment, place the distance in the segment header */ i++; n_read++; n = sscanf (&GMT_io.segment_header[1], "%lg", &d); e[i].dist = (n == 1 && dist == 0.0) ? d : dist; j_alloc = GMT_CHUNK; j = 0; lon_sum = 0.0; n_fields = GMT_input_ascii (fp, &n_expected_fields, &in); } if ((GMT_io.status & GMT_IO_EOF)) continue; /* At EOF */ e[i].lon = (double *) GMT_memory (VNULL, (size_t)j_alloc, sizeof (double), GMT_program); e[i].lat = (double *) GMT_memory (VNULL, (size_t)j_alloc, sizeof (double), GMT_program); while (! (GMT_io.status & (GMT_IO_SEGMENT_HEADER | GMT_IO_EOF))) { /* Keep going until FALSE or = 2 segment header */ if (GMT_io.status & GMT_IO_MISMATCH) { fprintf (stderr, "%s: Mismatch between actual (%d) and expected (%d) fields near line %d\n", GMT_program, n_fields, n_expected_fields, i); exit (EXIT_FAILURE); } n_read++; if (n_expected_fields < 2) { fprintf (stderr, "%s: Failure to read file %s near line %d\n", GMT_program, file, n_read); exit (EXIT_FAILURE); } e[i].lon[j] = in[0]; e[i].lat[j] = in[1]; if (MAPPING) { if (greenwich && e[i].lon[j] > 180.0) e[i].lon[j] -= 360.0; if (!greenwich && e[i].lon[j] < 0.0) e[i].lon[j] += 360.0; } if (check_cap && j > 0) { /* Keep track of sum (dlon) */ dlon = e[i].lon[j] - e[i].lon[j-1]; if (fabs (dlon) > 180.0) dlon = copysign (360.0 - fabs (dlon), -dlon); lon_sum += dlon; } j++; if (j == (j_alloc-1)) { j_alloc += GMT_CHUNK; e[i].lon = (double *) GMT_memory ((void *)e[i].lon, (size_t)j_alloc, sizeof (double), GMT_program); e[i].lat = (double *) GMT_memory ((void *)e[i].lat, (size_t)j_alloc, sizeof (double), GMT_program); } n_fields = GMT_input_ascii (fp, &n_expected_fields, &in); } e[i].np = j; /* If dist = -9999.0 then file is a polygon and we must close it if needed */ if (poly && !(e[i].lon[0] == e[i].lon[j-1] && e[i].lat[0] == e[i].lat[j-1])) { e[i].lon[j] = e[i].lon[0]; e[i].lat[j] = e[i].lat[0]; e[i].np++; if (check_cap) { /* Keep track of sum (dlon) */ dlon = e[i].lon[j] - e[i].lon[j-1]; if (fabs (dlon) > 180.0) dlon = copysign (360.0 - fabs (dlon), -dlon); lon_sum += dlon; } } if (check_cap && fabs (fabs (lon_sum) - 360.0) < GMT_CONV_LIMIT) { /* Contains a pole */ e[i].polar = (e[i].lat[0] < 0.0) ? -1 : +1; /* S or N pole */ } if (i == (i_alloc-1)) { i_alloc += GMT_CHUNK; e = (struct LINES *) GMT_memory ((void *)e, (size_t)i_alloc, sizeof (struct LINES), GMT_program); } } GMT_fclose (fp); GMT_io.multi_segments = save; i++; e = (struct LINES *) GMT_memory ((void *)e, (size_t)i, sizeof (struct LINES), GMT_program); *p = e; return (i); } int near_a_line (double lon, double lat, struct LINES *p, int np) { int i = 0, j0, j1, inside = FALSE; double d_min, dx, xc, yc, a, a_inv, d; while (i < np && !inside) { /* Find nearest point on this line */ d_min = dist_to_point (lon, lat, p[i].lon, p[i].lat, p[i].np, &j0); if (j0 == 0) j1 = j0 + 1; else if (j0 == p[i].np-1) j1 = j0 - 1; else j1 = ((*distance_func) (lon, lat, p[i].lon[j0-1], p[i].lat[j0-1]) < (*distance_func) (lon, lat, p[i].lon[j0+1], p[i].lat[j0+1])) ? j0 - 1 : j0 + 1; dx = p[i].lon[j1] - p[i].lon[j0]; if (dx == 0.0) { xc = p[i].lon[j0]; yc = lat; } else { a = (p[i].lat[j1] - p[i].lat[j0]) / dx; a_inv = -1.0 / a; xc = (lat - p[i].lat[j0] + a_inv * lon - a * p[i].lon[j0]) / (a_inv - a); yc = lat - a_inv * (xc - lon); } if (xc >= MIN(p[i].lon[j0], p[i].lon[j1]) && xc <= MAX(p[i].lon[j0], p[i].lon[j1]) && yc >= MIN(p[i].lat[j0], p[i].lat[j1]) && yc <= MAX(p[i].lat[j0], p[i].lat[j1])) d = (*distance_func) (lon, lat, xc, yc); else d = d_min; inside = (d <= p[i].dist); i++; } return (inside); } double dist_to_point (double lon, double lat, double *xp, double *yp, int np, int *id) { int i; double d, d_min; d_min = 1.0e100; for (i = 0; i < np; i++) { d = (*distance_func) (lon, lat, xp[i], yp[i]); if (d < d_min) { d_min = d; *id = i; } } return (d_min); } int near_a_point (double x, double y, double *xp, double *yp, double *dp, int np) { int i = 0, inside = FALSE; double d; while (i < np && !inside) { d = (*distance_func) (x, y, xp[i], yp[i]); inside = (d <= dp[i]); i++; } return (inside); } int near_a_point_cart (double x, double y, double *xp, double *yp, double *dp, int np) { int i = 0, inside = FALSE; double d; if ((x < (xp[0] - dp[0])) || (x > (xp[np-1]) + dp[np-1])) return (inside); while (i < np && !inside) { if (fabs (x - xp[i]) <= dp[i]) if (fabs (y - yp[i]) <= dp[i]) { d = (*distance_func) (x, y, xp[i], yp[i]); inside = (d <= dp[i]); } i++; } return (inside); } double GMT_cartesian_dist (double x0, double y0, double x1, double y1) { /* Calculates the good-old straight line distance in users units */ return (hypot ( (x1 - x0), (y1 - y0))); } double GMT_flatearth_dist (double x0, double y0, double x1, double y1) { /* Calculates the approximate flat earth distance in km */ return (hypot ( (x1 - x0) * cosd (0.5 * (y1 + y0)), (y1 - y0)) * DEG_TO_KM); } double GMT_km_dist (double x0, double y0, double x1, double y1) { /* Calculates the grdat circle distance in km */ return (GMT_great_circle_dist (x0, y0, x1, y1) * DEG_TO_KM); } int compare_x (const void *point_1, const void *point_2) { struct DATA *p1, *p2; p1 = (struct DATA *)point_1; p2 = (struct DATA *)point_2; if (p1->x < p2->x) return (-1); else if (p1->x > p2->x) return (1); else return (0); }