/* * FILE: vector.c * */ #include "common.h" #include "io_.h" #include "io_nc.h" /* get_quotation */ #include "mem_.h" /* move_byte */ #include #include "map.h" #include "ioserv.h" #include "vecplot.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define VSPACE 0.33 /* vertical whitespace allowance set to 1/3 of font size */ #define font_width(font_size) (font_size)/1.5 /* width is roughly 2/3 of height */ #define is_multiple(x,y) (fabs(frem((x),(y))) == 0) static NAME_LIST_ENTRY_TYPE SymbolList[] = { {"circle" , CIRCLE}, {"filled_circle" , CIRCLE | FILLED}, {"delta" , DELTA}, {"filled_delta" , DELTA | FILLED}, {"del" , DEL}, {"filled_del" , DEL | FILLED}, {"square" , SQUARE}, {"filled_square" , SQUARE | FILLED}, {"diamond" , DIAMOND}, {"filled_diamond" , DIAMOND | FILLED}, {"plus" , PLUS}, {"x" , XMARK}, {"none" , NONE}, {NULL , BADINT} }; FILE *FpPs = NULL; static LABEL_TYPE *CommonLabelsHead, *LocalLabelsHead; TITLE PlotTitle, MainTitle, SubTitle; static char TimeStamp[80], /* for plot time stamp, user-settable */ TodayDT[80], /* for PostScript Creation Date, from system clock */ PsFile[80], VecFiles[MAXNUMFILES][80], Annotation[80], Orientation[10]; static FONT TitleFont; static int IndexUV, PlotCruiseTrack, ShowPage, NewPage, NegativeLongitudes; static MARGIN PageMargin, PlotMargin; static XY_TYPE PageDim_i, PageSpace_i, /* page dimensions less page margins */ PlotDim_i, PlotSpace_i; /* maximum plot/subplot size from axis to axis */ int Bb[4]; /* bounding box coordinates */ XY_TYPE DegPerInch, /* plot scale in degrees per inch */ Origin_i; /* plot origin in inches */ static RANGE_INTERVAL LonRange, LatRange; static PLOT_ARRAY PlotArray; /* order of plots on the page (M x N) */ static LEGEND_TYPE Legend; static AXIS_TYPE Axis; static ARROW_TYPE Arrow; static struct { FILE_NAME_TYPE file; double gray_level, linewidth_p; } Map; int PageCount; static OPTION_TYPE LegendOptions[] = { {"end" , 0 , NULL , NULL}, {"label:" , 0 , read_quotation, Legend.text}, {"font:" , TYPE_STRING , op_get_param , Legend.font.face}, {"font_size:" , TYPE_DOUBLE , op_get_param , (char *) &Legend.font.size_p}, {"position:" , COORDINATE , read_xy , (char *) &Legend.pos}, {"length:" , TYPE_DOUBLE , op_get_param , (char *) &Legend.length}, {"type:" , TYPE_STRING , op_get_param , Legend.type}, {"linewidth:" , TYPE_DOUBLE , op_get_param , (char *) &Legend.linewidth_p}, {NULL , 0 , NULL , NULL} }; static OPTION_TYPE ArrowOptions[] = { {"end" , 0 , NULL , NULL}, {"headlength:" , TYPE_DOUBLE , op_get_param , (char *) &Arrow.headlength_i}, {"min_headlength:" , TYPE_DOUBLE , op_get_param , (char *) &Arrow.min_headlength_i}, {"linewidth:" , TYPE_DOUBLE , op_get_param , (char *) &Arrow.linewidth_p}, {"scale:" , TYPE_DOUBLE , op_get_param , (char *) &Arrow.cms_per_inch}, {NULL , 0 , NULL , NULL} }; static OPTION_TYPE AxisOptions[] = { {"end" , 0 , NULL , NULL}, {"font:" , TYPE_STRING , op_get_param , Axis.font.face}, {"font_size:" , TYPE_DOUBLE , op_get_param , (char *) &Axis.font.size_p}, {"linewidth:" , TYPE_DOUBLE , op_get_param , (char *) &Axis.linewidth_p}, {"decimal_place:" , COORDINATE , read_xy , (char *) &Axis.decimal_place}, {"label_interval:" , COORDINATE , read_xy , (char *) &Axis.label_interval}, {"origin:" , COORDINATE , read_xy , (char *) &Axis.origin_i}, {"scale:" , SCALE , read_xy , (char *) &Axis.deg_per_inch}, {NULL , 0 , NULL , NULL} }; static OPTION_TYPE MapOptions[] = { {"end" , 0 , NULL , NULL}, {"file:" , TYPE_STRING , op_get_param , Map.file}, {"shading:" , TYPE_DOUBLE , op_get_param , (char *) &Map.gray_level}, {"linewidth:" , TYPE_DOUBLE , op_get_param , (char *) &Map.linewidth_p}, {NULL , 0 , NULL , NULL} }; static OPTION_TYPE Options[] = { {"end" , 0 , NULL , NULL}, /*--- PLOT SCALE & PLACEMENT ---*/ {"new_page" , 0 , showpage , NULL}, {"subplot:" , 0 , subplot , NULL}, {"orientation:" , TYPE_STRING , op_get_param , (char *) Orientation}, {"top_margin:" , TYPE_DOUBLE , op_get_param , (char *) &PageMargin.top}, {"bottom_margin:" , TYPE_DOUBLE , op_get_param , (char *) &PageMargin.bottom}, {"left_margin:" , TYPE_DOUBLE , op_get_param , (char *) &PageMargin.left}, {"right_margin:" , TYPE_DOUBLE , op_get_param , (char *) &PageMargin.right}, /*--- VECTOR PLOTTING OPTIONS ---*/ {"vecfile:" , 1 , read_files , (char *) VecFiles}, {"vecfiles:" , 0 , read_files , (char *) VecFiles}, {"u_v_pair:" , TYPE_INT , op_get_param , (char *) &IndexUV}, {"cruise_track" , 1 , set_code , (char *) &PlotCruiseTrack}, {"arrow:" , ECHO , execute_options, (char *) ArrowOptions}, /*--- MAP PLOTTING OPTIONS ---*/ {"map:" , ECHO , execute_options, (char *) MapOptions}, /*--- AXES CONTROL ---*/ {"lon_range:" , LONGITUDE , read_range , (char *) &LonRange}, {"lat_range:" , LATITUDE , read_range , (char *) &LatRange}, {"axis:" , ECHO , execute_options, (char *) AxisOptions}, /*--- PLOT TITLES/ANNOTATIONS ---*/ {"main_title:" , MAIN_TITLE , read_quotation, MainTitle.text}, {"sub_title:" , SUBTITLE , read_quotation, SubTitle.text}, {"plot_title:" , PLOT_TITLE , read_quotation, PlotTitle.text}, {"title_font:" , TYPE_STRING , op_get_param , TitleFont.face}, {"title_font_size:" , TYPE_DOUBLE , op_get_param , (char *) &TitleFont.size_p}, {"label:" , 0 , read_label , NULL}, {"clear_labels" , 0 , clear_labels , (char *) &CommonLabelsHead}, {"legend:" , ECHO , execute_options, (char *) LegendOptions}, {"time_stamp:" , 0 , read_quotation, TimeStamp}, {"annotation:" , 0 , read_quotation, Annotation}, /*--- OUTPUT OPTIONS ---*/ {"psfile:" , TYPE_STRING , op_get_param , PsFile}, {"make_plot" , 0 , make_plot , NULL}, {"add_plot" , 1 , make_plot , NULL}, /*--- MISC. OPTIONS ---*/ {"skip_to:" , 0 , skip_to , NULL}, {NULL , 0 , NULL , NULL} }; int main(int argc, char *argv[]) { FILE *fpcon; fpcon = get_fpcnt(argc, argv); set_defaults(); while(execute_options(fpcon, Options, ECHO) > 0) ; if (FpPs != NULL) { showpage(FpPs, NULL, 0); /* Always closes FpPs. */ } return 0; } int make_plot(FILE * fpcon, char *dummy, int append_flag) { double cos_lat, fontspace; int rc; fflush(stdout); /* display all options for this plot */ if (PlotArray.current <= 0) return(-1); NegativeLongitudes = 0; if (LonRange.min < 0.0 || LonRange.max < 0.0) NegativeLongitudes = 1; if (LonRange.min > 180.0 || LonRange.max > 180.0) NegativeLongitudes = 0; /*----------------- determine plot size & placement -------------------*/ if (!stricmp(Orientation, "landscape")) { PageDim_i.x = 11.0; PageDim_i.y = 8.5; } else { PageDim_i.x = 8.5; PageDim_i.y = 11.0; } PageSpace_i.x = PageDim_i.x - PageMargin.left - PageMargin.right; PageSpace_i.y = PageDim_i.y - PageMargin.top - PageMargin.bottom; fontspace = 1.0 + VSPACE; /* adding some vertical white space to font */ if (strcmp(MainTitle.text, "")) PageSpace_i.y -= MainTitle.font.size_i * fontspace; if (strcmp(SubTitle.text, "")) PageSpace_i.y -= SubTitle.font.size_i * fontspace; Axis.font.size_i = Axis.font.size_p / DPI; if (NewPage) { PlotMargin.top = 0.25 + PlotTitle.font.size_i * fontspace; PlotMargin.bottom = Axis.font.size_i * fontspace; PlotMargin.left = (4 + (Axis.decimal_place.y >= 0 ? Axis.decimal_place.y : get_decimal_length(LatRange.step * Axis.label_interval.y))) * font_width(Axis.font.size_i); /* we won't bother to set PlotMargin.right based on a similar logic because the rightmost tick may or may not get a numeric label depending on LonRange.max and Axis.label_interval.x settings; besides, we do check if there is enough space for the label later in plot_axes. Instead, we'll leave it fixed at 0.25in. */ PlotMargin.right = 0.25; } PlotSpace_i.x = PageSpace_i.x / PlotArray.n - PlotMargin.left - PlotMargin.right; PlotSpace_i.y = PageSpace_i.y / PlotArray.m - PlotMargin.top - PlotMargin.bottom; if (LonRange.span == 0.0 || LatRange.span == 0.0) { fputs("\n Nothing to plot!\n", stderr); return (-1); } if (Axis.deg_per_inch.x < 0.0 || Axis.deg_per_inch.y < 0.0) /* autoscale = max. mercator */ { cos_lat = cos(M_PI * 0.5 * (LatRange.max + LatRange.min) / 180.0); /* 1) LonRange.span / DegPerInch.x <= PlotSpace_i.x; 2) LatRange.span / DegPerInch.y <= PlotSpace_i.y; 3) DegPerInch.y = DegPerInch.x * cos_lat; Solving above gives: */ DegPerInch.x = larger(LonRange.span / PlotSpace_i.x, LatRange.span / (PlotSpace_i.y * cos_lat)); DegPerInch.y = DegPerInch.x * cos_lat; } else { DegPerInch.x = Axis.deg_per_inch.x; DegPerInch.y = Axis.deg_per_inch.y; } PlotDim_i.x = LonRange.span / DegPerInch.x; /* in inches */ PlotDim_i.y = LatRange.span / DegPerInch.y; fprintf(stderr, "\n\n => plot size: %.2f (longitude) by %.2f (latitude) inches\n", PlotDim_i.x, PlotDim_i.y); /* if (PlotSpace_i.x - PlotDim_i.x < -0.000001 || PlotSpace_i.y - PlotDim_i.y < -0.000001) { fputs(" ERROR: Unable to fit plot on page.\n", stderr); fprintf(stderr, " Maximum plot size is %f\" x %f\" \n", PlotSpace_i.x, PlotSpace_i.y); fputs(" You can: 1) change the scale\n", stderr); fputs(" 2) reduce the range\n", stderr); if (stricmp(Orientation, "portrait")) fputs(" 3) specify portrait Orientation\n", stderr); return (-1); } */ if (Axis.origin_i.x == -1.0 && Axis.origin_i.y == -1.0) /* autocenter */ { Origin_i.x = PageMargin.left + (PlotArray.current - 1) % PlotArray.n * (PlotSpace_i.x + PlotMargin.left + PlotMargin.right) + PlotMargin.left + (PlotSpace_i.x - PlotDim_i.x) / 2.0; /* center within plot space */ Origin_i.y = PageMargin.bottom + (PlotArray.m - 1 - (PlotArray.current - 1) / PlotArray.n) * (PlotSpace_i.y + PlotMargin.top + PlotMargin.bottom) + PlotMargin.bottom + (PlotSpace_i.y - PlotDim_i.y) / 2.0; } else { Origin_i.x = Axis.origin_i.x; Origin_i.y = Axis.origin_i.y; } /*------------------- initialize postscript file ----------------------*/ if (PsFile[0]) { if (!append_flag || !EXISTS(PsFile)) { FpPs = fopen(PsFile, "w"); PageCount = append_flag = 0; /* just in case it's set but file d.n.e. */ } else { if (NewPage) { FpPs = fopen(PsFile, "r+"); /* * position to read num. of pages so far then to overwrite * '%%Trailer, ...' */ if (fseek(FpPs, -17L, 2) != 0 || fscanf(FpPs, "Pages: %d", &PageCount) != 1 || fseek(FpPs, -60L, 2) != 0) { /* above are offsets for text files where lines are terminated by 1 char only (LF); below are offsets for DOS text files with lines terminated by 2 chars (CRLF) */ if (fseek(FpPs, -19L, 2) != 0 || fscanf(FpPs, "Pages: %d", &PageCount) != 1 || fseek(FpPs, -64L, 2) != 0) { fprintf(stderr, "ERROR: Unable to read trailer in PostScript file %s.\n", PsFile); return (-1); } } } } if (FpPs == NULL) { fprintf(stderr, " ERROR: Unable to open postscript file %s.\n", PsFile); return (-1); } } else FpPs = stdout; fflush(stderr); if (!append_flag) initps(FpPs, MainTitle.text, TodayDT); /* write PostScript prologue */ if (NewPage) { ++PageCount; fprintf(FpPs, "%%%%Page: %d\n\n", PageCount); Bb[llx] = Bb[lly] = MAXINT; Bb[urx] = Bb[ury] = 0; NewPage = 0; } /*-------------- update bounding box -----------------*/ Bb[llx] = smaller(Bb[llx], DPI * (Origin_i.x - PlotMargin.left)); Bb[lly] = smaller(Bb[lly], DPI * (Origin_i.y - PlotMargin.bottom)); Bb[urx] = larger(Bb[urx], DPI * (Origin_i.x + PlotDim_i.x + PlotMargin.right)); Bb[ury] = larger(Bb[ury], DPI * (Origin_i.y + PlotDim_i.y + PlotMargin.top)); fputs("%%BeginObject:\n", FpPs); /* Save the original state; restore it at the end of the object. */ fputs("gsave\n", FpPs); /* landscape = CW rotation, then drop origin down 8.5 inches (612 pts) */ if (!stricmp(Orientation, "landscape")) fputs("90 rotate 0 -612 translate\n", FpPs); /* plotting scale is reset to measure in inches */ fprintf(FpPs, "%d %d scale\n", DPI, DPI); fprintf(FpPs, "%f %f translate\n", Origin_i.x, Origin_i.y); if (Map.file[0]) { fprintf(FpPs, "%f setlinewidth\n", Map.linewidth_p / DPI); drawmap(Map.file, FpPs, *((RANGE_TYPE *) &LonRange), *((RANGE_TYPE *) &LatRange), Map.gray_level); } if (strcmp(PlotTitle.text, "")) { fprintf(FpPs, "%f /%s Fsi\n", PlotTitle.font.size_i, PlotTitle.font.face); fprintf(FpPs, " (%s) %f %f SC\n\n", PlotTitle.text, PlotDim_i.x / 2.0, PlotDim_i.y + VSPACE * PlotTitle.font.size_i); } plot_axes(); if (IndexUV > 0 && Legend.text[0] && !strcmp(Legend.type, "local")) plot_legend(); rc = plot_vectors(); if (rc) return (-1); plot_labels(CommonLabelsHead); plot_labels(LocalLabelsHead); clear_labels(FpPs, &LocalLabelsHead, 0); fputs("grestore\n", FpPs);/* complete reset to original */ if (ShowPage) showpage(FpPs, NULL, 0); fputs("\n Plot finished.\n", stderr); return (0); } int showpage(FILE * fp, char *arg, int code) { double title_y; if (FpPs == NULL) return(-1); fputs("gsave\n", FpPs); /* landscape = CW rotation, then drop origin down 8.5 inches (612 pts) */ if (!stricmp(Orientation, "landscape")) fputs("90 rotate 0 -612 translate\n", FpPs); /* plotting scale is reset to measure in inches */ fprintf(FpPs, "%d %d scale\n", DPI, DPI); /* time stamp is set in 0.25in (right-aligned), 0.35in and * annotation is set in 0,25in (right-aligned), 0.50in * from the upper right corner of the page */ if (strcmp(TimeStamp, "")) { fprintf(FpPs, "%f /Times-Italic Fsi\n", 10.0 / DPI); fprintf(FpPs, "(%s) %f %f SR\n", TimeStamp, (PageDim_i.x - 0.25), (PageDim_i.y - 0.35)); } if (strcmp(Annotation, "")) { if (!strcmp(TimeStamp, "")) fprintf(FpPs, "%f /Times-Italic Fsi\n", 10.0 / DPI); fprintf(FpPs, "(%s) %f %f SR\n", Annotation, (PageDim_i.x - 0.25), (PageDim_i.y - 0.50)); } title_y = 1.0 * Bb[ury] / DPI; /* Bb[ury] is highest plot's top axis + PlotMargin.top, in points */ if (SubTitle.text[0]) { print_heading(&SubTitle, title_y); title_y += SubTitle.font.size_i * (1 + VSPACE); } if (MainTitle.text[0]) print_heading(&MainTitle, title_y); fprintf(FpPs, "%f setlinewidth\n", Axis.linewidth_p / DPI); if (IndexUV > 0 && Legend.text[0] && !stricmp(Legend.type, "common")) plot_legend(); fputs("grestore\n", FpPs); fprintf(FpPs, "showpage\n%%%%PageBoundingBox: %d %d %d %d\n", Bb[llx], Bb[lly], Bb[urx], Bb[ury]); fprintf(FpPs, "%%%%Trailer\n%%%%BoundingBox: %3d %3d %3d %3d\n%%%%Pages: %3d\n%%%%EOF\n", Bb[llx], Bb[lly], Bb[urx], Bb[ury], PageCount); fclose(FpPs); FpPs = NULL; NewPage = 1; return 0; } int plot_vectors(void) { FILE *fpvec; LONLAT_TYPE ll_i[MAXLONLAT], /* inches */ ll_d; /* degrees */ double angle, speed, u, v; /* m/s */ char buff[255]; int i, npt; int ifile; if (IndexUV > 0) { if (Arrow.min_headlength_i < 0.0) Arrow.min_headlength_i = 0.5 * Arrow.headlength_i; fputs("%---Vectors---\n", FpPs); fprintf(FpPs, "%f setlinewidth\n", Arrow.linewidth_p / DPI); fprintf(FpPs, "/headlength %f def\n", Arrow.headlength_i); fprintf(FpPs, "/halfheadthickness %f def\n", 0.25 * Arrow.headlength_i * 1.67 * Arrow.linewidth_p); fprintf(FpPs, "/arrowhead_min %f def\n", Arrow.min_headlength_i); } for (ifile = 0; stricmp(VecFiles[ifile], "end"); ifile++) { if ((fpvec = fopen(VecFiles[ifile], "r")) == NULL) { fprintf(stderr, "ERROR: Unable to open vector file %s\n.", VecFiles[ifile]); return (-1); } npt = 0; while (fscanf(fpvec, " %lf %lf", &(ll_d.lon), &(ll_d.lat)) == 2) { /* * the first pair of columns is position (lon-lat in degrees), * followed by column pairs of zonal & meridional velocities in cm/s */ if (ll_d.lon < LonRange.min) ll_d.lon += 360.0; if (ll_d.lon > LonRange.max) ll_d.lon -= 360.0; if (ll_d.lon >= LonRange.min && ll_d.lon <= LonRange.max && ll_d.lat >= LatRange.min && ll_d.lat <= LatRange.max) { ll_i[npt].lon = LonToIn(ll_d.lon); ll_i[npt].lat = LatToIn(ll_d.lat); if (IndexUV > 0) { for (i = 0; i < IndexUV - 1; i++) fscanf(fpvec, "%*f %*f"); /* skip unwanted pairs */ check_error((fscanf(fpvec, " %lf %lf", &u, &v) != 2), "scanning U/V from vector file"); if (u < 10000.0 && v < 10000.0) /* good value to plot */ { speed = sqrt(u * u + v * v) * 100; /* to cm/s */ /* * All this special case checking is essential to avoid * range errors or NaNs in the output. */ angle = speed >= 1e-10 ? (fabs(u) >= 1e-10 ? atan2(v, u) * 180 / M_PI : /* normal case */ (fabs(v) / v) * 180 / M_PI) : 0.0; fprintf(FpPs, "%9.4f %7.2f %9.4f %9.4f A\n", speed / Arrow.cms_per_inch, angle, ll_i[npt].lon, ll_i[npt].lat); } } if (PlotCruiseTrack) /* need to save points */ { npt++; check_error((npt >= MAXLONLAT), "max. array size exceeded"); } } fgets(buff, 255, fpvec); /* Discard remainder of line. */ } fclose(fpvec); if (npt > 1) { fputs("%---Cruise Track---\n", FpPs); fprintf(FpPs, "%f setlinewidth\n", 0.5 * Arrow.linewidth_p / DPI); fprintf(FpPs, "%9.4f %9.4f M\n", ll_i[0].lon, ll_i[0].lat); for (i = 1; i < npt; i++) { fprintf(FpPs, "%9.4f %9.4f L\n", ll_i[i].lon, ll_i[i].lat); } fputs("stroke\n", FpPs); } } /* End of loop through vector files. */ return 0; } void print_heading(TITLE * title, double y) { fprintf(FpPs, "%f /%s Fsi\n", title->font.size_i, title->font.face); fprintf(FpPs, "(%s) %f %f SC\n", title->text, 0.5 * (Bb[llx] + Bb[urx]) / DPI, y); /* add vertical title space to bounding box */ Bb[ury] = larger(Bb[ury], DPI * (y + title->font.size_i)); } int read_label(FILE * fp, LABEL_TYPE *arg, int code) { static LABEL_TYPE label; static char label_type[10], label_symbol[20], label_just[10]; static NAME_LIST_ENTRY_TYPE just[] = { {"left" , J_LEFT}, {"right" , J_RIGHT}, {"center" , J_CENTER}, {NULL , BADINT} }; static OPTION_TYPE label_options[] = { {"end" , 0 , NULL , NULL}, {"type:" , TYPE_STRING , op_get_param , label_type}, {"text:" , 0 , read_quotation, label.text}, {"symbol:" , TYPE_STRING , op_get_param, label_symbol}, {"position:" , COORDINATE , read_xy , (char *) &label.position}, {"justify:" , TYPE_STRING , op_get_param , label_just}, {"font:" , TYPE_STRING , op_get_param , label.font.face}, {"font_size:" , TYPE_DOUBLE , op_get_param , (char *) &label.font.size_p}, {"symbol_size:" , TYPE_DOUBLE , op_get_param , (char *) &label.symbol_size}, {NULL , 0 , NULL , NULL} }; static LABEL_TYPE *newlabel = NULL; /* set label defaults */ if (newlabel != NULL) /* carry over the previous label's settings */ { move_byte((char*) newlabel, (char *) &label, sizeof(LABEL_TYPE)); } else /* use program defaults */ { strcpy(label_type, "common"); strcpy(label_just, "left"); strcpy(label.font.face, "Helvetica"); label.font.size_p = 12; label.symbol_size = 4; } /* the following should always differ from label to label, hence reset: */ label.text[0] = '\0'; strcpy(label_symbol, "none"); label.position.x = label.position.y = -BADVALUE; if ((newlabel = (LABEL_TYPE *) malloc(sizeof(LABEL_TYPE))) == NULL) { fprintf(stderr, "Error : Out of memory in update_label\n"); return (1); } if (!execute_options(fp, label_options, ECHO)) return(1); label.symbol = get_code(SymbolList, label_symbol); label.just = get_code(just, label_just); move_byte((char *) &label, (char *) newlabel, sizeof(LABEL_TYPE)); if (!stricmp(label_type, "common")) { newlabel->next = CommonLabelsHead; /* NULL or previous label */ CommonLabelsHead = newlabel; } else { newlabel->next = LocalLabelsHead; LocalLabelsHead = newlabel; } return (0); } int read_quotation(FILE * fp, char *arg, int code) { get_quotation(fp, arg, 80); switch (code) { case MAIN_TITLE: strcpy(MainTitle.font.face, TitleFont.face); MainTitle.font.size_i = (TitleFont.size_p <= 0.0 ? MainTitle.font.size_p : TitleFont.size_p) / DPI; break; case SUBTITLE: strcpy(SubTitle.font.face, TitleFont.face); SubTitle.font.size_i = (TitleFont.size_p <= 0.0 ? SubTitle.font.size_p : TitleFont.size_p) / DPI; break; case PLOT_TITLE: strcpy(PlotTitle.font.face, TitleFont.face); PlotTitle.font.size_i = (TitleFont.size_p <= 0.0 ? PlotTitle.font.size_p : TitleFont.size_p) / DPI; break; default: break; } report_msg(arg); return (0); } int subplot(FILE * fp, char *arg, int code) { int sp; ShowPage = code; if (fscanf(fp, "%d", &sp) != 1) return 1; PlotArray.m = (int)(sp / 100); PlotArray.n = (int)( (sp - PlotArray.m * 100) / 10 ); PlotArray.current = sp % 10; if (PlotArray.current > PlotArray.m * PlotArray.n) { fprintf(stderr, "\n Invalid subplot value %d\n\n", sp); PlotArray.current = -1; } return 0; } void plot_legend() { int num_ticks, tick_no, long_tick; int precision, ii; double tick_length = 0.05, legend_width, legend_tick_inc, legend_length, y_label; XY_TYPE legend_pos; /* we want the local vars (legend_pos and legend_length) to always be recalculated while their global counterparts (Legend.pos & Legend.length) always retain the original (user-specified or default) values so the legend length & position will automatically readjust if the velocity scale or other plot parameters are altered in subsequent plots */ fputs("%---Legend---\n", FpPs); fprintf(FpPs, "%f setlinewidth\n", Legend.linewidth_p / DPI); legend_length = (Legend.length == -BADVALUE) ? Arrow.cms_per_inch : Legend.length; legend_width = legend_length / Arrow.cms_per_inch; Legend.font.size_i = Legend.font.size_p / DPI; if (Legend.pos.x == -BADVALUE || Legend.pos.y == -BADVALUE) { if (!strcmp(Legend.type, "local")) { /* local legend's upper right corner is set 0.5",0.75" from lower right corner of plot */ legend_pos.x = PlotDim_i.x - legend_width - 0.5; legend_pos.y = 0.75; } else { /* common legend's upper right corner is set 0.5" from rightmost plot axis+margin and 0.25" from bottommost plot axis+margin */ legend_pos.x = 1.0 * Bb[urx] / DPI - legend_width - 0.5; legend_pos.y = 1.0 * Bb[lly] / DPI - 0.25; } } else { legend_pos.x = Legend.pos.x; legend_pos.y = Legend.pos.y; } num_ticks = partition(legend_length, legend_width); legend_tick_inc = (legend_length / num_ticks) / Arrow.cms_per_inch; if (too_dense(legend_tick_inc)) { /* if it's even-valued, then reduce to half; if multiple of 5, then reduce to a fifth; else forget the sub-ticks altogether... */ long_tick = (num_ticks % 2 ? (num_ticks % 5 ? num_ticks : 5) : 2); legend_tick_inc *= long_tick; num_ticks /= long_tick; } /* put long tick at intervals of 5 or 2 or none, as appropriate */ long_tick = (num_ticks % 5 ? (num_ticks % 2 ? num_ticks : 2) : 5); fprintf(FpPs, "%f /%s Fsi\n", Legend.font.size_i, Legend.font.face); /* Horizontal line, left to right. */ fprintf(FpPs, " %f %f M %f 0.0 rlineto\n", legend_pos.x, legend_pos.y, legend_width); /* Left then right ticks and labels. */ precision = get_decimal_length(legend_length); y_label = legend_pos.y - 2 * tick_length - (1.0 + VSPACE) * Legend.font.size_i; for (ii = 0; ii < 2; ii++) { fprintf(FpPs, " %f %f M 0.0 %f R\n", legend_pos.x + ii * legend_width, legend_pos.y, -2.0 * tick_length); fprintf(FpPs, " (%.*f) %f %f SC\n", precision, ii * legend_length, legend_pos.x + ii * legend_width, y_label); } /* Intermediate ticks; every long_tick one is 1.5 times longer. */ for (tick_no = 1; tick_no < num_ticks; tick_no++) { fprintf(FpPs, " %f %f M 0.0 %f rlineto\n", legend_pos.x + legend_width * tick_no / num_ticks, legend_pos.y, -tick_length * (tick_no % long_tick ? 1.0 : 1.5)); } fputs("stroke\n\n", FpPs); /* Title. */ y_label -= (1 + VSPACE) * Legend.font.size_i; fprintf(FpPs, " (%s) %f %f SC\n", Legend.text, legend_pos.x + legend_width / 2.0, y_label); /* constants below approximate legend's space requirements in inches */ Bb[llx] = smaller(Bb[llx], (legend_pos.x + 0.25) * DPI); Bb[lly] = smaller(Bb[lly], y_label * DPI); Bb[urx] = larger(Bb[urx], (legend_pos.x + legend_width + 0.25) * DPI); Bb[ury] = larger(Bb[ury], legend_pos.y * DPI); } void plot_axes() { double x_tick, y_tick; /* tick positions in degrees */ double big_tick, small_tick; /* Tic lengths in inches. */ double x_tick_margin, y_tick_margin, x_tick_num; int lon_decimal, lat_decimal; char nsew; big_tick = 8 / 72.0; /* 8 pts */ small_tick = 0.5 * big_tick; x_tick_margin = -(1 + VSPACE) * Axis.font.size_i; y_tick_margin = -VSPACE * Axis.font.size_i; if (Axis.decimal_place.x < 0) { lon_decimal = get_decimal_length(LonRange.step * Axis.label_interval.x); lat_decimal = get_decimal_length(LatRange.step * Axis.label_interval.y); } else { lon_decimal = (int) Axis.decimal_place.x; lat_decimal = (int) Axis.decimal_place.y; } fputs("%---Axes---\n", FpPs); fprintf(FpPs, "%f setlinewidth\n", Axis.linewidth_p / DPI); fprintf(FpPs, "newpath\n"); fprintf(FpPs, "%f %f M\n", 0.0, 0.0); fprintf(FpPs, "%f %f L\n", LonRange.span / DegPerInch.x, 0.0); fprintf(FpPs, "%f %f L\n", LonRange.span / DegPerInch.x, LatRange.span / DegPerInch.y); fprintf(FpPs, "%f %f L\n", 0.0, LatRange.span / DegPerInch.y); fprintf(FpPs, "closepath\n"); fprintf(FpPs, "%f /%s Fsi\n", Axis.font.size_i, Axis.font.face); if (is_multiple(LonRange.max, LonRange.step) && is_multiple(LonRange.max, LonRange.step * Axis.label_interval.x)) { double hangover = 0.5 * (3 + (fabs(LonRange.max) > 0 ? (int)(log10(fabs(LonRange.max))) : 0) + lon_decimal); /* hangover calculated as half the number of chars on a longtiude label = at least one numeral, plus the degree symbol and hemisphere (hence 3) and then more numerals if >= +/-10, and then the no. of decimal places; we don't bother to count the decimal point which takes up little room anyway */ if (!(PlotArray.current % PlotArray.n) || PlotMargin.right + 0.5 * (PlotSpace_i.x - PlotDim_i.x) >= hangover * font_width(Axis.font.size_i)) x_tick = LonRange.max; else x_tick = LonRange.max - LonRange.step; } else x_tick = LonRange.max - (LonRange.max < 0 ? LonRange.step : 0) - frem(LonRange.max, LonRange.step); if (is_multiple(LatRange.max, LatRange.step)) y_tick = LatRange.max; else y_tick = LatRange.max - (LatRange.max < 0 ? LatRange.step : 0) - frem(LatRange.max, LatRange.step); while (x_tick - LonRange.min >= -EPSILON) { if (is_multiple(x_tick, LonRange.step * Axis.label_interval.x)) { /* Big x-ticks, top and bottom. */ fprintf(FpPs, "%f 0.0 M 0.0 %f rlineto\n", LonToIn(x_tick), big_tick); fprintf(FpPs, "%f %f M 0.0 %f neg rlineto\n", LonToIn(x_tick), PlotDim_i.y, big_tick); /* Labels for bottom x-ticks. */ x_tick_num = x_tick; /* Number to print. */ if (x_tick > 180.0) x_tick_num -= 360.0; /* JR: was 360.0 - x_tick_num */ nsew = ' '; if (x_tick_num > 0.0 && x_tick_num != 180.0) nsew = 'E'; else if (x_tick_num < 0.0 && x_tick_num != -180.0) nsew = 'W'; fprintf(FpPs, "(%.*f\312%c) %f %f SC\n", lon_decimal, fabs(x_tick_num), nsew, LonToIn(x_tick), x_tick_margin); } else { /* Small x-ticks, top and bottom. */ fprintf(FpPs, "%f 0.0 M 0.0 %f R\n", LonToIn(x_tick), small_tick); fprintf(FpPs, "%f %f M 0.0 %f R\n", LonToIn(x_tick), PlotDim_i.y, -small_tick); } x_tick -= LonRange.step; } while (y_tick - LatRange.min >= -EPSILON) { if (is_multiple(y_tick, LatRange.step * Axis.label_interval.y)) { /* Big y-ticks, left and right. */ fprintf(FpPs, "0.0 %f M %f 0.0 R\n", LatToIn(y_tick), big_tick); fprintf(FpPs, "%f %f M %f 0.0 R\n", PlotDim_i.x, LatToIn(y_tick), -big_tick); /* Labels for y-ticks. */ if (y_tick != 0.0) { fprintf(FpPs, "(%.*f\312%c) %f %f SR\n", lat_decimal, fabs(y_tick), (y_tick < 0.0 ? 'S' : 'N'), y_tick_margin, LatToIn(y_tick) - VSPACE * Axis.font.size_i); } else fprintf(FpPs, "(EQ) %f %f SR\n", y_tick_margin, LatToIn(y_tick) - VSPACE * Axis.font.size_i); } else { /* Small y-ticks, left and right. */ fprintf(FpPs, "0.0 %f M %f 0.0 R\n", LatToIn(y_tick), small_tick); fprintf(FpPs, "%f %f M %f 0.0 R\n", PlotDim_i.x, LatToIn(y_tick), -small_tick); } y_tick -= LatRange.step; } fputs("stroke\n", FpPs); } void plot_labels(LABEL_TYPE * labelhead) { double lon, lat; LABEL_TYPE *label; double symbol_space, symbol_size_i; static char stroke_or_fill[2][15] = {"stroked_symbol", "filled_symbol"}; /* center, left, right */ static int just_direction[] = {0, 1, -1}; static char show_string[3][3] = {"SC", "Sh", "SR"}; label = labelhead; while (label) { lon = label->position.x; lat = label->position.y; if (NegativeLongitudes) { if (lon > 180.0) lon -= 360.0; } else { if (lon < 0.0) lon += 360.0; } symbol_space = 0.0; if (label->symbol) { symbol_size_i = label->symbol_size / DPI; fprintf(FpPs, "%s\n", stroke_or_fill[label->symbol & FILLED]); fprintf(FpPs, " %f %f %f %s\n", LonToIn(lon), LatToIn(lat), symbol_size_i, get_name(SymbolList, label->symbol & 0xFFFE)); symbol_space = symbol_size_i; } if (label->text[0] != '\0') { label->font.size_i = label->font.size_p / DPI; fprintf(FpPs, " %f /%s Fsi\n", label->font.size_i, label->font.face); fprintf(FpPs, " (%s) %f %f %s\n", label->text, LonToIn(lon) + just_direction[label->just] * symbol_space, LatToIn(lat) - (label->font.size_i * 0.4) - ((!just_direction[label->just]) * (symbol_space + (label->font.size_i * 0.6))), show_string[label->just]); } label = label->next; } } void set_defaults() { time_t lt; /*--- PLOT SCALE & PLACEMENT ---*/ ShowPage = 1; /* single plot per page */ NewPage = 1; /* begin a new page */ PlotArray.m = PlotArray.n = PlotArray.current = 1; /* single plot per page */ Axis.deg_per_inch.x = Axis.deg_per_inch.y = -1.0; /* auto scaling */ Axis.origin_i.x = Axis.origin_i.y = -1.0; /* auto-center plot */ strcpy(Orientation, "landscape"); PageMargin.top = 1.0; /* time stamp & annotation go here */ PageMargin.bottom = 1.0; /* default common legend will go here */ PageMargin.left = 1.0; PageMargin.right = 0.5; /*--- VECTOR PLOTTING OPTIONS ---*/ strcpy(VecFiles[0], "end"); /* no vectors/cruise_track data */ IndexUV = 0; /* no vectors */ PlotCruiseTrack = 0; /* no cruise track */ Arrow.cms_per_inch = 100; Arrow.headlength_i = 0.10; Arrow.min_headlength_i = -1.0; /* defaults to half of Arrow.headlength_i later */ Arrow.linewidth_p = 0.6; /*--- MAP PLOTTING OPTIONS ---*/ Map.file[0] = '\0'; /* no land outlines */ Map.gray_level = 1.0; /* white = no shading */ Map.linewidth_p = 0.4; /*--- AXES CONTROL ---*/ LonRange.min = LonRange.max = 0.0; /* nothing to plot */ LatRange.min = LatRange.max = 0.0; /* nothing to plot */ Axis.linewidth_p = 0.5; /* pts default width */ Axis.decimal_place.x = Axis.decimal_place.y = -1; /* defaults to step * interval later */ Axis.label_interval.x = Axis.label_interval.y = 1; /* label every tick mark */ Axis.font.size_p = 10.0; strcpy(Axis.font.face, "Times-Bold"); /*--- PLOT TITLES/ANNOTATIONS ---*/ MainTitle.text[0] = '\0'; /* no main title */ SubTitle.text[0] = '\0'; /* no subtitle */ PlotTitle.text[0] = '\0'; /* no plot title */ strcpy(TitleFont.face, "Times-Bold"); TitleFont.size_p = 0.0; MainTitle.font.size_p = 20.0; SubTitle.font.size_p = 10.0; PlotTitle.font.size_p = 10.0; CommonLabelsHead = NULL; /* no common labels */ LocalLabelsHead = NULL; /* no local labels */ strcpy(Legend.type, "common"); /* single legend on entire page */ Legend.pos.x = Legend.pos.y = -BADVALUE; /* calculated later */ strcpy(Legend.text, "Speed (cm/s)"); strcpy(Legend.font.face, "Helvetica"); Legend.font.size_p = 10.0; Legend.length = -BADVALUE; /* defaults to Arrow.cms_per_inch later */ Legend.linewidth_p = 0.5; time(<); strcpy(TodayDT, ctime(<)); TodayDT[24] = '\0'; /* suppress linefeed */ strcpy(TimeStamp, TodayDT); /* today's date/time as returned by ctime */ Annotation[0] = '\0'; /* no annotation */ /*--- OUTPUT OPTIONS ---*/ PsFile[0] = '\0'; /* output to stdout */ }