/* * FILE: drawmap.c * * Function to generate PostScript instructions for drawing and shading land * areas for a given longitude-latitude range. * * PARAMETERS: * * mapfile - input file containing longitude-latitude coordinates of land * outlines fpps - pointer to PostScript file for output lon_range - * minimum & maximum longitude on x-axis lat_range - minimum & maximum * latitude on y-axis shading - gray level for shading (0.0 = black to 1.0 = * white) * */ #include #include #include #include "map.h" #define LonToIn(xx) (( (xx) - origin_d.x) / DegPerInch.x) #define LatToIn(yy) (( (yy) - origin_d.y) / DegPerInch.y) extern XY_TYPE DegPerInch; static XY_TYPE size_i, origin_d; #include "mapsub.c" void drawmap(char *mapfile, FILE * fpps, RANGE_TYPE lon_range, RANGE_TYPE lat_range, double shading) { LONLAT_TYPE current, previous, *last_in, first_point; LONLAT_TYPE tmp_current, tmp_previous; FILE *fpmap; int path_saved = 0; char shade_stroke[20]; double mid_lon, half_lon_span; RANGE_TYPE test_range; strcpy(shade_stroke, "stroke "); origin_d.x = lon_range.min; origin_d.y = lat_range.min; size_i.x = (lon_range.max - lon_range.min) / DegPerInch.x; size_i.y = (lat_range.max - lat_range.min) / DegPerInch.y; half_lon_span = 0.5 * (lon_range.max - lon_range.min); /* max. plotting range is 360 deg; so define test_range as 360 deg centered on the midpoint of the user's longitude range. This allows us to shift the input map file (0-360) to a range appropriate to the user's specifications. */ mid_lon = lon_range.min + half_lon_span; test_range.min = mid_lon - 180.0; test_range.max = mid_lon + 180.0; if (!strcmp(mapfile, "")) return; fputs("%---Map---\n", fpps); fputs("/shade {gsave setgray eofill grestore} bind def\n", fpps); fprintf(fpps, "gsave newpath %f %f M %f %f L %f %f L %f %f L clip newpath\n", 0.0, 0.0, size_i.x, 0.0, size_i.x, size_i.y, 0.0, size_i.y); if ((fpmap = fopen(mapfile, "r")) == NULL) { fprintf(stderr, "\n ERROR: Unable to open map file %s.\n", mapfile); return; } current.lon = current.lat = LAND_BOUNDARY; while (!feof(fpmap)) { previous.lon = current.lon; previous.lat = current.lat; if (fscanf(fpmap, " %lf %lf", ¤t.lon, ¤t.lat) != 2) break; /* the lon_range can specify any subset from -180 to 720; the map files are assumed to contain longitudes between 0 and 360; we need to check whether we need to shift the input longitudes: (note: be careful not to change the land & water boundary entries!) */ if (current.lon >= 0.0 && current.lon < test_range.min) current.lon += 360.0; if (current.lon > test_range.max) current.lon -= 360.0; if (in_range(current)) { if (in_range(previous)) { if (fabs(previous.lon - current.lon) < half_lon_span) /* continue existing path */ fprintf(fpps, "%9.4f %9.4f L\n", LonToIn(current.lon), LatToIn(current.lat)); else /* must be exiting lon-axis; map continues on opposite side */ { /* extend current path to axis being exited */ previous.lon = ExtendToAxis(previous.lon); fprintf(fpps, "%9.4f %9.4f L\n", LonToIn(previous.lon), LatToIn(previous.lat)); /* & start new path from reentry axis */ if (current.lat < -60) /* special case for Antarctica -- need * axes corners to close the polygon */ { fprintf(fpps, "%9.4f %9.4f L %9.4f %9.4f L %9.4f %9.4f L %9.4f %9.4f L\n", LonToIn(previous.lon), LatToIn(lat_range.min), LonToIn(ExtendToAxis(current.lon)), LatToIn(lat_range.min), LonToIn(ExtendToAxis(current.lon)), LatToIn(current.lat), LonToIn(current.lon), LatToIn(current.lat)); } else if (path_saved) /* resuming unfinished business */ { /* finish up the path on this side */ connect(fpps, previous, *pop(), lon_range, lat_range, 'M'); fputs(shade_stroke, fpps); fputs("grestore\n", fpps); /* resume path on opposite side */ current.lon = ExtendToAxis(current.lon); connect(fpps, *pop(), current, lon_range, lat_range, 'L'); path_saved--; } else /* save incomplete path & start new one on opposite side */ { if (push(previous)) return; fprintf(fpps, "%9.4f %9.4f L gsave newpath %9.4f %9.4f M %9.4f %9.4f L\n", LonToIn(previous.lon), LatToIn(previous.lat), LonToIn(ExtendToAxis(current.lon)), LatToIn(current.lat), LonToIn(current.lon), LatToIn(current.lat)); current.lon = ExtendToAxis(current.lon); if (push(current)) return; path_saved++; } } } else /* !in_range(previous), either new path or resume existing path */ { if (equal(previous.lon, LAND_BOUNDARY) || equal(previous.lon, WATER_BOUNDARY)) { fprintf(fpps, "%9.4f %9.4f M\n", LonToIn(current.lon), LatToIn(current.lat)); if (push(current)) return; /* remember first point */ } else if ((last_in = pop()) == NULL) /* new path */ { fprintf(fpps, "%9.4f %9.4f M\n", LonToIn(first_point.lon), LatToIn(first_point.lat)); connect(fpps, first_point, previous, lon_range, lat_range, 'L'); fprintf(fpps, "%9.4f %9.4f L\n", LonToIn(current.lon), LatToIn(current.lat)); if (push(first_point)) return; } else /* reentering range so resume existing path */ { connect(fpps, *last_in, previous, lon_range, lat_range, 'L'); fprintf(fpps, "%9.4f %9.4f L\n", LonToIn(current.lon), LatToIn(current.lat)); } } } else /* not in_range(current) -- either boundary or out-of-range */ { if (equal(current.lon, LAND_BOUNDARY)) { if (shading < 1.0) sprintf(shade_stroke, "%.3f shade stroke ", shading); if (in_range(previous)) /* close preceding land area properly */ { connect(fpps, previous, *pop(), lon_range, lat_range, 'L'); fputs(shade_stroke, fpps); } else /* either no path or exited range */ { if ((last_in = pop()) != NULL) /* exited range */ { connect(fpps, *last_in, previous, lon_range, lat_range, 'L'); /* connect last point to first point; this is usually identical for closed land areas, except those that cross 0 deg longitude */ connect(fpps, *last_in, *pop(), lon_range, lat_range, 'M'); fputs(shade_stroke, fpps); } } } else if (equal(current.lon, WATER_BOUNDARY)) { if (shading < 1.0) strcpy(shade_stroke, " 1.0 shade stroke "); if (in_range(previous)) { connect(fpps, previous, *pop(), lon_range, lat_range, 'L'); fprintf(fpps, " %f shade stroke\n", 1.0); /* white-out inland waters */ } else { if ((last_in = pop()) != NULL) { connect(fpps, *last_in, *pop(), lon_range, lat_range, 'L'); fprintf(fpps, " %f shade stroke\n", 1.0); } } } else /* out-of-range */ { if (in_range(previous)) /* exiting axis range */ { fprintf(fpps, "%9.4f %9.4f L\n", LonToIn(current.lon), LatToIn(current.lat)); if (push(current)) return; /* remember last point in */ } if (equal(previous.lon, LAND_BOUNDARY) || equal(previous.lon, WATER_BOUNDARY)) { first_point.lon = current.lon; first_point.lat = current.lat; } } } } /* end while !feof */ if (stack_index >= 0) /* unfinished outlines */ { do { if (in_range(current)) { tmp_current = *pop(); connect(fpps, current, tmp_current, lon_range, lat_range, 'L'); current = tmp_current; } else { tmp_previous = *pop(); tmp_current = *pop(); connect(fpps, tmp_previous, tmp_current, lon_range, lat_range, 'M'); current = tmp_current; } } while (stack_index > 0); fputs(shade_stroke, fpps); } fclose(fpmap); fputs("grestore\n", fpps); }