1 /*
2 *
3 * XASTIR, Amateur Station Tracking and Information Reporting
4 * Copyright (C) 1999,2000 Frank Giannandrea
5 * Copyright (C) 2000-2019 The Xastir Group
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Look at the README for more information on the program.
22 */
23
24 //#define MAP_SCALE_CHECK
25
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif // HAVE_CONFIG_H
30
31 #include "snprintf.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <ctype.h>
38 #include <sys/types.h>
39 #include <pwd.h>
40 #include <errno.h>
41
42 // Needed for Solaris
43 #ifdef HAVE_STRINGS_H
44 #include <strings.h>
45 #endif // HAVE_STRINGS_H
46
47 #ifdef HAVE_MAGICK
48 #if TIME_WITH_SYS_TIME
49 #include <sys/time.h>
50 #include <time.h>
51 #else // TIME_WITH_SYS_TIME
52 #if HAVE_SYS_TIME_H
53 #include <sys/time.h>
54 #else // HAVE_SYS_TIME_H
55 #include <time.h>
56 #endif // HAVE_SYS_TIME_H
57 #endif // TIME_WITH_SYS_TIME
58 #undef RETSIGTYPE
59 // TVR: "stupid ImageMagick"
60 // The problem is that magick/api.h includes Magick's config.h file, and that
61 // pulls in all the same autoconf-generated defines that we use.
62 // plays those games below, but I don't think in the end that they actually
63 // make usable macros with our own data in them.
64 // Fortunately, we don't need them, so I'll just undef the ones that are
65 // causing problems today. See main.c for fixes that preserve our values.
66 #undef PACKAGE
67 #undef VERSION
68 /* JMT - stupid ImageMagick */
69 #define XASTIR_PACKAGE_BUGREPORT PACKAGE_BUGREPORT
70 #undef PACKAGE_BUGREPORT
71 #define XASTIR_PACKAGE_NAME PACKAGE_NAME
72 #undef PACKAGE_NAME
73 #define XASTIR_PACKAGE_STRING PACKAGE_STRING
74 #undef PACKAGE_STRING
75 #define XASTIR_PACKAGE_TARNAME PACKAGE_TARNAME
76 #undef PACKAGE_TARNAME
77 #define XASTIR_PACKAGE_VERSION PACKAGE_VERSION
78 #undef PACKAGE_VERSION
79 #ifdef HAVE_MAGICK
80 #ifdef HAVE_MAGICKCORE_MAGICKCORE_H
81 #include <MagickCore/MagickCore.h>
82 #else
83 #ifdef HAVE_MAGICK_API_H
84 #include <magick/api.h>
85 #endif // HAVE_MAGICK_API_H
86 #endif //HAVE_MAGICKCORE_MAGICKCORE_H
87 #endif //HAVE_MAGICK
88 #undef PACKAGE_BUGREPORT
89 #define PACKAGE_BUGREPORT XASTIR_PACKAGE_BUGREPORT
90 #undef XASTIR_PACKAGE_BUGREPORT
91 #undef PACKAGE_NAME
92 #define PACKAGE_NAME XASTIR_PACKAGE_NAME
93 #undef XASTIR_PACKAGE_NAME
94 #undef PACKAGE_STRING
95 #define PACKAGE_STRING XASTIR_PACKAGE_STRING
96 #undef XASTIR_PACKAGE_STRING
97 #undef PACKAGE_TARNAME
98 #define PACKAGE_TARNAME XASTIR_PACKAGE_TARNAME
99 #undef XASTIR_PACKAGE_TARNAME
100 #undef PACKAGE_VERSION
101 #define PACKAGE_VERSION XASTIR_PACKAGE_VERSION
102 #undef XASTIR_PACKAGE_VERSION
103 #endif // HAVE_MAGICK
104
105 #include <dirent.h>
106 #include <netinet/in.h>
107 #include <Xm/XmAll.h>
108
109 #ifdef HAVE_X11_XPM_H
110 #include <X11/xpm.h>
111 #ifdef HAVE_LIBXPM // if we have both, prefer the extra library
112 #undef HAVE_XM_XPMI_H
113 #endif // HAVE_LIBXPM
114 #endif // HAVE_X11_XPM_H
115
116 #ifdef HAVE_XM_XPMI_H
117 #include <Xm/XpmI.h>
118 #endif // HAVE_XM_XPMI_H
119
120 #include <X11/Xlib.h>
121
122 #include <math.h>
123
124 #include "xastir.h"
125 #include "maps.h"
126 #include "alert.h"
127 #include "util.h"
128 #include "main.h"
129 #include "datum.h"
130 #include "draw_symbols.h"
131 #include "rotated.h"
132 #include "color.h"
133 #include "xa_config.h"
134
135 // Must be last include file
136 #include "leak_detection.h"
137
138 extern XmFontList fontlist1; // Menu/System fontlist
139
140 #define GRID_MORE 5000
141
142 #define CHECKMALLOC(m) if (!m) { fprintf(stderr, "***** Malloc Failed *****\n"); exit(0); }
143
144
145 // Check for XPM and/or ImageMagick. We use "NO_GRAPHICS"
146 // to disable some routines below if the support for them
147 // is not compiled in.
148 #if !(defined(HAVE_LIBXPM) || defined(HAVE_LIBXPM_IN_XM) || defined(HAVE_MAGICK))
149 #define NO_GRAPHICS 1
150 #endif // !(HAVE_LIBXPM || HAVE_LIBXPM_IN_XM || HAVE_MAGICK)
151
152 #if !(defined(HAVE_LIBXPM) || defined(HAVE_LIBXPM_IN_XM))
153 #define NO_XPM 1
154 #endif // !(HAVE_LIBXPM || HAVE_LIBXPM_IN_XM)
155
156
157 void draw_rotated_label_text_to_target (Widget w, int rotation, int x, int y, int label_length, int color, char *label_text, int fontsize, Pixmap target_pixmap, int draw_outline, int outline_bg_color);
158 int get_rotated_label_text_height_pixels(Widget w, char *label_text, int fontsize);
159
160 // Constants defining the color of the labeled grid border.
161 int outline_border_labels = TRUE; // if true put an outline around the border labels
162 int border_foreground_color = 0x20; // color of the map border, if shown
163 // 0x08 is black.
164 // 0x20 is white.
165 int outline_border_labels_color = 0x20;
166 // outline_border_labels_color = border_foreground_color;
167 // color of outline to draw around border labels
168 // use color of border to help make text more legible.
169
170 // Print options
171 Widget print_properties_dialog = (Widget)NULL;
172 static xastir_mutex print_properties_dialog_lock;
173 Widget print_postscript_dialog = (Widget)NULL;
174 static xastir_mutex print_postscript_dialog_lock;
175 Widget printer_data;
176 Widget previewer_data;
177
178 Widget rotate_90 = (Widget)NULL;
179 Widget auto_rotate = (Widget)NULL;
180 //char print_paper_size[20] = "Letter"; // Displayed in dialog, but not used yet.
181 int print_rotated = 0;
182 int print_auto_rotation = 0;
183 //float print_scale = 1.0; // Not used yet.
184 int print_auto_scale = 0;
185 //int print_blank_background_color = 0; // Not used yet.
186 int print_in_monochrome = 0;
187 int print_resolution = 150; // 72 dpi is normal for Postscript.
188 // 100 or 150 dpi work well with HP printer
189 int print_invert = 0; // Reverses black/white
190
191 char printer_program[MAX_FILENAME+1];
192 char previewer_program[MAX_FILENAME+1];
193
194 time_t last_snapshot = 0; // Used to determine when to take next snapshot
195 time_t last_kmlsnapshot = 0; // Used to determine when to take next kml snapshot
196 int doing_snapshot = 0;
197 int snapshot_interval = 0;
198
199
200 int mag;
201 int npoints; /* number of points in a line */
202
203
204 float raster_map_intensity = 0.65; // Raster map color intensity, set from Maps->Map Intensity
205 float imagemagick_gamma_adjust = 0.0; // Additional imagemagick map gamma correction, set from Maps->Adjust Gamma
206
207 // Storage for the index file timestamp
208 time_t map_index_timestamp;
209
210 int grid_size = 0;
211
212 // Rounding
213 #ifndef HAVE_ROUNDF
214 // Poor man's rounding, but rounds away from zero as roundf is supposed to.
215
roundf(float x)216 float roundf(float x)
217 {
218 int i;
219 i= (((x)>=0)?(((x)+.5)):(((x)-.5)));
220 return ((float)i);
221 }
222
223 #endif
224
225
226 // UTM Grid stuff
227 //
228 //#define UTM_DEBUG
229 //#define UTM_DEBUG_VERB
230 //#define UTM_DEBUG_ALLOC
231 //
max_i(int a,int b)232 static inline int max_i(int a, int b)
233 {
234 return (a > b ? a : b);
235 }
236 #define UTM_GRID_EQUATOR 10000000
237 // the maximum number of UTM zones that will appear on a screen that has a
238 // high enough resolution to display the within-zone utm grid
239 #define UTM_GRID_MAX_ZONES 4
240 // the maximum number of grid lines in each direction shown in each zone
241 // on the screen at one time
242 #define UTM_GRID_MAX_COLS_ROWS 64
243 #define UTM_GRID_DEF_NALLOCED 8
244 #define UTM_GRID_RC_EMPTY 0xffff
245
246 // the hash stores the upper left and lower right boundaries of the
247 // display of a utm grid to determine whether it matches the current
248 // screen boundaries or whether the grid needs to be recalculated
249 // for the current screen.
250 typedef struct
251 {
252 long ul_x;
253 long ul_y;
254 long lr_x;
255 long lr_y;
256 } hash_t;
257
258 // The utm grid is drawn by connecting a grid of points marking the
259 // intersection of the easting and northing lines. col_or_row holds
260 // this grid of points for the portion of a utm zone visible on the
261 // screen. Zone boundaries are not directly represented here.
262 // In particular, the parallels separating lettered zone rows are
263 // not directly represented here.
264 typedef struct
265 {
266 int firstpoint;
267 int npoints;
268 int nalloced;
269 XPoint *points;
270 } col_or_row_t;
271
272 // The portion of a utm zone visible on the screen has some number of
273 // visible easting lines and northing lines forming a grid that covers
274 // part or all of the screen. Zone holds arrays of the coordinates
275 // of the visible easting and northing intersections, and the next set
276 // of intersections off screen. The coarseness of this grid is
277 // determined by the current scale and stored in utm_grid_spacing_m.
278 typedef struct
279 {
280 unsigned int ncols;
281 col_or_row_t col[UTM_GRID_MAX_COLS_ROWS];
282 unsigned int nrows;
283 col_or_row_t row[UTM_GRID_MAX_COLS_ROWS];
284 int boundary_x;
285 } zone_t;
286
287 // A utm grid is represented by its hash (upper left and lower right coordinates)
288 // which are used to check whether or not it fits the current screen or needs to
289 // be recalculated, and an array of the zones visible on the screen, each containing
290 // arrays of the points marking the easting/northing intersections of a zone (with
291 // one or more lettered zone rows).
292 typedef struct
293 {
294 hash_t hash;
295 unsigned int nzones;
296 zone_t zone[UTM_GRID_MAX_ZONES];
297 } utm_grid_t;
298
299 utm_grid_t utm_grid;
300
301 unsigned int utm_grid_spacing_m;
302 // ^ the above is extern'ed in maps.h for use by draw_ruler_text in
303 // db.c
304
305
306
307
308
309 // Clear out/set up the UTM/MGRS minor grid intersection arrays.
310 //
311 // do_alloc = 0: Don't allocate memory
312 // 1: Allocate memory for the points
313 //
314 // Returns: 0 if ok
315 // 1 if malloc problem
316 //
utm_grid_clear(int do_alloc)317 int utm_grid_clear(int do_alloc)
318 {
319 int i, j;
320
321 utm_grid.hash.ul_x = 0;
322 utm_grid.hash.ul_y = 0;
323 utm_grid.hash.lr_x = 0;
324 utm_grid.hash.lr_y = 0;
325 utm_grid.nzones = 0;
326
327 for (i=0; i < UTM_GRID_MAX_ZONES; i++)
328 {
329
330 utm_grid.zone[i].ncols = utm_grid.zone[i].nrows = 0;
331 utm_grid.zone[i].boundary_x = 0;
332
333 for (j=0; j < UTM_GRID_MAX_COLS_ROWS; j++)
334 {
335
336 //
337 // Clear out column arrays
338 //
339 utm_grid.zone[i].col[j].firstpoint = UTM_GRID_RC_EMPTY;
340 utm_grid.zone[i].col[j].npoints = 0;
341
342 if (utm_grid.zone[i].col[j].nalloced)
343 {
344 free(utm_grid.zone[i].col[j].points);
345 }
346
347 utm_grid.zone[i].col[j].nalloced = 0;
348 utm_grid.zone[i].col[j].points = NULL;
349
350 if (do_alloc)
351 {
352
353 // Allocate enough space for 8 points
354 utm_grid.zone[i].col[j].points = calloc(UTM_GRID_DEF_NALLOCED, sizeof(XPoint));
355 if (!utm_grid.zone[i].col[j].points)
356 {
357 fprintf(stderr,"calloc(%d, %d) for z=%d col=%d FAILED!\n",
358 UTM_GRID_DEF_NALLOCED,
359 (int)sizeof(XPoint),
360 i,
361 j);
362 // abort(); // Causes a segfault
363 return(1);
364 }
365 utm_grid.zone[i].col[j].nalloced = UTM_GRID_DEF_NALLOCED;
366 }
367
368 //
369 // Clear out row arrays
370 //
371 utm_grid.zone[i].row[j].firstpoint = UTM_GRID_RC_EMPTY;
372 utm_grid.zone[i].row[j].npoints = 0;
373 utm_grid.zone[i].row[j].nalloced = 0;
374
375 if (utm_grid.zone[i].row[j].nalloced)
376 {
377 free(utm_grid.zone[i].row[j].points);
378 }
379
380 utm_grid.zone[i].row[j].nalloced = 0;
381 utm_grid.zone[i].row[j].points = NULL;
382
383 if (do_alloc)
384 {
385
386 // Allocate enough space for 8 points
387 utm_grid.zone[i].row[j].points = calloc(UTM_GRID_DEF_NALLOCED, sizeof(XPoint));
388 if (!utm_grid.zone[i].row[j].points)
389 {
390 fprintf(stderr,"calloc(%d, %d) for z=%d row=%d FAILED!\n",
391 UTM_GRID_DEF_NALLOCED,
392 (int)sizeof(XPoint),
393 i,
394 j);
395 // abort(); // Causes a segfault
396 return(1);
397 }
398 utm_grid.zone[i].row[j].nalloced = UTM_GRID_DEF_NALLOCED;
399 }
400 }
401 }
402 return(0);
403 }
404
405
406
407
408
maps_init(void)409 void maps_init(void)
410 {
411 fprintf(stderr,"\n\nBuilt-in map types:\n");
412 fprintf(stderr,"%10s USGS GNIS Datapoints\n","gnis");
413 fprintf(stderr,"%10s USGS GNIS Datapoints w/population\n","pop");
414 fprintf(stderr,"%10s APRSdos Maps\n","map");
415 fprintf(stderr,"%10s WinAPRS/MacAPRS/X-APRS Maps\n","map");
416
417 fprintf(stderr,"\nSupport for these additional map types has been compiled in: \n");
418
419 #ifdef HAVE_MAGICK
420 fprintf(stderr,"%10s Image Map (ImageMagick/GraphicsMagick library, many formats allowed)\n","geo");
421 #endif // HAVE_MAGICK
422
423 #ifndef NO_GRAPHICS
424 #ifdef HAVE_LIBCURL
425 fprintf(stderr,"%10s URL (Internet maps via libcurl library)\n","geo");
426 fprintf(stderr,"%10s URL (OpenStreetMaps via libcurl library\n","geo");
427 fprintf(stderr,"%10s Copyright OpenStreetMap and contributors, CC-BY-SA)\n", "");
428
429 #else
430 #ifdef HAVE_WGET
431 fprintf(stderr,"%10s URL (Internet maps via wget)\n","geo");
432 fprintf(stderr,"%10s URL (OpenStreetMaps via wget\n","geo");
433 fprintf(stderr,"%10s Copyright OpenStreetMap and contributors, CC-BY-SA)\n", "");
434 #endif // HAVE_WGET
435 #endif // HAVE_LIBCURL
436 #endif // NO_GRAPHICS
437
438
439 #ifdef HAVE_LIBSHP
440 fprintf(stderr,"%10s ESRI Shapefile Maps (Shapelib library)\n","shp");
441 #endif // HAVE_LIBSHP
442
443
444 #ifdef HAVE_LIBGEOTIFF
445 fprintf(stderr,"%10s USGS DRG Geotiff Topographic Maps (libgeotiff/libproj)\n","tif");
446 #endif // HAVE_LIBGEOTIFF
447
448 #ifndef NO_XPM
449 fprintf(stderr,"%10s X Pixmap Maps (XPM library)\n","xpm");
450 #endif // NO_XPM
451
452 init_critical_section( &print_properties_dialog_lock );
453 init_critical_section( &print_postscript_dialog_lock );
454
455 // Clear the minor UTM/MGRS grid arrays. Do _not_ allocate
456 // memory for the points.
457 (void)utm_grid_clear(0);
458 }
459
460
461
462
463
464 /*
465 * Calculate NS distance scale at a given location
466 * in meters per Xastir unit
467 */
calc_dscale_y(long UNUSED (x),long UNUSED (y))468 double calc_dscale_y(long UNUSED(x), long UNUSED(y) )
469 {
470
471 // approximation by looking at +/- 0.5 minutes offset
472 // (void)(calc_distance(y-3000, x, y+3000, x)/6000.0);
473 // but this scale is fixed at 1852/6000
474 return((double)(1852.0/6000.0));
475 }
476
477
478
479
480
481 /*
482 * Calculate EW distance scale at a given location
483 * in meters per Xastir unit
484 */
calc_dscale_x(long x,long y)485 double calc_dscale_x(long x, long y)
486 {
487
488 // approximation by looking at +/- 0.5 minutes offset
489 // we should find a better formula...
490 return(calc_distance(y, x-3000, y, x+3000)/6000.0);
491 }
492
493
494
495
496
497 /*
498 * Calculate x map scaling for current location
499 * With that we could have equal distance scaling or a better
500 * view for pixel maps
501 */
get_x_scale(long x,long y,long ysc)502 long get_x_scale(long x, long y, long ysc)
503 {
504 long xsc;
505 double sc_x;
506 double sc_y;
507
508 sc_x = calc_dscale_x(x,y); // meter per Xastir unit
509 sc_y = calc_dscale_y(x,y);
510 if (sc_x < 0.01 || ysc > 50000)
511 // keep it near the poles (>88 deg) or if big parts of world seen
512 {
513 xsc = ysc;
514 }
515 else
516 // adjust y scale, so that the distance is identical in both directions:
517 {
518 xsc = (long)(ysc * sc_y / sc_x +0.4999);
519 }
520
521 //fprintf(stderr,"Scale: x %5.3fkm/deg, y %5.3fkm/deg, x %ld y %ld\n",sc_x*360,sc_y*360,xsc,ysc);
522 return(xsc);
523 }
524
525
526
527
528
529 /** MAP DRAWING ROUTINES **/
530
531
532
533 // Function to perform 2D line-clipping Using the improved parametric
534 // line-clipping algorithm by Liang, Barsky, and Slater published in
535 // the paper: "Some Improvements to a Parametric Line Clipping
536 // Algorithm", 1992. Called by clip2d() function below. This
537 // function is set up for float values. See the clipt_long() and
538 // clipt_int() functions for use with other types of values.
539 //
540 // Returns False if the line is rejected, True otherwise. May modify
541 // t0 and t1.
542 //
clipt(float p,float q,float * t0,float * t1)543 int clipt(float p, float q, float *t0, float *t1)
544 {
545 float r;
546 int accept = True;
547
548
549 if (p < 0) // Entering visible region, so compute t0
550 {
551
552 if (q < 0) // t0 will be non-negative, so continue
553 {
554
555 r = q / p;
556
557 if (r > *t1) // t0 will exceed t1, so reject
558 {
559 accept = False;
560 }
561 else if (r > *t0) // t0 is max of r's
562 {
563 *t0 = r;
564 }
565 }
566 }
567 else
568 {
569
570 if (p > 0) // Exiting visible region, so compute t1
571 {
572
573 if (q < p) // t1 will be <= 1, so continue
574 {
575
576 r = q / p;
577
578 if (r < *t0) // t1 will be <= t0, so reject
579 {
580 accept = False;
581 }
582 else if (r < *t1) // t1 is min of r's
583 {
584 *t1 = r;
585 }
586 }
587 }
588 else // p == 0
589 {
590
591 if (q < 0) // Line parallel and outside, so reject
592 {
593 accept = False;
594 }
595 }
596 }
597 return(accept);
598 }
599
600
601
602
603
604 // Function to perform 2D line-clipping using the improved parametric
605 // line-clipping algorithm by Liang, Barsky, and Slater published in
606 // the paper: "Some Improvements to a Parametric Line Clipping
607 // Algorithm", 1992. Uses the clipt() function above.
608 //
609 // Returns False if the line is rejected, True otherwise. x0, y0,
610 // x1, y1 may get modified by this function. These will be the new
611 // clipped line ends that fit inside the window.
612 //
613 // Clip 2D line segment with endpoints (x0,y0) and (x1,y1). The clip
614 // window is x_left <= x <= x_right and y_bottom <= y <= y_top.
615 //
616 // This function is set up for float values. See the clip2d_long()
617 // and clip2d_screen() functions for use with other types of values.
618 //
clip2d(float * x0,float * y0,float * x1,float * y1)619 int clip2d(float *x0, float *y0, float *x1, float *y1)
620 {
621 float t0, t1;
622 float delta_x, delta_y;
623 int visible = False;
624 float x_left, y_top; // NW corner of screen
625 float x_right, y_bottom; // SE corner of screen
626
627
628 // Assign floating point values for screen corners
629 y_top = f_NW_corner_latitude;
630 y_bottom = f_SE_corner_latitude;
631 x_left = f_NW_corner_longitude;
632 x_right = f_SE_corner_longitude;
633
634 if ( ((*x0 < x_left) && (*x1 < x_left))
635 || ((*x0 > x_right) && (*x1 > x_right))
636 || ((*y0 < y_bottom) && (*y1 < y_bottom))
637 || ((*y0 > y_top) && (*y1 > y_top)) )
638 {
639
640 // Both endpoints are on outside and same side of visible
641 // region, so reject.
642 return(visible);
643 }
644
645 t0 = 0;
646 t1 = 1;
647 delta_x = *x1 - *x0;
648
649 if ( clipt(-delta_x, *x0 - x_left, &t0, &t1) ) // left
650 {
651
652 if ( clipt(delta_x, x_right - *x0, &t0, &t1) ) // right
653 {
654
655 delta_y = *y1 - *y0;
656
657 if ( clipt(-delta_y, *y0 - y_bottom, &t0, &t1) ) // bottom
658 {
659
660 if ( clipt(delta_y, y_top - *y0, &t0, &t1) ) // top
661 {
662 // Compute coordinates
663
664 if (t1 < 1) // Compute V1' (new x1,y1)
665 {
666 *x1 = *x0 + t1 * delta_x;
667 *y1 = *y0 + t1 * delta_y;
668 }
669
670 if (t0 > 0) // Compute V0' (new x0,y0)
671 {
672 *x0 = *x0 + t0 * delta_x;
673 *y0 = *y0 + t0 * delta_y;
674 }
675 visible = True;
676 }
677 }
678 }
679 }
680 return(visible);
681 }
682
683
684
685
686
687 // Function to perform 2D line-clipping Using the improved parametric
688 // line-clipping algorithm by Liang, Barsky, and Slater published in
689 // the paper: "Some Improvements to a Parametric Line Clipping
690 // Algorithm", 1992. Called by clip2d_long() function below. This
691 // function is set up for Xastir coordinate values (unsigned longs).
692 // See the clipt() and clipt_int() functions for use with other
693 // types of values.
694 //
695 // Returns False if the line is rejected, True otherwise. May modify
696 // t0 and t1.
697 //
clipt_long(long p,long q,float * t0,float * t1)698 int clipt_long(long p, long q, float *t0, float *t1)
699 {
700 float r;
701 int accept = True;
702
703
704 if (p < 0) // Entering visible region, so compute t0
705 {
706
707 if (q < 0) // t0 will be non-negative, so continue
708 {
709
710 r = q / (p * 1.0);
711
712 if (r > *t1) // t0 will exceed t1, so reject
713 {
714 accept = False;
715 }
716 else if (r > *t0) // t0 is max of r's
717 {
718 *t0 = r;
719 }
720 }
721 }
722 else
723 {
724
725 if (p > 0) // Exiting visible region, so compute t1
726 {
727
728 if (q < p) // t1 will be <= 1, so continue
729 {
730
731 r = q / (p * 1.0);
732
733 if (r < *t0) // t1 will be <= t0, so reject
734 {
735 accept = False;
736 }
737 else if (r < *t1) // t1 is min of r's
738 {
739 *t1 = r;
740 }
741 }
742 }
743 else // p == 0
744 {
745
746 if (q < 0) // Line parallel and outside, so reject
747 {
748 accept = False;
749 }
750 }
751 }
752 //fprintf(stderr,"clipt_long: %d\n",accept);
753 return(accept);
754 }
755
756
757
758
759
760 // Function to perform 2D line-clipping using the improved parametric
761 // line-clipping algorithm by Liang, Barsky, and Slater published in
762 // the paper: "Some Improvements to a Parametric Line Clipping
763 // Algorithm", 1992. Uses the clipt_long() function above.
764 //
765 // Returns False if the line is rejected, True otherwise. x0, y0,
766 // x1, y1 may get modified by this function. These will be the new
767 // clipped line ends that fit inside the window.
768 //
769 // Clip 2D line segment with endpoints (x0,y0) and (x1,y1). The clip
770 // window is x_left <= x <= x_right and y_bottom <= y <= y_top.
771 //
772 // This function uses the Xastir coordinate system. We had to flip
773 // y_bottom/y_top below due to the coordinate system being
774 // upside-down.
775 //
776 // This function is set up for Xastir coordinate values (unsigned
777 // longs). See the clip2d() or clip2d_screen() functions for use
778 // with other types of values.
779 //
clip2d_long(unsigned long * x0,unsigned long * y0,unsigned long * x1,unsigned long * y1)780 int clip2d_long(unsigned long *x0, unsigned long *y0, unsigned long *x1, unsigned long *y1)
781 {
782 float t0, t1;
783 long delta_x, delta_y;
784 int visible = False;
785 unsigned long x_left = NW_corner_longitude;
786 unsigned long x_right = SE_corner_longitude;
787 // Reverse the following two as our Xastir coordinate system is
788 // upside down. The algorithm requires this order.
789 unsigned long y_top = SE_corner_latitude;
790 unsigned long y_bottom = NW_corner_latitude;
791
792
793 if ( ( (*x0 < x_left ) && (*x1 < x_left ) )
794 || ( (*x0 > x_right ) && (*x1 > x_right ) )
795 || ( (*y0 < y_bottom) && (*y1 < y_bottom) )
796 || ( (*y0 > y_top ) && (*y1 > y_top ) ) )
797 {
798
799 // Both endpoints are on same side of visible region and
800 // outside of it, so reject.
801 //fprintf(stderr,"reject 1\n");
802 return(visible);
803 }
804
805 t0 = 0;
806 t1 = 1;
807 delta_x = *x1 - *x0;
808
809 if ( clipt_long(-delta_x, *x0 - x_left, &t0, &t1) ) // left
810 {
811
812 if ( clipt_long(delta_x, x_right - *x0, &t0, &t1) ) // right
813 {
814
815 delta_y = *y1 - *y0;
816
817 if ( clipt_long(-delta_y, *y0 - y_bottom, &t0, &t1) ) // bottom
818 {
819
820 if ( clipt_long(delta_y, y_top - *y0, &t0, &t1) ) // top
821 {
822 // Compute coordinates
823
824 if (t1 < 1) // Compute V1' (new x1,y1)
825 {
826 *x1 = *x0 + t1 * delta_x;
827 *y1 = *y0 + t1 * delta_y;
828 }
829
830 if (t0 > 0) // Compute V0' (new x0,y0)
831 {
832 *x0 = *x0 + t0 * delta_x;
833 *y0 = *y0 + t0 * delta_y;
834 }
835 visible = True;
836 }
837 else
838 {
839 //fprintf(stderr,"reject top\n");
840 }
841 }
842 else
843 {
844 //fprintf(stderr,"reject bottom\n");
845 }
846 }
847 else
848 {
849 //fprintf(stderr,"reject right\n");
850 }
851 }
852 else
853 {
854 //fprintf(stderr,"reject left\n");
855 }
856 return(visible);
857 }
858
859
860
861
862
863 // Function to perform 2D line-clipping Using the improved parametric
864 // line-clipping algorithm by Liang, Barsky, and Slater published in
865 // the paper: "Some Improvements to a Parametric Line Clipping
866 // Algorithm", 1992. Called by clip2d_screen() function below. This
867 // function is set up for screen coordinate values (unsigned ints).
868 // See the clipt() and clipt_long functions for use with other types
869 // of values.
870 //
871 // Returns False if the line is rejected, True otherwise. May modify
872 // t0 and t1.
873 //
clipt_int(int p,int q,float * t0,float * t1)874 int clipt_int(int p, int q, float *t0, float *t1)
875 {
876 float r;
877 int accept = True;
878
879
880 if (p < 0) // Entering visible region, so compute t0
881 {
882
883 if (q < 0) // t0 will be non-negative, so continue
884 {
885
886 r = q / (p * 1.0);
887
888 if (r > *t1) // t0 will exceed t1, so reject
889 {
890 accept = False;
891 }
892 else if (r > *t0) // t0 is max of r's
893 {
894 *t0 = r;
895 }
896 }
897 }
898 else
899 {
900
901 if (p > 0) // Exiting visible region, so compute t1
902 {
903
904 if (q < p) // t1 will be <= 1, so continue
905 {
906
907 r = q / (p * 1.0);
908
909 if (r < *t0) // t1 will be <= t0, so reject
910 {
911 accept = False;
912 }
913 else if (r < *t1) // t1 is min of r's
914 {
915 *t1 = r;
916 }
917 }
918 }
919 else // p == 0
920 {
921
922 if (q < 0) // Line parallel and outside, so reject
923 {
924 accept = False;
925 }
926 }
927 }
928 //fprintf(stderr,"clipt_int: %d\n",accept);
929 return(accept);
930 }
931
932
933
934
935
936 // Function to perform 2D line-clipping using the improved parametric
937 // line-clipping algorithm by Liang, Barsky, and Slater published in
938 // the paper: "Some Improvements to a Parametric Line Clipping
939 // Algorithm", 1992. Uses the clipt_int() function above.
940 //
941 // Returns False if the line is rejected, True otherwise. x0, y0,
942 // x1, y1 may get modified by this function. These will be the new
943 // clipped line ends that fit inside the window.
944 //
945 // Clip 2D line segment with endpoints (x0,y0) and (x1,y1). The clip
946 // window is x_left <= x <= x_right and y_bottom <= y <= y_top.
947 //
948 // This function uses the screen coordinate system. We had to flip
949 // y_bottom/y_top below due to the coordinate system being
950 // upside-down.
951 //
952 // This function is set up for screen coordinate values (unsigned
953 // ints). See the clip2d() and clip2d_long() functions for use
954 // with other types of values.
955 //
clip2d_screen(unsigned int * x0,unsigned int * y0,unsigned int * x1,unsigned int * y1)956 int clip2d_screen(unsigned int *x0, unsigned int *y0, unsigned int *x1, unsigned int *y1)
957 {
958 float t0, t1;
959 int delta_x, delta_y;
960 int visible = False;
961 unsigned int x_right = screen_width;
962 unsigned int x_left = 0;
963 // Reverse the following two as our screen coordinate system is
964 // upside down. The algorithm requires this order.
965 unsigned int y_top = screen_height;
966 unsigned int y_bottom = 0;
967
968
969 if ( ( (*x0 < x_left ) && (*x1 < x_left ) )
970 || ( (*x0 > x_right ) && (*x1 > x_right ) )
971 || ( (*y0 < y_bottom) && (*y1 < y_bottom) )
972 || ( (*y0 > y_top ) && (*y1 > y_top ) ) )
973 {
974
975 // Both endpoints are on same side of visible region and
976 // outside of it, so reject.
977 //fprintf(stderr,"reject 1\n");
978 return(visible);
979 }
980
981 t0 = 0;
982 t1 = 1;
983 delta_x = *x1 - *x0;
984
985 if ( clipt_int(-delta_x, *x0 - x_left, &t0, &t1) ) // left
986 {
987
988 if ( clipt_int(delta_x, x_right - *x0, &t0, &t1) ) // right
989 {
990
991 delta_y = *y1 - *y0;
992
993 if ( clipt_int(-delta_y, *y0 - y_bottom, &t0, &t1) ) // bottom
994 {
995
996 if ( clipt_int(delta_y, y_top - *y0, &t0, &t1) ) // top
997 {
998 // Compute coordinates
999
1000 if (t1 < 1) // Compute V1' (new x1,y1)
1001 {
1002 *x1 = *x0 + t1 * delta_x;
1003 *y1 = *y0 + t1 * delta_y;
1004 }
1005
1006 if (t0 > 0) // Compute V0' (new x0,y0)
1007 {
1008 *x0 = *x0 + t0 * delta_x;
1009 *y0 = *y0 + t0 * delta_y;
1010 }
1011 visible = True;
1012 }
1013 else
1014 {
1015 //fprintf(stderr,"reject top\n");
1016 }
1017 }
1018 else
1019 {
1020 //fprintf(stderr,"reject bottom\n");
1021 }
1022 }
1023 else
1024 {
1025 //fprintf(stderr,"reject right\n");
1026 }
1027 }
1028 else
1029 {
1030 //fprintf(stderr,"reject left\n");
1031 }
1032 return(visible);
1033 }
1034
1035
1036
1037
1038
1039 // Draws a point onto a pixmap. Assumes that the proper GC has
1040 // already been set up for drawing the correct color/width/linetype,
1041 // etc. If the bounding box containing the point doesn't intersect
1042 // with the current view, the point isn't drawn.
1043 //
1044 // Input point is in the Xastir coordinate system.
1045 //
draw_point(Widget w,unsigned long x1,unsigned long y1,GC gc,Pixmap which_pixmap,int skip_duplicates)1046 void draw_point(Widget w,
1047 unsigned long x1,
1048 unsigned long y1,
1049 GC gc,
1050 Pixmap which_pixmap,
1051 int skip_duplicates)
1052 {
1053
1054 int x1i, y1i;
1055 static int last_x1i;
1056 static int last_y1i;
1057
1058
1059 // Check whether the two bounding boxes intersect. If not, skip
1060 // the rest of this function. Do we need to worry about
1061 // special-case code here to handle vertical/horizontal lines
1062 // (width or length of zero)?
1063 //
1064 //
1065 // WE7U
1066 // Check to see if we can do this faster than map_visible() can. A
1067 // point is a special case that should take only four compares
1068 // against the map window boundaries.
1069 //
1070 if (!map_visible(y1, y1, x1, x1))
1071 {
1072 // Skip this vector
1073 //fprintf(stderr,"Point not visible\n");
1074 return;
1075 }
1076
1077 // Convert to screen coordinates. Careful here!
1078 // The format conversions you'll need if you try to
1079 // compress this into two lines will get you into
1080 // trouble.
1081 x1i = x1 - NW_corner_longitude;
1082 x1i = x1i / scale_x;
1083
1084 y1i = y1 - NW_corner_latitude;
1085 y1i = y1i / scale_y;
1086
1087 if (skip_duplicates)
1088 {
1089 if (x1i == last_x1i && y1i == last_y1i)
1090 {
1091 return;
1092 }
1093 }
1094
1095 // XDrawPoint uses 16-bit unsigned integers
1096 // (shorts). Make sure we stay within the limits.
1097 (void)XDrawPoint(XtDisplay(w),
1098 which_pixmap,
1099 gc,
1100 l16(x1i),
1101 l16(y1i));
1102
1103 last_x1i = x1i;
1104 last_y1i = y1i;
1105 }
1106
1107
1108
1109
1110
1111 // Draws a vector onto a pixmap. Assumes that the proper GC has
1112 // already been set up for drawing the correct color/width/linetype,
1113 // etc.
1114 //
1115 // Input points are in lat/long coordinates.
1116 //
draw_point_ll(Widget w,float y1,float x1,GC gc,Pixmap which_pixmap,int skip_duplicates)1117 void draw_point_ll(Widget w,
1118 float y1, // lat1
1119 float x1, // long1
1120 GC gc,
1121 Pixmap which_pixmap,
1122 int skip_duplicates)
1123 {
1124
1125 unsigned long x1L, y1L;
1126
1127 //
1128 // WE7U
1129 // We should probably do four floating point compares against the
1130 // map boundaries here in order to speed up rejection of points that
1131 // don't fit our window. If we change to that, copy the conversion-
1132 // to-screen-coordinates and drawing stuff from draw_point() down to
1133 // this routine so that we don't go through the comparisons again
1134 // there.
1135 //
1136 // Convert the point to the Xastir coordinate system.
1137 convert_to_xastir_coordinates(&x1L,
1138 &y1L,
1139 x1,
1140 y1);
1141
1142 // Call the draw routine above.
1143 draw_point(w, x1L, y1L, gc, which_pixmap, skip_duplicates);
1144 }
1145
1146
1147
1148
1149
1150 // Draws a vector onto a pixmap. Assumes that the proper GC has
1151 // already been set up for drawing the correct color/width/linetype,
1152 // etc. If the bounding box containing the vector doesn't intersect
1153 // with the current view, the line isn't drawn.
1154 //
1155 // Input points are in the Xastir coordinate system.
1156 //
draw_vector(Widget w,unsigned long x1,unsigned long y1,unsigned long x2,unsigned long y2,GC gc,Pixmap which_pixmap,int skip_duplicates)1157 void draw_vector(Widget w,
1158 unsigned long x1,
1159 unsigned long y1,
1160 unsigned long x2,
1161 unsigned long y2,
1162 GC gc,
1163 Pixmap which_pixmap,
1164 int skip_duplicates)
1165 {
1166
1167 int x1i, x2i, y1i, y2i;
1168 static int last_x1i, last_x2i, last_y1i, last_y2i;
1169
1170
1171 //fprintf(stderr,"%ld,%ld %ld,%ld\t",x1,y1,x2,y2);
1172 if ( !clip2d_long(&x1, &y1, &x2, &y2) )
1173 {
1174 // Skip this vector
1175 //fprintf(stderr,"Line not visible\n");
1176 //fprintf(stderr,"%ld,%ld %ld,%ld\n",x1,y1,x2,y2);
1177 return;
1178 }
1179 //fprintf(stderr,"%ld,%ld %ld,%ld\n",x1,y1,x2,y2);
1180
1181 // Convert to screen coordinates. Careful here!
1182 // The format conversions you'll need if you try to
1183 // compress this into two lines will get you into
1184 // trouble.
1185 x1i = x1 - NW_corner_longitude;
1186 x1i = x1i / scale_x;
1187
1188 y1i = y1 - NW_corner_latitude;
1189 y1i = y1i / scale_y;
1190
1191 x2i = x2 - NW_corner_longitude;
1192 x2i = x2i / scale_x;
1193
1194 y2i = y2 - NW_corner_latitude;
1195 y2i = y2i / scale_y;
1196
1197 if (skip_duplicates)
1198 {
1199 if (last_x1i == x1i
1200 && last_x2i == x2i
1201 && last_y1i == y1i
1202 && last_y2i == y2i)
1203 {
1204 return;
1205 }
1206 }
1207
1208 // XDrawLine uses 16-bit unsigned integers
1209 // (shorts). Make sure we stay within the limits.
1210 // clip2d_long() should make sure of this anyway as it clips
1211 // lines to fit the window.
1212 //
1213 (void)XDrawLine(XtDisplay(w),
1214 which_pixmap,
1215 gc,
1216 l16(x1i),
1217 l16(y1i),
1218 l16(x2i),
1219 l16(y2i));
1220
1221 last_x1i = x1i;
1222 last_x2i = x2i;
1223 last_y1i = y1i;
1224 last_y2i = y2i;
1225 }
1226
1227
1228
1229
1230
1231 // Draws a vector onto a pixmap. Assumes that the proper GC has
1232 // already been set up for drawing the correct color/width/linetype,
1233 // etc.
1234 //
1235 // Input points are in lat/long coordinates.
1236 //
draw_vector_ll(Widget w,float y1,float x1,float y2,float x2,GC gc,Pixmap which_pixmap,int skip_duplicates)1237 void draw_vector_ll(Widget w,
1238 float y1, // lat1
1239 float x1, // long1
1240 float y2, // lat2
1241 float x2, // long2
1242 GC gc,
1243 Pixmap which_pixmap,
1244 int skip_duplicates)
1245 {
1246
1247 unsigned long x1L, x2L, y1L, y2L;
1248 int x1i, x2i, y1i, y2i;
1249 static int last_x1i, last_x2i, last_y1i, last_y2i;
1250
1251
1252 //fprintf(stderr,"%lf,%lf %lf,%lf\t",x1,y1,x2,y2);
1253 if ( !clip2d(&x1, &y1, &x2, &y2) )
1254 {
1255 // Skip this vector
1256 //fprintf(stderr,"Line not visible: %lf,%lf %lf,%lf\n",y1,x1,y2,x2);
1257 return;
1258 }
1259 //fprintf(stderr,"%lf,%lf %lf,%lf\n",x1,y1,x2,y2);
1260
1261 // Convert the points to the Xastir coordinate system.
1262 convert_to_xastir_coordinates(&x1L,
1263 &y1L,
1264 x1,
1265 y1);
1266
1267 convert_to_xastir_coordinates(&x2L,
1268 &y2L,
1269 x2,
1270 y2);
1271
1272 //fprintf(stderr,"%ld,%ld %ld,%ld\n",x1L,y1L,x2L,y2L);
1273
1274 // Convert to screen coordinates. Careful here!
1275 // The format conversions you'll need if you try to
1276 // compress this into two lines will get you into
1277 // trouble.
1278 x1i = (int)(x1L - NW_corner_longitude);
1279 x1i = x1i / scale_x;
1280
1281 y1i = (int)(y1L - NW_corner_latitude);
1282 y1i = y1i / scale_y;
1283
1284 x2i = (int)(x2L - NW_corner_longitude);
1285 x2i = x2i / scale_x;
1286
1287 y2i = (int)(y2L - NW_corner_latitude);
1288 y2i = y2i / scale_y;
1289
1290 if (skip_duplicates)
1291 {
1292 if (last_x1i == x1i
1293 && last_x2i == x2i
1294 && last_y1i == y1i
1295 && last_y2i == y2i)
1296 {
1297 // fprintf(stderr,"Duplicate line\n");
1298 return;
1299 }
1300 }
1301 //fprintf(stderr,"NW_corner_latitude:%ld, NW_corner_longitude:%ld, scale_x:%ld, scale_y:%ld\n",
1302 // NW_corner_latitude,
1303 // NW_corner_longitude,
1304 // scale_x,
1305 // scale_y);
1306
1307 //fprintf(stderr,"%d,%d %d,%d\n\n",x1i,y1i,x2i,y2i);
1308
1309 // XDrawLine uses 16-bit unsigned integers
1310 // (shorts). Make sure we stay within the limits.
1311 // clip2d() should make sure of this anyway as it clips lines to
1312 // fit the window.
1313 //
1314 (void)XDrawLine(XtDisplay(w),
1315 which_pixmap,
1316 gc,
1317 l16(x1i),
1318 l16(y1i),
1319 l16(x2i),
1320 l16(y2i));
1321
1322 last_x1i = x1i;
1323 last_x2i = x2i;
1324 last_y1i = y1i;
1325 last_y2i = y2i;
1326 }
1327
1328
1329
1330
1331
1332 // Find length of a standard string of seven zeroes in the border font.
1333 // Supporting function for draw_grid() and the grid drawing functions.
get_standard_border_string_width_pixels(Widget w,int length)1334 int get_standard_border_string_width_pixels(Widget w, int length)
1335 {
1336 int string_width_pixels = 0; // Width of the unrotated seven_zeroes label string in pixels.
1337 char seven_zeroes[8] = "0000000";
1338 char five_zeroes[6] = "00000";
1339
1340 if (length==5)
1341 {
1342 string_width_pixels = get_rotated_label_text_length_pixels(w, five_zeroes, FONT_BORDER);
1343 }
1344
1345 string_width_pixels = get_rotated_label_text_length_pixels(w, seven_zeroes, FONT_BORDER);
1346 return string_width_pixels;
1347 }
1348
1349
1350
1351
1352
1353 // Find height of a standard string in the border font
1354 // Supporting function for draw_grid() and the grid drawing functions.
get_standard_border_string_height_pixels(Widget w)1355 int get_standard_border_string_height_pixels(Widget w)
1356 {
1357 int string_width_pixels = 0; // Width of the unrotated seven_zeroes label string in pixels.
1358 char one_zero[2] = "0";
1359
1360 string_width_pixels = get_rotated_label_text_height_pixels(w, one_zero, FONT_BORDER);
1361 return string_width_pixels;
1362 }
1363
1364
1365
1366
1367
1368 // Find out the width to use for the border to apply when uing a labeled grid.
1369 // Border width is determined from the height of the border font.
1370 // Supporting function for draw_grid() and the grid drawing functions.
get_border_width(Widget w)1371 int get_border_width(Widget w)
1372 {
1373 int border_width = 14; // The width of the border to draw around the
1374 // map to place labeled tick marks into
1375 // should be an even number.
1376 // The default here is overidden by size of the border font.
1377 int string_height_pixels = 0; // Height of a string in the border font in pixels
1378
1379 string_height_pixels = get_standard_border_string_height_pixels(w);
1380 // Rotated text functions used to draw the border text add some
1381 // blank space at the bottom of the text so make the border wide enough
1382 // to compensate for this.
1383 border_width = string_height_pixels + (string_height_pixels/2) + 1;
1384 // check to see if string_height_pixels is even
1385 if ((float)string_height_pixels/2.0!=floor((float)string_height_pixels/2.0))
1386 {
1387 border_width++;
1388 }
1389 // we are using draw nice string to write the metadata in the top border, so
1390 // make sure the border is wide enough for it, even if the grid labels are small.
1391 if (border_width < 14)
1392 {
1393 border_width = 14;
1394 }
1395 return border_width;
1396 }
1397
1398
1399
1400
1401
1402 // ***********************************************************
1403 //
1404 // get_horizontal_datum()
1405 //
1406 // Provides the current map datum. Current default is WGS84.
1407 // Parameters: datum, character array ponter for the string
1408 // that will be filled with the current datum
1409 // sizeof_datum, the size of the datum character array.
1410 //
1411 //***********************************************************
get_horizontal_datum(char * datum,int sizeof_datum)1412 void get_horizontal_datum(char *datum, int sizeof_datum)
1413 {
1414 char metadata_datum[6] = "WGS84"; // datum to display in metadata on top border
1415 xastir_snprintf(datum, sizeof_datum, "%s", metadata_datum);
1416 if (sizeof_datum<6)
1417 {
1418 fprintf(stderr,"Datum [%s] truncated to [%s]\n",metadata_datum,datum);
1419 }
1420 }
1421
1422
1423
1424
1425
1426 // Lat/Long coordinate system, draw lat/long lines. Called by
1427 // draw_grid() below.
1428 //
draw_complete_lat_lon_grid(Widget w)1429 void draw_complete_lat_lon_grid(Widget w)
1430 {
1431
1432 int coord;
1433 char dash[2];
1434 unsigned int x,x1,x2;
1435 unsigned int y,y1,y2;
1436 unsigned int stepsx; // spacing of grid lines
1437 unsigned int stepsy; // spacing of grid lines
1438 int coordinate_format; // Format to use for coordinates on border (e.g. decimal degrees).
1439 char grid_label[25]; // String to draw labels on grid lines
1440 int screen_width_xastir; // screen width in xastir units (1/100 of a second)
1441 // int screen_height_xastir; // screen height in xastir units (1/100 of a second)
1442 int border_width; // the width of the labeled border in pixels.
1443 int string_width_pixels = 0;// Width of a grid label string in pixels.
1444 float screen_width_degrees; // Width of the screen in degrees
1445 int log_screen_width_degrees; // Log10 of the screen width in degrees, used to scale degrees
1446 long xx2, yy2;
1447 long xx, yy;
1448 unsigned int last_label_end; // cordinate of the end of the previous label
1449 char metadata_datum[6]; // datum to display in metadata on top border
1450 char grid_label1[25]; // String to draw latlong metadata
1451 char grid_label2[25]; // String to draw latlong metadata
1452 char top_label[180]; // String to draw metadata on top border
1453
1454 if (!long_lat_grid) // We don't wish to draw a map grid
1455 {
1456 return;
1457 }
1458
1459 // convert between selected coordinate format constant and display format constants
1460 if (coordinate_system == USE_DDDDDD)
1461 {
1462 coordinate_format = CONVERT_DEC_DEG;
1463 }
1464 else if (coordinate_system == USE_DDMMSS)
1465 {
1466 coordinate_format = CONVERT_DMS_NORMAL_FORMATED;
1467 }
1468 else
1469 {
1470 coordinate_format = CONVERT_HP_NORMAL_FORMATED;
1471 }
1472 border_width = get_border_width(w);
1473 // Find xastir coordinates of upper left and lower right corners.
1474 xx = NW_corner_longitude + (border_width * scale_x);
1475 yy = NW_corner_latitude + (border_width * scale_y);
1476 xx2 = NW_corner_longitude + ((screen_width - border_width) * scale_x);
1477 yy2 = NW_corner_latitude + ((screen_height - border_width) * scale_y);
1478 screen_width_xastir = xx2 - xx;
1479 // screen_height_xastir = yy2 - yy;
1480 // Determine some parameters used in drawing the border.
1481 string_width_pixels = get_standard_border_string_width_pixels(w, 7);
1482 // 1 xastir coordinate = 1/100 of a second
1483 // 100*60*60 xastir coordinates (=360000 xastir coordinates) = 1 degree
1484 // 64800000 xastir coordinates = 180 degrees
1485 // 360000 xastir coordinates = 1 degree
1486 // scale_x * (screen_width/10) = one tenth of the screen width in xastir coordinates
1487 // scale_x number of xastir coordinates per pixel
1488 screen_width_degrees = (float)(screen_width_xastir / (float)360000);
1489 log_screen_width_degrees = log10(screen_width_degrees);
1490
1491
1492 if (draw_labeled_grid_border==TRUE)
1493 {
1494 get_horizontal_datum(metadata_datum, sizeof(metadata_datum));
1495
1496 // Put metadata in top border.
1497 // find location of upper left corner of map, convert to Lat/Long
1498 convert_lon_l2s(xx, grid_label1, sizeof(grid_label1), coordinate_format);
1499 convert_lat_l2s(yy, grid_label2, sizeof(grid_label2), coordinate_format);
1500
1501 strcpy(grid_label, grid_label1);
1502 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
1503 strcat(grid_label, " ");
1504 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
1505 strcat(grid_label, grid_label2);
1506 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
1507
1508 // find location of lower right corner of map, convert to Lat/Long
1509 convert_lon_l2s(xx2, grid_label1, sizeof(grid_label1), coordinate_format);
1510 convert_lat_l2s(yy2, grid_label2, sizeof(grid_label2), coordinate_format);
1511 //"XASTIR Map of %s (upper left) to %s %s (lower right). Lat/Long grid, %s datum. ",
1512 xastir_snprintf(top_label,
1513 sizeof(top_label),
1514 langcode("MDATA002"),
1515 grid_label,grid_label1,grid_label2,metadata_datum);
1516 draw_rotated_label_text_to_target (w, 270,
1517 border_width+2,
1518 border_width-1,
1519 sizeof(top_label),colors[0x10],top_label,FONT_BORDER,
1520 pixmap_final,
1521 outline_border_labels, colors[outline_border_labels_color]);
1522 }
1523
1524 // A crude grid spacing can be obtained from scaling one tenth of the screen width.
1525 // This works, but puts the grid lines at arbitrary increments of a degree.
1526 //stepsx = (scale_x*(screen_width/10));
1527
1528 // Setting the grid using the base 10 log of the screen width in degrees allows
1529 // both scaling the grid to the screen and spacing the grid lines at appropriately
1530 // rounded increments of a degree.
1531 //
1532 // Set default grid to 0.1 degree. This will be used when the screen width is about 1 degree.
1533 stepsx = 36000; // if (log_screen_width_degrees == 0)
1534 // Work out an appropriate grid spacing for the screen size and coordinate system.
1535 if (log_screen_width_degrees > 0)
1536 {
1537 // grid spacing is rounded to 10 degree increment.
1538 stepsx = ((int)(screen_width_degrees / (pow(10,log_screen_width_degrees)))*pow(10,log_screen_width_degrees)) * 36000;
1539 }
1540 if (log_screen_width_degrees < 0)
1541 {
1542 // Grid spacing is rounded to less than one degree.
1543 if (coordinate_system == USE_DDDDDD)
1544 {
1545 // Round to tenths or hundrethds or thousanths of a degree.
1546 stepsx = ((float)(int)(((float)screen_width_degrees / pow(10,log_screen_width_degrees)*10.0)))*pow(10,log_screen_width_degrees) * 3600;
1547 }
1548 else
1549 {
1550 // For decimal minutes or minutes and seconds.
1551 // Find screen width and log screen width in minutes.
1552 screen_width_degrees = screen_width_degrees * 60.0;
1553 log_screen_width_degrees = log10(screen_width_degrees);
1554 // round to minutes or tenths of minutes.
1555 stepsx = ((float)(int)
1556 ((float)(screen_width_degrees) / pow(10,log_screen_width_degrees) * 10.0)
1557 )
1558 * pow(10,log_screen_width_degrees) * 3600;
1559 if (log_screen_width_degrees==0)
1560 {
1561 stepsx = 600; // 6000 = 1 minute
1562 }
1563 }
1564 }
1565 // Grid should now be close to reasonable spacing for screen size, but
1566 // may need to be tuned.
1567 // Test for too tightly or too coarsely spaced grid.
1568 if (stepsx<(unsigned int)(scale_x * string_width_pixels * 1.5))
1569 {
1570 stepsx = stepsx * 2.0;
1571 }
1572 if (stepsx<(unsigned int)(scale_x * string_width_pixels * 1.5))
1573 {
1574 stepsx = stepsx * 2.0;
1575 }
1576 if (stepsx>(unsigned int)((scale_x * screen_width)/3.5))
1577 {
1578 stepsx = stepsx / 2.0;
1579 }
1580 // Handle special case of very small screen - only draw a single grid line
1581 if (screen_width < (string_width_pixels * 2))
1582 {
1583 stepsx = (scale_x*(screen_width/2));
1584 }
1585 // Make sure we don't pass an erronous stepsx of 0 on.
1586 if (stepsx==0)
1587 {
1588 stepsx=36000;
1589 }
1590
1591 // Use the same grid spacing for both latitude and longitude grids.
1592 // We could use a scaling factor related to the screen height width ratio here.
1593 stepsy = stepsx;
1594
1595 // protect against division by zero
1596 if (scale_x==0)
1597 {
1598 scale_x = 1;
1599 }
1600 if (scale_y==0)
1601 {
1602 scale_y = 1;
1603 }
1604
1605 // Now draw and label the grid.
1606 // Draw vertical longitude lines
1607 if (NW_corner_latitude >= 0)
1608 {
1609 y1 = 0;
1610 }
1611 else
1612 {
1613 y1 = -NW_corner_latitude/scale_y;
1614 }
1615
1616 y2 = (180*60*60*100-NW_corner_latitude)/scale_y;
1617
1618 if (y2 > (unsigned int)screen_height)
1619 {
1620 y2 = screen_height-1;
1621 }
1622
1623 coord = NW_corner_longitude+stepsx-(NW_corner_longitude%stepsx);
1624 if (coord < 0)
1625 {
1626 coord = 0;
1627 }
1628
1629 last_label_end = 0;
1630 for (; coord < SE_corner_longitude && coord <= 360*60*60*100; coord += stepsx)
1631 {
1632
1633 x = (coord-NW_corner_longitude)/scale_x;
1634
1635 if ((coord%(648000*100)) == 0)
1636 {
1637 (void)XSetLineAttributes (XtDisplay (w),
1638 gc_tint,
1639 1,
1640 LineSolid,
1641 CapButt,
1642 JoinMiter);
1643 (void)XDrawLine (XtDisplay (w),
1644 pixmap_final,
1645 gc_tint,
1646 l16(x),
1647 l16(y1),
1648 l16(x),
1649 l16(y2));
1650 (void)XSetLineAttributes (XtDisplay (w),
1651 gc_tint,
1652 1,
1653 LineOnOffDash,
1654 CapButt,
1655 JoinMiter);
1656 continue; // Go to next iteration of for loop
1657 }
1658 else if ((coord%(72000*100)) == 0)
1659 {
1660 dash[0] = dash[1] = 8;
1661 (void)XSetDashes (XtDisplay (w), gc_tint, 0, dash, 2);
1662 }
1663 else if ((coord%(7200*100)) == 0)
1664 {
1665 dash[0] = dash[1] = 4;
1666 (void)XSetDashes (XtDisplay (w), gc_tint, 0, dash, 2);
1667 }
1668 else if ((coord%(300*100)) == 0)
1669 {
1670 dash[0] = dash[1] = 2;
1671 (void)XSetDashes (XtDisplay (w), gc_tint, 0, dash, 2);
1672 }
1673
1674 (void)XDrawLine (XtDisplay (w),
1675 pixmap_final,
1676 gc_tint,
1677 l16(x),
1678 l16(y1),
1679 l16(x),
1680 l16(y2));
1681
1682 if (draw_labeled_grid_border==TRUE)
1683 {
1684 // Label the longitudes in lower border.
1685 convert_lon_l2s(coord, grid_label, sizeof(grid_label), coordinate_format);
1686 if (log_screen_width_degrees > 0 && strlen(grid_label) > 5)
1687 {
1688 // truncate the grid_label string
1689 if (coordinate_system==USE_DDMMMM)
1690 {
1691 // Add ' and move EW and null characters forward.
1692 grid_label[strlen(grid_label)-5] = '\'';
1693 grid_label[strlen(grid_label)-4] = grid_label[strlen(grid_label)-1];
1694 grid_label[strlen(grid_label)-3] = grid_label[strlen(grid_label)];
1695 }
1696 else
1697 {
1698 // Move EW and null characters forward.
1699 grid_label[strlen(grid_label)-5] = grid_label[strlen(grid_label)-1];
1700 grid_label[strlen(grid_label)-4] = grid_label[strlen(grid_label)];
1701 }
1702 }
1703 string_width_pixels = get_rotated_label_text_length_pixels(w, grid_label, FONT_BORDER);
1704 // test for overlap of label with previously printed label
1705 if (x > last_label_end + 3 && x < (unsigned int)(screen_width - string_width_pixels))
1706 {
1707 draw_rotated_label_text_to_target (w, 270,
1708 x,
1709 screen_height,
1710 sizeof(grid_label),colors[0x09],grid_label,FONT_BORDER,
1711 pixmap_final,
1712 outline_border_labels, colors[outline_border_labels_color]);
1713 last_label_end = x + string_width_pixels;
1714 }
1715 }
1716 }
1717
1718 // Draw horizontal latitude lines.
1719 last_label_end = 0;
1720 if (NW_corner_longitude >= 0)
1721 {
1722 x1 = 0;
1723 }
1724 else
1725 {
1726 x1 = -NW_corner_longitude/scale_x;
1727 }
1728
1729 x2 = (360*60*60*100-NW_corner_longitude)/scale_x;
1730 if (x2 > (unsigned int)screen_width)
1731 {
1732 x2 = screen_width-1;
1733 }
1734
1735 coord = NW_corner_latitude+stepsy-(NW_corner_latitude%stepsy);
1736 if (coord < 0)
1737 {
1738 coord = 0;
1739 }
1740
1741 for (; coord < SE_corner_latitude && coord <= 180*60*60*100; coord += stepsy)
1742 {
1743
1744 y = (coord-NW_corner_latitude)/scale_y;
1745
1746 if ((coord%(324000*100)) == 0)
1747 {
1748 (void)XSetLineAttributes (XtDisplay (w),
1749 gc_tint,
1750 1,
1751 LineSolid,
1752 CapButt,
1753 JoinMiter);
1754 (void)XDrawLine (XtDisplay (w),
1755 pixmap_final,
1756 gc_tint,
1757 l16(x1),
1758 l16(y),
1759 l16(x2),
1760 l16(y));
1761 (void)XSetLineAttributes (XtDisplay (w),
1762 gc_tint,
1763 1,
1764 LineOnOffDash,
1765 CapButt,
1766 JoinMiter);
1767 continue; // Go to next iteration of for loop
1768 }
1769 else if ((coord%(36000*100)) == 0)
1770 {
1771 dash[0] = dash[1] = 8;
1772 (void)XSetDashes (XtDisplay (w), gc_tint, 4, dash, 2);
1773 }
1774 else if ((coord%(3600*100)) == 0)
1775 {
1776 dash[0] = dash[1] = 4;
1777 (void)XSetDashes (XtDisplay (w), gc_tint, 2, dash, 2);
1778 }
1779 else if ((coord%(150*100)) == 0)
1780 {
1781 dash[0] = dash[1] = 2;
1782 (void)XSetDashes (XtDisplay (w), gc_tint, 1, dash, 2);
1783 }
1784
1785 (void)XDrawLine (XtDisplay (w),
1786 pixmap_final,
1787 gc_tint,
1788 l16(x1),
1789 l16(y),
1790 l16(x2),
1791 l16(y));
1792
1793 if (draw_labeled_grid_border==TRUE)
1794 {
1795 // label the latitudes on left and right borders
1796 // (unlike UTM where easting before northing order is important)
1797 convert_lat_l2s(coord, grid_label, sizeof(grid_label), coordinate_format);
1798 if (log_screen_width_degrees > 0 && strlen(grid_label) > 5)
1799 {
1800 // truncate the grid_label string
1801 if (coordinate_system==USE_DDMMMM)
1802 {
1803 // Add ' and move EW and null characters forward.
1804 grid_label[strlen(grid_label)-5] = '\'';
1805 grid_label[strlen(grid_label)-4] = grid_label[strlen(grid_label)-1];
1806 grid_label[strlen(grid_label)-3] = grid_label[strlen(grid_label)];
1807 }
1808 else
1809 {
1810 // Move EW and null characters forward.
1811 grid_label[strlen(grid_label)-5] = grid_label[strlen(grid_label)-1];
1812 grid_label[strlen(grid_label)-4] = grid_label[strlen(grid_label)];
1813 }
1814 }
1815 string_width_pixels = get_rotated_label_text_length_pixels(w, grid_label, FONT_BORDER);
1816 // check to make sure we aren't overwriting the previous label text
1817 if ((y > last_label_end+3) && (y > (unsigned int)string_width_pixels))
1818 {
1819 draw_rotated_label_text_to_target (w, 180,
1820 screen_width,
1821 y,
1822 sizeof(grid_label),colors[0x09],grid_label,FONT_BORDER,
1823 pixmap_final,
1824 outline_border_labels, colors[outline_border_labels_color]);
1825 draw_rotated_label_text_to_target (w, 180,
1826 border_width,
1827 y,
1828 sizeof(grid_label),colors[0x09],grid_label,FONT_BORDER,
1829 pixmap_final,
1830 outline_border_labels, colors[outline_border_labels_color]);
1831 last_label_end = y + string_width_pixels;
1832 }
1833 }
1834 }
1835 } // End of draw_complete_lat_lon_grid()
1836
1837
1838
1839
1840
1841 // Draw the major zones for UTM and MGRS. Called by draw_grid()
1842 // below.
1843 //
1844 // These are based off 6-degree lat/long lines, with a few irregular
1845 // zones that have to be special-cased. This part of the code
1846 // handles the irregular zones in SW Norway (31V/32V) and the
1847 // regions near Svalbard (31X/33X/35X/37X) just fine.
1848
1849 // These are based off the central meridian running up the middle of
1850 // each zone (3 degrees from either side of the standard six-degree
1851 // zones). Even the irregular zones key off the same medians. UTM
1852 // grids are defined in terms of meters instead of lat/long, so they
1853 // don't line up with the left/right edges of the zones or with the
1854 // longitude lines.
1855 //
1856 // According to Peter Dana (Geographer's Craft web pages), even when
1857 // the major grid boundaries have been shifted, the meridian used
1858 // for drawing the subgrids is still based on six-degree boundaries
1859 // (as if the major grid hadn't been shifted at all). That means we
1860 // are drawing the subgrids correctly as it stands now for the
1861 // irregular grids (31V/32V/31X/33X/35X/37X). The irregular zones
1862 // have sizes of 3/9/12 degrees (width) instead of 6 degrees.
1863 //
1864 // UTM NOTES: 84 degrees North to 80 degrees South. 60 zones, each
1865 // covering six (6) degrees of longitude. Each zone extends three
1866 // degrees eastward and three degrees westward from its central
1867 // meridian. Zones are numbered consecutively west to east from the
1868 // 180 degree meridian. From 84 degrees North and 80 degrees South
1869 // to the respective poles, the Universal Polar Stereographic (UPS)
1870 // is used.
1871 //
1872 // For MGRS and UTM-Special grid only:
1873 // UTM Zone 32 has been widened to 9� (at the expense of zone 31)
1874 // between latitudes 56� and 64� (band V) to accommodate southwest
1875 // Norway. Thus zone 32 extends westwards to 3�E in the North Sea.
1876 // Similarly, between 72� and 84� (band X), zones 33 and 35 have
1877 // been widened to 12� to accommodate Svalbard. To compensate for
1878 // these 12� wide zones, zones 31 and 37 are widened to 9� and zones
1879 // 32, 34, and 36 are eliminated. Thus the W and E boundaries of
1880 // zones are 31: 0 - 9 E, 33: 9 - 21 E, 35: 21 - 33 E and 37: 33 -
1881 // 42 E.
1882 //
1883 // UTM is depending on the ellipsoid and the datum used. For our
1884 // purposes, we're always using WGS84 ellipsoid and datum, so it's a
1885 // non-issue.
1886 //
1887 // Horizontal bands corresponding to the NATO UTM/UPS lettering:
1888 // Zones go from A (south pole) to Z (north pole). South of -80 are
1889 // zones A/B, north of +84 are zones Y/Z. "I" and "O" are not used.
1890 // Zones from C to W are 8 degrees high. Zone X is 12 degrees high.
1891 //
1892 // We need these NATO letters or a N/S designator in order to
1893 // specify which hemisphere the UTM coordinates are in. Often, the
1894 // same coordinates can appear in either hemisphere. Some computer
1895 // software uses +/- to designate northings instead of N/S or the
1896 // NATO lettered bands.
1897 //
1898 // UPS system is used at the poles instead of UTM. UPS uses a false
1899 // northing and easting of 2,000,000 meters.
1900 //
1901 // An arbitrary false northing of 10,000,000 at the equator is used
1902 // for southern latitudes only. Northern latitudes assume the
1903 // equator northing is at zero. An arbitrary false easting of
1904 // 500,000 is along the meridian of each zone (3 degrees from each
1905 // side). The lettered grid lines are necessary due to some
1906 // coordinates being valid in both the northern and the southern
1907 // hemisphere.
1908 //
1909 // Y/Z 84N to 90N (UPS System) false N/E = 2,000,000
1910 // X 72N to 84N (12 degrees latitude, equator=0)
1911 // W 64N to 72N ( 8 degrees latitude, equator=0)
1912 // V 56N to 64N ( 8 degrees latitude, equator=0)
1913 // U 48N to 56N ( 8 degrees latitude, equator=0)
1914 // T 40N to 48N ( 8 degrees latitude, equator=0)
1915 // S 32N to 40N ( 8 degrees latitude, equator=0)
1916 // R 24N to 32N ( 8 degrees latitude, equator=0)
1917 // Q 16N to 24N ( 8 degrees latitude, equator=0)
1918 // P 8N to 16N ( 8 degrees latitude, equator=0)
1919 // N 0N to 8N ( 8 degrees latitude, equator=0)
1920 // M 0S to 8S ( 8 degrees latitude, equator=10,000,000)
1921 // L 8S to 16S ( 8 degrees latitude, equator=10,000,000)
1922 // K 16S to 24S ( 8 degrees latitude, equator=10,000,000)
1923 // J 24S to 32S ( 8 degrees latitude, equator=10,000,000)
1924 // H 32S to 40S ( 8 degrees latitude, equator=10,000,000)
1925 // G 40S to 48S ( 8 degrees latitude, equator=10,000,000)
1926 // F 48S to 56S ( 8 degrees latitude, equator=10,000,000)
1927 // E 56S to 64S ( 8 degrees latitude, equator=10,000,000)
1928 // D 64S to 72S ( 8 degrees latitude, equator=10,000,000)
1929 // C 72S to 80S ( 8 degrees latitude, equator=10,000,000)
1930 // A/B 80S to 90S (UPS System) false N/E = 2,000,000
1931 //
draw_major_utm_mgrs_grid(Widget w)1932 void draw_major_utm_mgrs_grid(Widget w)
1933 {
1934 int ii;
1935
1936 // need to move metadata to its own function and put it up after grids have been drawn.
1937 //*******
1938 // variables for metadata in grid border
1939 long xx2, yy2; // coordinates of screen corners used for metadata
1940 int border_width; // Width of the border to draw labels into.
1941 double easting, northing; // Values used in border metadata.
1942 int x, y; // Screen coordinates for border labels.
1943 char zone_str[10];
1944 char zone_str2[10];
1945 char metadata_datum[6];
1946 char grid_label[25]; // String to draw labels on grid lines
1947 char grid_label1[25]; // String to draw latlong metadata
1948 char top_label[180]; // String to draw metadata on top border
1949
1950 // variables to support components of MGRS strings in the metadata
1951 char mgrs_zone[4] = " "; // MGRS zone letter
1952 char mgrs_eastingL[3] = " ";
1953 char mgrs_northingL[3] = " ";
1954 unsigned int int_utmEasting;
1955 unsigned int int_utmNorthing;
1956 char mgrs_space_string[4] = " ";
1957 // int mgrs_single_digraph = FALSE; // mgrs_ul_digraph and mgrs_ur_digraph are the same.
1958 char mgrs_ul_digraph[3] = " "; // MGRS digraph for upper left corner of screen
1959 char mgrs_lr_digraph[3] = " "; // MGRS digraph for lower right corner of screen
1960
1961
1962 //fprintf(stderr,"draw_major_utm_mgrs_grid start\n");
1963
1964 if (!long_lat_grid) // We don't wish to draw a map grid
1965 {
1966 return;
1967 }
1968
1969 // Vertical lines:
1970
1971 // Draw the vertical vectors (except for the irregular regions
1972 // and the prime meridian). The polar areas only have two zones
1973 // each, so we don't want to draw through those areas.
1974
1975 for (ii = -180; ii < 0; ii += 6)
1976 {
1977 draw_vector_ll(w, -80.0, (float)ii, 84.0, (float)ii, gc_tint, pixmap_final, 0);
1978 }
1979 for (ii = 42; ii <= 180; ii += 6)
1980 {
1981 draw_vector_ll(w, -80.0, (float)ii, 84.0, (float)ii, gc_tint, pixmap_final, 0);
1982 }
1983
1984 // Draw the short vertical vectors in the polar regions
1985 draw_vector_ll(w, -90.0, -180.0, -80.0, -180.0, gc_tint, pixmap_final, 0);
1986 draw_vector_ll(w, -90.0, 180.0, -80.0, 180.0, gc_tint, pixmap_final, 0);
1987 draw_vector_ll(w, 84.0, -180.0, 90.0, -180.0, gc_tint, pixmap_final, 0);
1988 draw_vector_ll(w, 84.0, 180.0, 90.0, 180.0, gc_tint, pixmap_final, 0);
1989
1990 if (coordinate_system == USE_UTM_SPECIAL
1991 || coordinate_system == USE_MGRS)
1992 {
1993 // For MGRS, we need to draw irregular zones in certain
1994 // areas.
1995
1996 // Draw the partial vectors from 80S to the irregular region
1997 draw_vector_ll(w, -80.0, 6.0, 56.0, 6.0, gc_tint, pixmap_final, 0);
1998 draw_vector_ll(w, -80.0, 12.0, 72.0, 12.0, gc_tint, pixmap_final, 0);
1999 draw_vector_ll(w, -80.0, 18.0, 72.0, 18.0, gc_tint, pixmap_final, 0);
2000 draw_vector_ll(w, -80.0, 24.0, 72.0, 24.0, gc_tint, pixmap_final, 0);
2001 draw_vector_ll(w, -80.0, 30.0, 72.0, 30.0, gc_tint, pixmap_final, 0);
2002 draw_vector_ll(w, -80.0, 36.0, 72.0, 36.0, gc_tint, pixmap_final, 0);
2003
2004 // Draw the short vertical vectors in the irregular region
2005 draw_vector_ll(w, 56.0, 3.0, 64.0, 3.0, gc_tint, pixmap_final, 0);
2006 draw_vector_ll(w, 64.0, 6.0, 72.0, 6.0, gc_tint, pixmap_final, 0);
2007 draw_vector_ll(w, 72.0, 9.0, 84.0, 9.0, gc_tint, pixmap_final, 0);
2008 draw_vector_ll(w, 72.0, 21.0, 84.0, 21.0, gc_tint, pixmap_final, 0);
2009 draw_vector_ll(w, 72.0, 33.0, 84.0, 33.0, gc_tint, pixmap_final, 0);
2010
2011 // Draw the short vertical vectors above the irregular region
2012 draw_vector_ll(w, 84.0, 6.0, 84.0, 6.0, gc_tint, pixmap_final, 0);
2013 draw_vector_ll(w, 84.0, 12.0, 84.0, 12.0, gc_tint, pixmap_final, 0);
2014 draw_vector_ll(w, 84.0, 18.0, 84.0, 18.0, gc_tint, pixmap_final, 0);
2015 draw_vector_ll(w, 84.0, 24.0, 84.0, 24.0, gc_tint, pixmap_final, 0);
2016 draw_vector_ll(w, 84.0, 30.0, 84.0, 30.0, gc_tint, pixmap_final, 0);
2017 draw_vector_ll(w, 84.0, 36.0, 84.0, 36.0, gc_tint, pixmap_final, 0);
2018 }
2019 else
2020 {
2021 // Draw normal zone boundaries used for civilian UTM
2022 // grid.
2023 for (ii = 6; ii < 42; ii += 6)
2024 {
2025 draw_vector_ll(w, -80.0, (float)ii, 84.0, (float)ii, gc_tint, pixmap_final, 0);
2026 }
2027 }
2028
2029
2030 // Horizontal lines:
2031
2032 // Draw the 8 degree spaced lines, except for the equator
2033 for (ii = -80; ii < 0; ii += 8)
2034 {
2035 draw_vector_ll(w, (float)ii, -180.0, (float)ii, 180.0, gc_tint, pixmap_final, 0);
2036 }
2037 // Draw the 8 degree spaced lines
2038 for (ii = 8; ii <= 72; ii += 8)
2039 {
2040 draw_vector_ll(w, (float)ii, -180.0, (float)ii, 180.0, gc_tint, pixmap_final, 0);
2041 }
2042
2043 // Draw the one 12 degree spaced line
2044 draw_vector_ll(w, 84.0, -180.0, 84.0, 180.0, gc_tint, pixmap_final, 0);
2045
2046 // Draw the pole lines
2047 draw_vector_ll(w, -90.0, -180.0, -90.0, 180.0, gc_tint, pixmap_final, 0);
2048 draw_vector_ll(w, 90.0, -180.0, 90.0, 180.0, gc_tint, pixmap_final, 0);
2049
2050 // Set to solid line for the equator. Make it extra wide as
2051 // well.
2052 (void)XSetLineAttributes (XtDisplay (w), gc_tint, 3, LineSolid, CapButt,JoinMiter);
2053
2054 // Draw the equator as a solid line
2055 draw_vector_ll(w, 0.0, -180.0, 0.0, 180.0, gc_tint, pixmap_final, 0);
2056
2057 (void)XSetLineAttributes (XtDisplay (w), gc_tint, 2, LineSolid, CapButt,JoinMiter);
2058
2059 // Draw the prime meridian in the same manner
2060 draw_vector_ll(w, -80.0, 0.0, 84.0, 0.0, gc_tint, pixmap_final, 0);
2061
2062 // add metadata and labels
2063 if (draw_labeled_grid_border==TRUE && scale_x > 3000)
2064 {
2065 // Determine the width of the border
2066 border_width = get_border_width(w);
2067 // Find out what the map datum is.
2068 get_horizontal_datum(metadata_datum, sizeof(metadata_datum));
2069
2070 // Put metadata in top border.
2071 // find location of upper left corner of map, convert to UTM
2072 xx2 = NW_corner_longitude + (border_width * scale_x);
2073 yy2 = NW_corner_latitude + (border_width * scale_y);
2074 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str),
2075 xx2, yy2);
2076 if (coordinate_system == USE_MGRS)
2077 {
2078 convert_xastir_to_MGRS_str_components(mgrs_zone, strlen(mgrs_zone),
2079 mgrs_eastingL, sizeof(mgrs_eastingL),
2080 mgrs_northingL, sizeof(mgrs_northingL),
2081 &int_utmEasting, &int_utmNorthing,
2082 xx2, yy2,
2083 0, mgrs_space_string, strlen(mgrs_space_string));
2084 xastir_snprintf(mgrs_ul_digraph, sizeof(mgrs_ul_digraph),
2085 "%c%c", mgrs_eastingL[0], mgrs_northingL[0]);
2086 xastir_snprintf(grid_label,
2087 sizeof(grid_label),
2088 "%s %s %05.0f %05.0f",
2089 mgrs_zone,mgrs_ul_digraph,(float)int_utmEasting,(float)int_utmNorthing);
2090 }
2091 else
2092 {
2093 char easting_str[10];
2094 char northing_str[10];
2095
2096 xastir_snprintf(easting_str, sizeof(easting_str), " %07.0f", easting);
2097 xastir_snprintf(northing_str, sizeof(northing_str), " %07.0f", northing);
2098 strcpy(grid_label, zone_str);
2099 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
2100 strcat(grid_label, easting_str);
2101 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
2102 strcat(grid_label, northing_str);
2103 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
2104 }
2105 // find location of lower right corner of map, convert to UTM
2106 xx2 = NW_corner_longitude + ((screen_width - border_width) * scale_x);
2107 yy2 = NW_corner_latitude + ((screen_height - border_width) * scale_y);
2108 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str),
2109 xx2, yy2);
2110 if (coordinate_system == USE_MGRS)
2111 {
2112 convert_xastir_to_MGRS_str_components(mgrs_zone, strlen(mgrs_zone),
2113 mgrs_eastingL, sizeof(mgrs_eastingL),
2114 mgrs_northingL, sizeof(mgrs_northingL),
2115 &int_utmEasting, &int_utmNorthing,
2116 xx2, yy2,
2117 0, mgrs_space_string, strlen(mgrs_space_string));
2118 xastir_snprintf(mgrs_lr_digraph, sizeof(mgrs_lr_digraph),
2119 "%c%c", mgrs_eastingL[0], mgrs_northingL[0]);
2120 xastir_snprintf(grid_label1,
2121 sizeof(grid_label1),
2122 "%s %s %05.0f %05.0f",
2123 mgrs_zone,mgrs_lr_digraph,(float)int_utmEasting,(float)int_utmNorthing);
2124 if (strcmp(mgrs_lr_digraph,mgrs_ul_digraph)==0)
2125 {
2126 // mgrs_single_digraph = TRUE; // mgrs_ul_digraph and mgrs_ur_digraph are the same.
2127 }
2128 else
2129 {
2130 // mgrs_single_digraph = FALSE; // mgrs_ul_digraph and mgrs_ur_digraph are the same.
2131 }
2132 }
2133 else
2134 {
2135 char easting_str[10];
2136 char northing_str[10];
2137
2138 xastir_snprintf(easting_str, sizeof(easting_str), " %07.0f", easting);
2139 xastir_snprintf(northing_str, sizeof(northing_str), " %07.0f", northing);
2140 strcpy(grid_label1, zone_str);
2141 grid_label1[sizeof(grid_label1)-1] = '\0'; // Terminate string
2142 strcat(grid_label1, easting_str);
2143 grid_label1[sizeof(grid_label1)-1] = '\0'; // Terminate string
2144 strcat(grid_label1, northing_str);
2145 grid_label1[sizeof(grid_label1)-1] = '\0'; // Terminate string
2146 }
2147 // Write metadata on upper border of map.
2148 //"XASTIR Map of %s (upper left) to %s (lower right). UTM zones, %s datum. ",
2149 xastir_snprintf(top_label,
2150 sizeof(top_label),
2151 langcode("MDATA003"),
2152 grid_label,grid_label1,metadata_datum);
2153 draw_rotated_label_text_to_target (w, 270,
2154 border_width+2,
2155 border_width-1,
2156 sizeof(top_label),colors[0x10],top_label,FONT_BORDER,
2157 pixmap_final,
2158 outline_border_labels, colors[outline_border_labels_color]);
2159 // Crudely identify zone boundaries by
2160 // iterating across bottom border.
2161 xastir_snprintf(zone_str2,
2162 sizeof(zone_str2),
2163 "%s"," ");
2164 for (x=1; x<(screen_width - border_width); x++)
2165 {
2166 xx2 = NW_corner_longitude + (x * scale_x);
2167 yy2 = NW_corner_latitude + ((screen_height - border_width) * scale_y);
2168 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str),
2169 xx2, yy2);
2170 zone_str[strlen(zone_str)-1] = '\0';
2171 if (strcmp(zone_str,zone_str2) !=0)
2172 {
2173 draw_rotated_label_text_to_target (w, 270,
2174 x + 1,
2175 screen_height,
2176 sizeof(zone_str),colors[0x10],zone_str,FONT_BORDER,
2177 pixmap_final,
2178 outline_border_labels, colors[outline_border_labels_color]);
2179 }
2180 xastir_snprintf(zone_str2,
2181 sizeof(zone_str2),
2182 "%s",zone_str);
2183 }
2184 // Crudely identify zone letters by iterating down left border
2185 for (y=(border_width*2); y<(screen_height - border_width); y++)
2186 {
2187 xx2 = NW_corner_longitude + (border_width * scale_x);
2188 yy2 = NW_corner_latitude + (y * scale_y);
2189 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str),
2190 xx2, yy2);
2191 zone_str[0] = zone_str[strlen(zone_str)-1];
2192 zone_str[1] = '\0';
2193 if (strcmp(zone_str,zone_str2) !=0)
2194 {
2195 draw_rotated_label_text_to_target (w, 270,
2196 1,
2197 y,
2198 sizeof(zone_str),colors[0x10],zone_str,FONT_BORDER,
2199 pixmap_final,
2200 outline_border_labels, colors[outline_border_labels_color]);
2201 }
2202 xastir_snprintf(zone_str2,
2203 sizeof(zone_str2),
2204 "%s",zone_str);
2205 }
2206 } // end if draw labeled border
2207
2208 // Set the line width and style in the GC to 1 pixel wide for
2209 // drawing the smaller grid
2210 (void)XSetLineAttributes (XtDisplay (w), gc_tint, 1, LineOnOffDash, CapButt,JoinMiter);
2211
2212 //fprintf(stderr,"draw_major_utm_mgrs_grid end\n");
2213
2214 } // End of draw_major_utm_mgrs_grid()
2215
2216
2217
2218
2219
2220 // This is the function which actually draws a minor UTM grid.
2221 // Called by draw_minor_utm_mgrs_grid() function below.
2222 // draw_minor_utm_mgrs_grid() is the function which calculates the
2223 // grid points.
2224 //
actually_draw_utm_minor_grid(Widget w)2225 void actually_draw_utm_minor_grid(Widget w)
2226 {
2227
2228
2229 int border_width; // Width of the border to draw labels into.
2230 int numberofzones = 0; // number of elements in utm_grid.zone[] that are used
2231 int Zone;
2232 int ii;
2233 int easting_color; // Colors for the grid labels
2234 int northing_color;
2235 int zone_color; // zone label color
2236 int label_on_left; // if true, draw northing labels on left
2237 long xx, yy, xx2, yy2;
2238 char zone_str[10];
2239 char zone_str2[10];
2240 double easting, northing;
2241 int short_width_pixels = 0; // Width of an unrotated string of five_zeroes in the border font in pixels.
2242 int string_width_pixels = 0;// Width of an unrotated string of seven_zeroes in the border font in pixels.
2243 char metadata_datum[6];
2244 char grid_label[25]; // String to draw labels on grid lines
2245 char grid_label1[25]; // String to draw latlong metadata
2246 char top_label[180]; // String to draw metadata on top border
2247 int grid_spacing_pixels; // Spacing of fine grid lines in pixels.
2248 int bottom_point; // utm_grid.zone[].col[].npoints can extend past the
2249 // bottom of the screen, this is the lowest point in the
2250 // points array that is on the screen.
2251 int skip_alternate_label; // Skip alternate easting and northing labels
2252 // if they would overlap on the
2253 // display.
2254 int last_line_labeled; // Marks lines that were labeled
2255 // when alternate lines are not
2256 // being labeled.
2257
2258 // variables to support components of MGRS strings
2259 char mgrs_zone[4] = " "; // MGRS zone letter
2260 char mgrs_eastingL[3] = " ";
2261 char mgrs_northingL[3] = " ";
2262 unsigned int int_utmEasting;
2263 unsigned int int_utmNorthing;
2264 char mgrs_space_string[4] = " ";
2265 int mgrs_single_digraph = FALSE; // mgrs_ul_digraph and mgrs_ur_digraph are the same.
2266 char mgrs_ul_digraph[3] = " "; // MGRS digraph for upper left corner of screen
2267 char mgrs_lr_digraph[3] = " "; // MGRS digraph for lower right corner of screen
2268
2269 if (!long_lat_grid) // We don't wish to draw a map grid
2270 {
2271 return;
2272 }
2273
2274 // OLD: Draw grid in dashed white lines.
2275 // NEW: Tint the lines as they go along, making them appear
2276 // no matter what color is underneath.
2277 (void)XSetForeground(XtDisplay(w), gc_tint, colors[0x27]);
2278
2279 // Note: npoints can be negative here! Make sure our code
2280 // checks for that. Initially npoints was an unsigned int.
2281 // Changed it to an int so that we can get and check for
2282 // negative values, bypassing segfaults.
2283 //
2284 numberofzones = 0;
2285
2286 // Determine the width of the border
2287 border_width = get_border_width(w);
2288 // Determine some parameters used in drawing the border.
2289 string_width_pixels = get_standard_border_string_width_pixels(w, 7);
2290 short_width_pixels = get_standard_border_string_width_pixels(w,5);
2291
2292 for (Zone=0; Zone < UTM_GRID_MAX_ZONES; Zone++)
2293 {
2294
2295 if (utm_grid.zone[Zone].ncols > 0)
2296 {
2297 // find out how many zones are actually drawn on the map
2298 numberofzones++;
2299 }
2300 }
2301
2302 for (Zone=0; Zone < UTM_GRID_MAX_ZONES; Zone++)
2303 {
2304
2305 for (ii=0; ii < (int)utm_grid.zone[Zone].ncols; ii++)
2306 {
2307 if (utm_grid.zone[Zone].col[ii].npoints > 1)
2308 {
2309
2310 // We need to check for points that are more
2311 // than +/- 16383. If we have any, it can cause
2312 // X11 to lock up for a while drawing lots of
2313 // extra lines, due to bugs in X11. We do that
2314 // checking above with xx and yy.
2315 //
2316 (void)XDrawLines(XtDisplay(w),
2317 pixmap_final,
2318 gc_tint,
2319 utm_grid.zone[Zone].col[ii].points,
2320 l16(utm_grid.zone[Zone].col[ii].npoints),
2321 CoordModeOrigin);
2322 }
2323 }
2324
2325 for (ii=0; ii < (int)utm_grid.zone[Zone].nrows; ii++)
2326 {
2327 if (utm_grid.zone[Zone].row[ii].npoints > 1)
2328 {
2329
2330 // We need to check for points that are more
2331 // than +/- 16383. If we have any, it can cause
2332 // X11 to lock up for a while drawing lots of
2333 // extra lines, due to bugs in X11. We do that
2334 // checking above with xx and yy.
2335 //
2336 (void)XDrawLines(XtDisplay(w),
2337 pixmap_final,
2338 gc_tint,
2339 utm_grid.zone[Zone].row[ii].points,
2340 l16(utm_grid.zone[Zone].row[ii].npoints),
2341 CoordModeOrigin);
2342 }
2343 }
2344
2345 // Check each of the 4 possible utm_grid.zone array elements
2346 // that might contain a grid, and label the grid if it exists.
2347 if (utm_grid.zone[Zone].nrows>0 && utm_grid.zone[Zone].ncols>0)
2348 {
2349 if (draw_labeled_grid_border==TRUE)
2350 {
2351 // Label the UTM grid on the border.
2352 // Since the coordinate of the current mouse pointer position is
2353 // continually updated, labeling the grid is primarily for the
2354 // purpose of printing maps and saving screenshots.
2355 //
2356 // ******* Doesn't work properly near poles when 3 zones are on screen
2357 // ******* (e.g. 13,14,15) - overlaps northings for 14 and 15.
2358 // ******* Doesn't clearly distinguish one zone with 2 lettered rows
2359 // ******* (e.g. 18T,18U) needs color distinction between northings
2360 // ******* to indicate which northings are in which lettered row.
2361 //
2362
2363 // Default labels for just one zone on screen are black text for
2364 // zone at lower left corner, eastings on bottom, and northings
2365 // at right.
2366 // Idea is to normally start at the lower left corner
2367 // users can then easily follow left to right to get easting,
2368 // and bottom to top to get northing.
2369 // For two zones, second zone uses blue text for eastings and northings.
2370 easting_color = 0x08; // black text
2371 northing_color = 0x08; // black text
2372 zone_color = 0x08; // black text
2373 // 0x09=blue (0x0e=yellow works well with outline, but not without).
2374 label_on_left = FALSE;
2375
2376 // Find out what the map datum is.
2377 get_horizontal_datum(metadata_datum, sizeof(metadata_datum));
2378
2379 if (numberofzones>1)
2380 {
2381 // check to see if the upper left and lower left corners are in the same zone
2382 // if not, label the upper left corner
2383 xx = (border_width * scale_x) + NW_corner_longitude;
2384 yy = ((screen_height - border_width) * scale_y) + NW_corner_latitude;
2385 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx, yy);
2386 yy = (border_width * scale_y) + NW_corner_latitude;
2387 convert_xastir_to_UTM(&easting, &northing, zone_str2, sizeof(zone_str2), xx, yy);
2388 if (strcmp(zone_str,zone_str2)!=0)
2389 {
2390 xastir_snprintf(grid_label,
2391 sizeof(grid_label),
2392 "%s",
2393 zone_str2);
2394 //draw_nice_string(w,pixmap_final,0,
2395 // border_width+2,
2396 // (2*border_width)+2,
2397 // grid_label,
2398 // 0x10,zone_color,(int)strlen(grid_label));
2399 draw_rotated_label_text_to_target (w, 270,
2400 border_width+2,
2401 (2*border_width)+2,
2402 sizeof(grid_label),colors[zone_color],grid_label,FONT_BORDER,
2403 pixmap_final,
2404 1, colors[0x0f]);
2405 }
2406 if (strcmp(zone_str,zone_str2)!=0)
2407 {
2408 xastir_snprintf(grid_label,
2409 sizeof(grid_label),
2410 "%s",
2411 zone_str);
2412 draw_rotated_label_text_to_target (w, 270,
2413 border_width+2,
2414 screen_height - (2*border_width) - 2,
2415 sizeof(grid_label),colors[zone_color],grid_label,FONT_BORDER,
2416 pixmap_final,
2417 1, colors[0x0f]);
2418 }
2419 zone_color = 0x09;
2420 // likewise for upper and lower right corners
2421 xx = ((screen_width - border_width) * scale_x) + NW_corner_longitude;
2422 yy = ((screen_height - border_width) * scale_y) + NW_corner_latitude;
2423 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx, yy);
2424 yy = (border_width * scale_y) + NW_corner_latitude;
2425 convert_xastir_to_UTM(&easting, &northing, zone_str2, sizeof(zone_str2), xx, yy);
2426 if (strcmp(zone_str,zone_str2)!=0)
2427 {
2428 xastir_snprintf(grid_label,
2429 sizeof(grid_label),
2430 "%s",
2431 zone_str2);
2432 //draw_nice_string(w,pixmap_final,0,
2433 // screen_width - (border_width * 3) ,
2434 // (2*border_width)+2,
2435 // grid_label,
2436 // 0x10,zone_color,(int)strlen(grid_label));
2437 draw_rotated_label_text_to_target (w, 270,
2438 screen_width - (border_width * 3),
2439 (2*border_width)+2,
2440 sizeof(grid_label),colors[zone_color],grid_label,FONT_BORDER,
2441 pixmap_final,
2442 1, colors[0x0f]);
2443 }
2444 if (strcmp(zone_str,zone_str2)!=0)
2445 {
2446 xastir_snprintf(grid_label,
2447 sizeof(grid_label),
2448 "%s",
2449 zone_str);
2450 draw_rotated_label_text_to_target (w, 270,
2451 screen_width - (border_width * 3),
2452 screen_height - (2*border_width) - 2,
2453 sizeof(grid_label),colors[zone_color],grid_label,FONT_BORDER,
2454 pixmap_final,
2455 1, colors[0x0f]);
2456 }
2457
2458 // are we currently the same zone as the upper left corner
2459 // if so, we need to place the northing labels on the left side
2460 xx = (utm_grid.zone[Zone].col[0].points[0].x * scale_x) + NW_corner_longitude;
2461 yy = (utm_grid.zone[Zone].col[0].points[0].y * scale_y) + NW_corner_latitude;
2462 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx, yy);
2463 convert_xastir_to_UTM(&easting, &northing, zone_str2, sizeof(zone_str2), NW_corner_longitude, NW_corner_latitude);
2464 if (strcmp(zone_str,zone_str2)==0)
2465 {
2466 northing_color = 0x08; // 0x08 = black, same as lower left easting
2467 label_on_left = TRUE;
2468 }
2469
2470 }
2471 // check to see if there is a horizontal boundary
2472 // compare xone of upper left and lower left corners
2473 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), NW_corner_longitude, NW_corner_latitude);
2474
2475 // Overwrite defaults as appropriate and
2476 // label zones differently if more than one appears on the screen.
2477
2478 if (Zone > 0)
2479 {
2480 // write the zone label on the bottom border
2481 zone_color = 0x09; // blue
2482 easting_color = 0x09; // blue
2483 northing_color = 0x09; // blue
2484 xx2 = utm_grid.zone[Zone].col[0].points[0].x;
2485 xx = (xx2 * scale_x) + NW_corner_longitude;
2486 yy2 = utm_grid.zone[Zone].col[0].points[utm_grid.zone[Zone].col[0].npoints-1].y;
2487 yy = (yy2 * scale_y) + NW_corner_latitude;
2488 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx,yy);
2489 xastir_snprintf(grid_label,
2490 sizeof(grid_label),
2491 "%s",
2492 zone_str);
2493 draw_rotated_label_text_to_target (w, 270,
2494 xx2,
2495 screen_height,
2496 sizeof(grid_label),colors[easting_color],grid_label,FONT_BORDER,
2497 pixmap_final,
2498 outline_border_labels, colors[outline_border_labels_color]);
2499 //draw_nice_string(w,pixmap_final,0,
2500 // xx2,
2501 // screen_height - 2,
2502 // grid_label,
2503 // 0x10,zone_color,(int)strlen(grid_label));
2504 }
2505
2506 if (Zone==0)
2507 {
2508 // write the zone of the lower left corner of the map
2509 xx = (border_width * scale_x) + NW_corner_longitude;
2510 yy = ((screen_height - border_width) * scale_y) + NW_corner_latitude;
2511 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx, yy);
2512 xastir_snprintf(grid_label,
2513 sizeof(grid_label),
2514 "%s",
2515 zone_str);
2516 draw_rotated_label_text_to_target (w, 270,
2517 1,
2518 screen_height,
2519 sizeof(grid_label),colors[easting_color],grid_label,FONT_BORDER,
2520 pixmap_final,
2521 outline_border_labels, colors[outline_border_labels_color]);
2522 //draw_nice_string(w,pixmap_final,0,
2523 // 1,
2524 // screen_height - 2,
2525 // grid_label,
2526 // 0x10,0x20,(int)strlen(grid_label));
2527 }
2528 // Put metadata in top border.
2529 // find location of upper left corner of map, convert to UTM
2530 xx2 = NW_corner_longitude + (border_width * scale_x);
2531 yy2 = NW_corner_latitude + (border_width * scale_y);
2532 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str),
2533 xx2, yy2);
2534 if (coordinate_system == USE_MGRS)
2535 {
2536 convert_xastir_to_MGRS_str_components(mgrs_zone, strlen(mgrs_zone),
2537 mgrs_eastingL, sizeof(mgrs_eastingL),
2538 mgrs_northingL, sizeof(mgrs_northingL),
2539 &int_utmEasting, &int_utmNorthing,
2540 xx2, yy2,
2541 0, mgrs_space_string, strlen(mgrs_space_string));
2542 xastir_snprintf(mgrs_ul_digraph, sizeof(mgrs_ul_digraph),
2543 "%c%c", mgrs_eastingL[0], mgrs_northingL[0]);
2544 xastir_snprintf(grid_label,
2545 sizeof(grid_label),
2546 "%s %s %05.0f %05.0f",
2547 mgrs_zone,mgrs_ul_digraph,(float)int_utmEasting,(float)int_utmNorthing);
2548 }
2549 else
2550 {
2551 char easting_str[10];
2552 char northing_str[10];
2553
2554 xastir_snprintf(easting_str, sizeof(easting_str), " %07.0f", easting);
2555 xastir_snprintf(northing_str, sizeof(northing_str), " %07.0f", northing);
2556 strcpy(grid_label, zone_str);
2557 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
2558 strcat(grid_label, easting_str);
2559 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
2560 strcat(grid_label, northing_str);
2561 grid_label[sizeof(grid_label)-1] = '\0'; // Terminate string
2562 }
2563 // find location of lower right corner of map, convert to UTM
2564 xx2 = NW_corner_longitude + ((screen_width - border_width) * scale_x);
2565 yy2 = NW_corner_latitude + ((screen_height - border_width) * scale_y);
2566 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str),
2567 xx2, yy2);
2568 if (coordinate_system == USE_MGRS)
2569 {
2570 convert_xastir_to_MGRS_str_components(mgrs_zone, strlen(mgrs_zone),
2571 mgrs_eastingL, sizeof(mgrs_eastingL),
2572 mgrs_northingL, sizeof(mgrs_northingL),
2573 &int_utmEasting, &int_utmNorthing,
2574 xx2, yy2,
2575 0, mgrs_space_string, strlen(mgrs_space_string));
2576 xastir_snprintf(mgrs_lr_digraph, sizeof(mgrs_lr_digraph),
2577 "%c%c", mgrs_eastingL[0], mgrs_northingL[0]);
2578 xastir_snprintf(grid_label1,
2579 sizeof(grid_label1),
2580 "%s %s %05.0f %05.0f",
2581 mgrs_zone,mgrs_lr_digraph,(float)int_utmEasting,(float)int_utmNorthing);
2582 if (strcmp(mgrs_lr_digraph,mgrs_ul_digraph)==0)
2583 {
2584 mgrs_single_digraph = TRUE; // mgrs_ul_digraph and mgrs_ur_digraph are the same.
2585 }
2586 else
2587 {
2588 mgrs_single_digraph = FALSE; // mgrs_ul_digraph and mgrs_ur_digraph are the same.
2589 }
2590 }
2591 else
2592 {
2593 char easting_str[10];
2594 char northing_str[10];
2595
2596 xastir_snprintf(easting_str, sizeof(easting_str), " %07.0f", easting);
2597 xastir_snprintf(northing_str, sizeof(northing_str), " %07.0f", northing);
2598 strcpy(grid_label1, zone_str);
2599 grid_label1[sizeof(grid_label1)-1] = '\0'; // Terminate string
2600 strcat(grid_label1, easting_str);
2601 grid_label1[sizeof(grid_label1)-1] = '\0'; // Terminate string
2602 strcat(grid_label1, northing_str);
2603 grid_label1[sizeof(grid_label1)-1] = '\0'; // Terminate string
2604 }
2605 //"XASTIR Map of %s (upper left) to %s (lower right). UTM %d m grid, %s datum. ",
2606 xastir_snprintf(top_label,
2607 sizeof(top_label),
2608 langcode("MDATA001"),
2609 grid_label,grid_label1,utm_grid_spacing_m,metadata_datum);
2610 //draw_nice_string(w,pixmap_final,0,
2611 // border_width+2,
2612 // border_width-2,
2613 // top_label,
2614 // 0x10,0x20,(int)strlen(top_label));
2615 draw_rotated_label_text_to_target (w, 270,
2616 border_width+2,
2617 border_width-1,
2618 sizeof(top_label),colors[0x10],top_label,FONT_BORDER,
2619 pixmap_final,
2620 outline_border_labels, colors[outline_border_labels_color]);
2621
2622 // deterimne whether the easting and northing strings will fit
2623 // in a grid box, or whether easting strings in adjacent boxes
2624 // will overlap (so that alternate strings can be skipped).
2625 if (utm_grid.zone[Zone].ncols > 1)
2626 {
2627 // find out the number of pixels beteen two grid lines
2628 grid_spacing_pixels =
2629 utm_grid.zone[Zone].col[1].points[0].x -
2630 utm_grid.zone[Zone].col[0].points[0].x;
2631
2632 if (grid_spacing_pixels == 0)
2633 {
2634 grid_spacing_pixels = -1; // Skip
2635 }
2636
2637 }
2638 else
2639 {
2640 // only one column in this zone, skip alternate doesn't matter
2641 grid_spacing_pixels = -1;
2642 }
2643
2644 // Is truncated easting or northing larger than grid spacing?
2645 // If so, skip alternate labels
2646 // short_width_pixels+2 seems to work well.
2647 if (short_width_pixels+2>grid_spacing_pixels)
2648 {
2649 skip_alternate_label = TRUE;
2650 }
2651 else
2652 {
2653 skip_alternate_label = FALSE;
2654 }
2655
2656 // Label the grid lines on the border.
2657 // Put easting along the bottom for easier correct ordering of easting and northing
2658 // by people who are reading the map.
2659 last_line_labeled = FALSE;
2660 for (ii=1; ii < (int)utm_grid.zone[Zone].ncols; ii++)
2661 {
2662 // label meridianal grid lines with easting
2663
2664 if (utm_grid.zone[Zone].col[ii].npoints > 1)
2665 {
2666
2667 // adjust up in case npoints goes far below the screen
2668 if (grid_spacing_pixels == 0)
2669 {
2670 continue; // Go to next iteration of for loop
2671 }
2672
2673 bottom_point = (int)(screen_height/grid_spacing_pixels);
2674
2675 if (bottom_point >= utm_grid.zone[Zone].col[ii].npoints)
2676 {
2677 bottom_point = utm_grid.zone[Zone].col[ii].npoints - 1;
2678 }
2679 if (skip_alternate_label==TRUE && last_line_labeled==TRUE)
2680 {
2681 last_line_labeled = FALSE;
2682 }
2683 else
2684 {
2685 xx = (utm_grid.zone[Zone].col[ii].points[bottom_point].x * scale_x) + NW_corner_longitude;
2686 yy = (utm_grid.zone[Zone].col[ii].points[bottom_point].y * scale_y) + NW_corner_latitude;
2687 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx, yy);
2688 // To display full precision to one meter, use:
2689 //xastir_snprintf(grid_label,
2690 // sizeof(grid_label),
2691 // "%06.0f0",
2692 // (float)((utm_grid_spacing_m/10) * roundf(easting/(utm_grid_spacing_m))));
2693 //
2694 // Divide easting by utm_grid_spacing to make sure the line is labeled
2695 // correctly, and not a few meters off, and truncate to at least 100 m.
2696 xastir_snprintf(grid_label,
2697 sizeof(grid_label),
2698 "%05.0f",
2699 (float)((utm_grid_spacing_m/100) * roundf(easting/(utm_grid_spacing_m))));
2700 // truncate the label to an appropriate level of precision for the grid
2701 if (utm_grid_spacing_m ==1000)
2702 {
2703 grid_label[4] = ' ';
2704 }
2705 if (utm_grid_spacing_m ==10000)
2706 {
2707 grid_label[3] = ' ';
2708 grid_label[4] = ' ';
2709 }
2710 if (utm_grid_spacing_m ==100000)
2711 {
2712 grid_label[2] = ' ';
2713 grid_label[3] = ' ';
2714 grid_label[4] = ' ';
2715 }
2716 if (coordinate_system == USE_MGRS)
2717 {
2718 convert_xastir_to_MGRS_str_components(mgrs_zone, strlen(mgrs_zone),
2719 mgrs_eastingL, sizeof(mgrs_eastingL),
2720 mgrs_northingL, sizeof(mgrs_northingL),
2721 &int_utmEasting, &int_utmNorthing,
2722 xx, yy,
2723 0, mgrs_space_string, strlen(mgrs_space_string));
2724 grid_label[0] = mgrs_eastingL[0];
2725 grid_label[1] = mgrs_northingL[0];
2726 if (mgrs_single_digraph==FALSE)
2727 {
2728 grid_label[1] = '_';
2729 }
2730 }
2731 // draw each number at the bottom of the screen just to the right of the
2732 // relevant grid line at its location at the bottom of the screen
2733 //draw_nice_string(w,pixmap_final,0,
2734 // utm_grid.zone[Zone].col[i].points[bottom_point].x+1,
2735 // screen_height-2,
2736 // grid_label,
2737 // 0x10,easting_color,(int)strlen(grid_label));
2738
2739 // Don't overwrite the zone label, half the seven zeros string should give it room.
2740 // Don't draw the label if it will go off the left edge fo the screen.
2741 if ((utm_grid.zone[Zone].col[ii].points[bottom_point].x+1 > (string_width_pixels/2))
2742 && (utm_grid.zone[Zone].col[ii].points[bottom_point].x+1 < (screen_width - string_width_pixels))
2743 )
2744 {
2745 // ok to draw the label
2746 last_line_labeled = TRUE;
2747 draw_rotated_label_text_to_target (w, 270,
2748 utm_grid.zone[Zone].col[ii].points[bottom_point].x+1,
2749 screen_height,
2750 sizeof(grid_label),colors[easting_color],grid_label,FONT_BORDER,
2751 pixmap_final,
2752 outline_border_labels, colors[outline_border_labels_color]);
2753 }
2754 }
2755 }
2756 }
2757 last_line_labeled = FALSE;
2758 // put northing along the right border, again for easier correct ordering of easting and northing.
2759 for (ii=0; ii < (int)utm_grid.zone[Zone].nrows; ii++)
2760 {
2761 // label latitudinal grid lines with northing
2762 if (utm_grid.zone[Zone].row[ii].npoints > 1)
2763 {
2764 if (skip_alternate_label==TRUE && last_line_labeled==TRUE)
2765 {
2766 last_line_labeled = FALSE;
2767 }
2768 else
2769 {
2770 if (label_on_left==TRUE)
2771 {
2772 xx = (utm_grid.zone[Zone].row[ii].points[0].x * scale_x) + NW_corner_longitude;
2773 }
2774 else
2775 {
2776 xx = (utm_grid.zone[Zone].row[ii].points[utm_grid.zone[Zone].row[ii].npoints-1].x * scale_x) + NW_corner_longitude;
2777 }
2778 yy = (utm_grid.zone[Zone].row[ii].points[utm_grid.zone[Zone].row[ii].npoints-1].y * scale_y) + NW_corner_latitude;
2779 convert_xastir_to_UTM(&easting, &northing, zone_str, sizeof(zone_str), xx, yy);
2780 // To display to full 1 meter precision use:
2781 //xastir_snprintf(grid_label,
2782 // sizeof(grid_label),
2783 // "%06.0f0",
2784 // (float)((utm_grid_spacing_m/10) * roundf(northing/(utm_grid_spacing_m))));
2785 //
2786 // Divide northing by utm grid spacing to make sure the line is labeled correctly
2787 // and displays zeroes in its least significant digits, and truncate to 100 m
2788 xastir_snprintf(grid_label,
2789 sizeof(grid_label),
2790 "%05.0f",
2791 (float)((utm_grid_spacing_m/100) * roundf(northing/(utm_grid_spacing_m))));
2792 if (utm_grid_spacing_m ==1000)
2793 {
2794 grid_label[4] = ' ';
2795 }
2796 if (utm_grid_spacing_m ==10000)
2797 {
2798 grid_label[3] = ' ';
2799 grid_label[4] = ' ';
2800 }
2801 if (utm_grid_spacing_m ==100000)
2802 {
2803 grid_label[2] = ' ';
2804 grid_label[3] = ' ';
2805 grid_label[4] = ' ';
2806 }
2807 if (coordinate_system == USE_MGRS)
2808 {
2809 convert_xastir_to_MGRS_str_components(mgrs_zone, strlen(mgrs_zone),
2810 mgrs_eastingL, 3,
2811 mgrs_northingL, 3,
2812 &int_utmEasting, &int_utmNorthing,
2813 xx, yy,
2814 0, mgrs_space_string, strlen(mgrs_space_string));
2815 grid_label[0] = mgrs_eastingL[0];
2816 if (mgrs_single_digraph==FALSE)
2817 {
2818 grid_label[0] = '_';
2819 }
2820 grid_label[1] = mgrs_northingL[0];
2821 }
2822 // Draw northing labels.
2823 // Draw each number just above the relevant grid line along the right side
2824 // of the screen. Don't write in the bottom border or off the top of the screen.
2825 if (label_on_left==TRUE)
2826 {
2827 // label northings on left border
2828 // don't overwrite the zone designator in the lower left border
2829 if ((utm_grid.zone[Zone].row[ii].points[0].y < (screen_height - border_width))
2830 &&
2831 (utm_grid.zone[Zone].row[ii].points[0].y > (string_width_pixels))
2832 )
2833 {
2834 last_line_labeled = TRUE;
2835 draw_rotated_label_text_to_target (w, 180,
2836 border_width,
2837 utm_grid.zone[Zone].row[ii].points[0].y,
2838 sizeof(grid_label),colors[northing_color],grid_label,FONT_BORDER,
2839 pixmap_final,
2840 outline_border_labels, colors[outline_border_labels_color]);
2841 }
2842 }
2843 else
2844 {
2845 if (((utm_grid.zone[Zone].row[ii].points[utm_grid.zone[Zone].row[ii].npoints-1].y-1)
2846 < (screen_height - border_width))
2847 &&
2848 ((utm_grid.zone[Zone].row[ii].points[utm_grid.zone[Zone].row[ii].npoints-1].y-1)
2849 > (string_width_pixels))
2850 )
2851 {
2852 // label northings on right border
2853 last_line_labeled = TRUE;
2854 draw_rotated_label_text_to_target (w, 180,
2855 screen_width,
2856 utm_grid.zone[Zone].row[ii].points[utm_grid.zone[Zone].row[ii].npoints-1].y-1,
2857 sizeof(grid_label),colors[northing_color],grid_label,FONT_BORDER,
2858 pixmap_final,
2859 outline_border_labels, colors[outline_border_labels_color]);
2860 }
2861 }
2862 }
2863 }
2864 } // for i=0 to nrows
2865 } // if draw labeled grid border
2866 } // if utm_grid.zone[Zone] is non-empty
2867 } // for each zone in utm_grid.zone
2868 } // End of actually_draw_utm_minor_grid() function
2869
2870
2871
2872
2873
2874 // Calculate the minor UTM grids. Called by draw_grid() below.
2875 // This function calculates and caches a within-zone UTM grid
2876 // for the current map view if one does not allready exist, it
2877 // then calls actually_draw_utm_minor_grid() function above to do
2878 // the drawing once the grid has been calculated. Zone boundaries
2879 // are drawn separately by draw_major_utm_mgrs_grid().
2880 //
2881 // This routine appears to draw most of the UTM/UPS grid ok, with
2882 // the exceptions of:
2883 //
2884 // 1) Sometimes fails to draw vertical lines nearest zone
2885 // boundaries.
2886 // 2) Lines connect across zone boundaries in an incorrect manner,
2887 // jumping up one grid interval across the boundary.
2888 // 3) Segfaults near the special zone intersections as you zoom in.
2889 //
2890 // The code currently creates a col and row array per zone visible,
2891 // with XPoints malloced that contain the grid intersections in
2892 // screen coordinates. If the screen is zoomed or panned they are
2893 // recalculated.
2894 //
2895 // Perhaps we could do the same but with lat/long coordinates in the
2896 // future so that we'd only have to recalculate when a new Zone came
2897 // into view. We'd use the lat/long vector drawing programs above
2898 // then, with a possible slowdown due to more calculations if we're
2899 // not moving around.
2900 //
2901 // Returns: 0 if successful or nothing to draw
2902 // 1 if malloc error
2903 // 2 if iterations error
2904 // 3 if out of zones
2905 // 4 if realloc failure
2906 //
draw_minor_utm_mgrs_grid(Widget w)2907 int draw_minor_utm_mgrs_grid(Widget w)
2908 {
2909
2910 long xx, yy, xx1, yy1;
2911 double e[4], n[4];
2912 char place_str[10], zone_str[10];
2913 int done = 0;
2914 int z1, z2, Zone, col, col_point, row, row_point, row_point_start;
2915 int iterations = 0;
2916 int finished_with_current_zone = 0;
2917 int ii, jj;
2918 float slope;
2919 int coordinate_system_backup = coordinate_system;
2920
2921
2922 col = 0;
2923 row = 0;
2924 col_point = 0;
2925 row_point = 0;
2926 row_point_start = 0;
2927 Zone = 0;
2928
2929 // Set up for drawing zone grid(s)
2930 if (scale_x < 15)
2931 {
2932 utm_grid_spacing_m = 100;
2933 }
2934 else if (scale_x < 150)
2935 {
2936 utm_grid_spacing_m = 1000;
2937 }
2938 else if (scale_x < 1500)
2939 {
2940 utm_grid_spacing_m = 10000;
2941 }
2942 else if (scale_x < 3000)
2943 {
2944 utm_grid_spacing_m = 100000;
2945 }
2946 else
2947 {
2948 utm_grid_spacing_m = 0;
2949 // All done! Don't draw the minor grids. Major grids
2950 // have already been drawn by this point.
2951 return(0);
2952 }
2953
2954 // Check hash to see if utm_grid is already set up
2955 if (utm_grid.hash.ul_x == NW_corner_longitude &&
2956 utm_grid.hash.ul_y == NW_corner_latitude &&
2957 utm_grid.hash.lr_x == SE_corner_longitude &&
2958 utm_grid.hash.lr_y == SE_corner_latitude)
2959 {
2960
2961 // XPoint arrays are already set up. Go draw the grid.
2962 actually_draw_utm_minor_grid(w);
2963
2964 return(0);
2965 }
2966
2967
2968 // If we get to this point, we need to re-create the minor UTM/MGRS
2969 // grids as they haven't been set up yet or they don't match the
2970 // current view.
2971
2972
2973 // Clear the minor UTM/MGRS grid arrays. Alloc space for
2974 // the points in the grid structure.
2975 if (utm_grid_clear(1))
2976 {
2977 // If we got here, we had a problem with malloc's
2978 return(1);
2979 }
2980
2981 // Find top left point of current view
2982 xx = NW_corner_longitude;
2983 yy = NW_corner_latitude;
2984
2985 // Note that the minor grid depends on the STANDARD six degree
2986 // UTM zones, not the UTM-Special/MGRS zones. Force our
2987 // calculations to use the standard zones.
2988 coordinate_system = USE_UTM;
2989 convert_xastir_to_UTM(&e[0], &n[0], place_str, sizeof(place_str), xx, yy);
2990 coordinate_system = coordinate_system_backup;
2991
2992 n[0] += UTM_GRID_EQUATOR; // To work in southern hemisphere
2993
2994
2995 // Select starting point, NW corner of NW zone
2996
2997 // Move the coordinates to the nearest subgrid intersection,
2998 // based on our current grid spacing. The grid intersection
2999 // we calculate here is northwest of our view's northwest
3000 // corner.
3001 e[0] /= utm_grid_spacing_m;
3002 e[0] = (double)((int)e[0] * utm_grid_spacing_m);
3003 n[0] /= utm_grid_spacing_m;
3004 n[0] = (double)((int)n[0] * utm_grid_spacing_m);
3005 n[0] += utm_grid_spacing_m;
3006
3007
3008 //WE7U
3009 // It appears that the horizontal grid lines get messed up in cases
3010 // where the top horizontal line isn't in view on it's left end.
3011 // That's a major clue! Read the comment below (again with a "WE7U"
3012 // tag). The problem occurs at the point where we copy the last
3013 // point from the previous grid over to the first point of a new
3014 // grid. That can cause us to be off by one, as for the grid on the
3015 // left, the top horizontal line _is_ in view on the left. We end
3016 // up connecting the wrong horizontal lines together because of this
3017 // mismatch, but again, only if the top horizontal line on the left
3018 // grid is above the current view.
3019 //
3020 // It also appears that the vertical lines that are missing in some
3021 // cases are on the right of the zone boundary. This is probably
3022 // because the top of that line doesn't go to the top of the view.
3023 // On views where it does, the line is drawn. I assume this is
3024 // because we're drawing from NW corner to the right, and then down,
3025 // which would cause that line to be skipped if it's not present on
3026 // the first line?
3027
3028
3029 e[1] = e[0];
3030 n[1] = n[0];
3031
3032
3033
3034
3035
3036 /////////////////////////////////////////////////////////////////////
3037 /////////////////////////////////////////////////////////////////////
3038 // Start filling in the row/column arrays of grid intersections
3039 while (!done)
3040 {
3041 XPoint *temp_point;
3042
3043
3044 // Here's our escape in case we get stuck in this loop.
3045 // We can go through this loop multiple times for each
3046 // zone though, depending on our grid spacing. 64 rows * 64
3047 // colums * 8 points each = 32768, which gives us our upper
3048 // limit.
3049 if (iterations++ > 32768)
3050 {
3051 fprintf(stderr,
3052 "draw_minor_utm_mgrs_grid() looped too many times, escaping.\n");
3053 utm_grid_clear(1);
3054 return(2);
3055 }
3056
3057
3058 if (finished_with_current_zone)
3059 {
3060 // Set up to compute the next zone
3061
3062 xx = NW_corner_longitude + ((utm_grid.zone[Zone].boundary_x + 1) * scale_x);
3063
3064 yy = NW_corner_latitude;
3065
3066 // Note that the minor grid depends on the STANDARD six
3067 // degree UTM zones, not the UTM-Special/MGRS zones.
3068 // Force our calculations to use the standard zones.
3069 coordinate_system = USE_UTM;
3070 convert_xastir_to_UTM(&e[0], &n[0], place_str, sizeof(place_str), xx, yy);
3071 coordinate_system = coordinate_system_backup;
3072
3073 n[0] += UTM_GRID_EQUATOR; // To work in southern hemisphere
3074
3075 // Fix the coordinates to the nearest subgrid intersection based on
3076 // our current grid spacing. Bump both the easting and northing up
3077 // by one subgrid.
3078 e[0] /= utm_grid_spacing_m;
3079 e[0] = (double)((int)e[0] * utm_grid_spacing_m);
3080 e[0] += utm_grid_spacing_m;
3081 n[0] /= utm_grid_spacing_m;
3082 n[0] = (double)((int)n[0] * utm_grid_spacing_m);
3083 n[0] += utm_grid_spacing_m;
3084
3085 e[1] = e[0];
3086 n[1] = n[0];
3087
3088 #ifdef UTM_DEBUG
3089 fprintf(stderr,"\nFinished Zone=%d\n", Zone);
3090 #endif
3091
3092 // We're all done with the current zone. Increment
3093 // to the next zone and set up to calculate its
3094 // points.
3095 Zone++;
3096
3097 #ifdef UTM_DEBUG
3098 fprintf(stderr,"\nstarting Zone=%d, row_point_start=1\n", Zone);
3099 #endif
3100
3101 row_point = row_point_start = 1;
3102 col = row = col_point = 0;
3103 finished_with_current_zone = 0;
3104
3105 if (Zone >= UTM_GRID_MAX_ZONES)
3106 {
3107 fprintf(stderr,"Error: Zone=%d: out of zones!\n", Zone);
3108 Zone = 0;
3109 done = 1;
3110 utm_grid_clear(1);
3111 return(3);
3112 }
3113 } // End of if(finished_with_current_zone)
3114
3115 // Note that the minor grid depends on the STANDARD six
3116 // degree UTM zones, not the UTM-Special/MGRS zones. Force
3117 // our calculations to use the standard zones.
3118 coordinate_system = USE_UTM;
3119 convert_UTM_to_xastir(e[1], n[1]-UTM_GRID_EQUATOR, place_str, &xx, &yy);
3120 coordinate_system = coordinate_system_backup;
3121
3122 xx1 = xx; // Save
3123 yy1 = yy; // Save
3124
3125 // Note that the minor grid depends on the STANDARD six
3126 // degree UTM zones, not the UTM-Special/MGRS zones. Force
3127 // our calculations to use the standard zones.
3128 coordinate_system = USE_UTM;
3129 convert_xastir_to_UTM(&e[2], &n[2], zone_str, sizeof(zone_str), xx, yy);
3130 coordinate_system = coordinate_system_backup;
3131
3132 n[2] += UTM_GRID_EQUATOR;
3133 xx = (xx - NW_corner_longitude) / scale_x;
3134 yy = (yy - NW_corner_latitude) / scale_y;
3135
3136 // Not all columns (and maybe rows) will start at point
3137 // 0
3138 if (utm_grid.zone[Zone].col[col].firstpoint == UTM_GRID_RC_EMPTY)
3139 {
3140 utm_grid.zone[Zone].col[col].firstpoint = l16(col_point);
3141 #ifdef UTM_DEBUG
3142 fprintf(stderr,"col[%d] started at point %d\n", col, col_point);
3143 #endif
3144 }
3145 if (utm_grid.zone[Zone].row[row].firstpoint == UTM_GRID_RC_EMPTY)
3146 {
3147 utm_grid.zone[Zone].row[row].firstpoint = l16(row_point);
3148 #ifdef UTM_DEBUG
3149 fprintf(stderr,"row[%d] started at point %d\n", row, row_point);
3150 #endif
3151 }
3152
3153 // Check to see if we need to alloc more space for
3154 // column points
3155 ii = utm_grid.zone[Zone].col[col].npoints +
3156 utm_grid.zone[Zone].col[col].firstpoint + 1;
3157 if (ii > utm_grid.zone[Zone].col[col].nalloced)
3158 {
3159 #ifdef UTM_DEBUG_ALLOC
3160 fprintf(stderr,"i=%d n=%d realloc(utm_grid.zone[%d].col[%d].points, ",
3161 ii, utm_grid.zone[Zone].col[col].nalloced, Zone, col);
3162 #endif
3163 ii = ((ii / UTM_GRID_DEF_NALLOCED) + 1) * UTM_GRID_DEF_NALLOCED;
3164 #ifdef UTM_DEBUG_ALLOC
3165 fprintf(stderr,"%d)\n", ii);
3166 #endif
3167
3168 temp_point = realloc(utm_grid.zone[Zone].col[col].points,
3169 ii * sizeof(XPoint));
3170
3171 if (temp_point)
3172 {
3173 utm_grid.zone[Zone].col[col].points = temp_point;
3174 utm_grid.zone[Zone].col[col].nalloced = ii;
3175 }
3176 else
3177 {
3178 puts("realloc FAILED!");
3179 (void)utm_grid_clear(1); // Clear arrays and allocate memory for points
3180 return(4);
3181 }
3182 }
3183
3184 // Check to see if we need to alloc more space for row
3185 // points
3186 ii = utm_grid.zone[Zone].row[row].npoints +
3187 utm_grid.zone[Zone].row[row].firstpoint + 1;
3188 if (ii > utm_grid.zone[Zone].row[row].nalloced)
3189 {
3190 #ifdef UTM_DEBUG_ALLOC
3191 fprintf(stderr,"i=%d n=%d realloc(utm_grid.zone[%d].row[%d].points, ",
3192 ii, utm_grid.zone[Zone].row[row].nalloced, Zone, row);
3193 #endif
3194 ii = ((ii / UTM_GRID_DEF_NALLOCED) + 1) * UTM_GRID_DEF_NALLOCED;
3195 #ifdef UTM_DEBUG_ALLOC
3196 fprintf(stderr,"%d)\n", ii);
3197 #endif
3198
3199 temp_point = realloc(utm_grid.zone[Zone].row[row].points,
3200 ii * sizeof(XPoint));
3201
3202 if (temp_point)
3203 {
3204 utm_grid.zone[Zone].row[row].points = temp_point;
3205 utm_grid.zone[Zone].row[row].nalloced = ii;
3206 }
3207 else
3208 {
3209 puts("realloc FAILED!");
3210 (void)utm_grid_clear(1); // Clear arrays and allocate memory for points
3211 return(4);
3212 }
3213 }
3214
3215 // Here we check to see whether we are inserting points
3216 // that are greater than about +/- 32767. If so,
3217 // truncate at that. This prevents XDrawLines() from
3218 // going nuts and drawing hundreds of extra lines.
3219 //
3220 xx = l16(xx);
3221 yy = l16(yy);
3222
3223 utm_grid.zone[Zone].col[col].points[col_point].x = l16(xx);
3224 utm_grid.zone[Zone].col[col].points[col_point].y = l16(yy);
3225 utm_grid.zone[Zone].col[col].npoints++;
3226 utm_grid.zone[Zone].row[row].points[row_point].x = l16(xx);
3227 utm_grid.zone[Zone].row[row].points[row_point].y = l16(yy);
3228 utm_grid.zone[Zone].row[row].npoints++;
3229
3230 #ifdef UTM_DEBUG
3231 fprintf(stderr,"utm_grid.zone[%d].col[%d].points[%d] = [ %ld,%ld ] npoints=%d\n",
3232 Zone, col, col_point, xx, yy, utm_grid.zone[Zone].col[col].npoints);
3233 fprintf(stderr,"utm_grid.zone[%d].row[%d].points[%d] = [ %ld,%ld ]\n",
3234 Zone, row, row_point, xx, yy);
3235 #endif
3236
3237 col++;
3238 row_point++;
3239 if (col >= UTM_GRID_MAX_COLS_ROWS)
3240 {
3241 finished_with_current_zone++;
3242 }
3243
3244 z1 = atoi(place_str);
3245 z2 = atoi(zone_str);
3246 if (z1 != z2 || xx > screen_width) // We hit a boundary
3247 {
3248
3249 #ifdef UTM_DEBUG_VERB
3250 if (z1 != z2)
3251 {
3252 fprintf(stderr,"Zone boundary! \"%s\" -> \"%s\"\n", place_str, zone_str);
3253 }
3254 else
3255 {
3256 puts("Screen boundary!");
3257 }
3258 #endif
3259
3260 //#warning
3261 //#warning I suspect that I should not use just col for the following.
3262 //#warning
3263 if (col-2 >= 0)
3264 slope = (float)(yy - utm_grid.zone[Zone].col[col-2].points[col_point].y) /
3265 (float)(xx - utm_grid.zone[Zone].col[col-2].points[col_point].x + 0.001);
3266 else
3267 {
3268 slope = 0.0;
3269 }
3270
3271 if (xx > screen_width)
3272 {
3273 xx1 = screen_width;
3274 }
3275 else
3276 {
3277
3278 // 360,000 Xastir units equals one degree. This
3279 // code appears to be adjusting xx1 to a major
3280 // zone edge.
3281 xx1 = (xx1 / (6 * 360000)) * 6 * 360000;
3282 xx1 = (xx1 - NW_corner_longitude) / scale_x;
3283 }
3284
3285 utm_grid.zone[Zone].boundary_x = xx1;
3286 yy1 = yy - (xx - xx1) * slope;
3287
3288 #ifdef UTM_DEBUG
3289 fprintf(stderr,"_tm_grid.zone[%d].col[%d].points[%d] = [ %ld,%ld ]\n",
3290 Zone, col-1, col_point, xx1, yy1);
3291 fprintf(stderr,"_tm_grid.zone[%d].row[%d].points[%d] = [ %ld,%ld ]\n",
3292 Zone, row, row_point-1, xx1, yy1);
3293 #endif
3294
3295 if (col-1 >= 0 && row_point-1 >= 0)
3296 {
3297 utm_grid.zone[Zone].col[col-1].points[col_point].x = l16(xx1);
3298 utm_grid.zone[Zone].col[col-1].points[col_point].y = l16(yy1);
3299 utm_grid.zone[Zone].row[row].points[row_point-1].x = l16(xx1);
3300 utm_grid.zone[Zone].row[row].points[row_point-1].y = l16(yy1);
3301 if (z1 != z2 && Zone+1 < UTM_GRID_MAX_ZONES)
3302 {
3303 // copy over last points to start off new
3304 // zone
3305 #ifdef UTM_DEBUG
3306 fprintf(stderr,"ztm_grid.zone[%d].row[%d].points[%d] = [ %ld,%ld ]\n",
3307 Zone+1, row, 0, xx1, yy1);
3308 #endif
3309
3310 //WE7U
3311 // This is where we can end up linking up/down one grid width
3312 // between zones!!! Without it though, we end up have a blank
3313 // section to the right of the zone boundary. Perhaps we could do
3314 // this here, but when we get the next points calculated, we could
3315 // check to see if we're off by about one grid width in the vertical
3316 // direction. If so, shift the initial point by that amount?
3317 //
3318 // Another possibility might be to draw bottom-to-top if in northern
3319 // hemisphere, and top-to-bottom if in southern hemisphere. That
3320 // way we'd have the max amount of lines present when we start, and
3321 // some might peter out as we draw along N/S. Looking at the
3322 // southern hemisphere right now though, that method doesn't appear
3323 // to work. We get the same problems there even though we're
3324 // drawing top to bottom.
3325 //
3326 utm_grid.zone[Zone+1].row[row].points[0].x = l16(xx1);
3327 utm_grid.zone[Zone+1].row[row].points[0].y = l16(yy1);
3328 utm_grid.zone[Zone+1].row[row].firstpoint = 0;
3329 utm_grid.zone[Zone+1].row[row].npoints = 1;
3330 }
3331 }
3332
3333
3334 // Check last built row to see if it is all off
3335 // screen
3336 finished_with_current_zone++; // Assume we're done with this zone
3337 for (ii=0; ii < utm_grid.zone[Zone].row[row].npoints; ii++)
3338 {
3339 if (utm_grid.zone[Zone].row[row].points[ii].y <= screen_height)
3340 {
3341 finished_with_current_zone = 0; // Some points were within the zone, keep computing
3342 }
3343 }
3344
3345
3346 e[1] = e[0]; // carriage return
3347 n[1] -= utm_grid_spacing_m; // line feed
3348 // Yea, your comments are real funny Olivier... Gets the point
3349 // across though!
3350
3351
3352 row++;
3353 if (row >= UTM_GRID_MAX_COLS_ROWS)
3354 {
3355 finished_with_current_zone++;
3356 }
3357
3358 utm_grid.zone[Zone].ncols = max_i(col, utm_grid.zone[Zone].ncols);
3359 utm_grid.zone[Zone].nrows = max_i(row, utm_grid.zone[Zone].nrows);
3360 col = 0;
3361 row_point = row_point_start;
3362 col_point++;
3363
3364 if (n[1] < 0)
3365 {
3366 fprintf(stderr,"n[1] < 0\n");
3367 finished_with_current_zone++;
3368 }
3369
3370 if (finished_with_current_zone && xx > screen_width)
3371 {
3372 done = 1;
3373 }
3374
3375 // Go to next iteration of while loop (skip next statement)
3376 continue;
3377 }
3378
3379 e[1] += utm_grid_spacing_m;
3380
3381 } // End of while (done) loop
3382
3383 /////////////////////////////////////////////////////////////////////
3384 /////////////////////////////////////////////////////////////////////
3385
3386
3387
3388 //fprintf(stderr, "After while loop\n");
3389
3390 // utm_grid.zone[] now contains an array of points marking fine grid
3391 // line intersections for parts of 1 to 4 zones that appear on
3392 // the screen. Each utm_grid.zone[] is a vertical stripe, and may include
3393 // more than one zone letter, e.g. zone[0] might include 15U and 15T,
3394 // while zone[1] might include 16U and 16T.
3395
3396 //#define UTM_DEBUG_VERB
3397
3398 for (Zone=0; Zone < UTM_GRID_MAX_ZONES; Zone++)
3399 {
3400
3401 #ifdef UTM_DEBUG_VERB
3402 fprintf(stderr,"\nutm_grid.zone[%d].ncols=%d\nutm_grid.zone[%d].nrows=%d\n",
3403 Zone, utm_grid.zone[Zone].ncols, Zone, utm_grid.zone[Zone].nrows);
3404 #endif
3405
3406 // Cleanup columns
3407 for (ii=0; ii < (int)utm_grid.zone[Zone].ncols; ii++)
3408 {
3409 int np = utm_grid.zone[Zone].col[ii].npoints;
3410 int fp = utm_grid.zone[Zone].col[ii].firstpoint;
3411 int nbp = 0;
3412
3413 #ifdef UTM_DEBUG_VERB
3414 fprintf(stderr,"utm_grid.zone[%d].col[%d].npoints=%d .firstpoint=%d\n",
3415 Zone, ii, np, fp);
3416 if (np < 2)
3417 {
3418 puts(" Not enough points!");
3419 }
3420 else
3421 {
3422 puts("");
3423 }
3424
3425 for (jj=fp; jj < fp+np; jj++)
3426 {
3427 fprintf(stderr," col[%d].points[%d] = [ %d, %d ]", ii, jj,
3428 utm_grid.zone[Zone].col[ii].points[jj].x,
3429 utm_grid.zone[Zone].col[ii].points[jj].y);
3430 if (utm_grid.zone[Zone].col[ii].points[jj].x ==
3431 utm_grid.zone[Zone].boundary_x)
3432 {
3433 puts(" Boundary");
3434 }
3435 else
3436 {
3437 puts("");
3438 }
3439 }
3440 #endif
3441 for (jj=fp; jj < fp+np; jj++)
3442 {
3443 if (utm_grid.zone[Zone].col[ii].points[jj].x ==
3444 utm_grid.zone[Zone].boundary_x)
3445 {
3446 nbp++;
3447 }
3448 else if (nbp > 0) // We had a boundary point, but not anymore
3449 {
3450 fp = utm_grid.zone[Zone].col[ii].firstpoint = l16(jj - 1);
3451 //fprintf(stderr,"np:%d, jj:%d\n",np,jj);
3452 // This can result in negative numbers!
3453 np = utm_grid.zone[Zone].col[ii].npoints = np - jj + 1;
3454 //fprintf(stderr,"new np:%d\n",np);
3455 if (np < 0)
3456 {
3457 np = 0; // Prevents segfaults in
3458 // XDrawLines() and memmove()
3459 // below.
3460 }
3461 break; // Exit from for loop
3462 }
3463 if (nbp == np) // All points are boundary points
3464 {
3465 fp = utm_grid.zone[Zone].col[ii].firstpoint = 0;
3466 np = utm_grid.zone[Zone].col[ii].npoints = 0;
3467 }
3468 }
3469
3470 // What's the below code doing? Can get a segfault without this in
3471 // the XDrawLines() functions below (fixed by making npoints an int
3472 // instead of an unsigned int). Sometimes we get a segfault right
3473 // here due to the memmove() function. In one such case, np was -2.
3474 // Latest code keeps some lines from getting drawn, but at least we
3475 // don't get a segfault.
3476 //
3477 if (fp > 0)
3478 {
3479 if (np > 0)
3480 {
3481 memmove(&utm_grid.zone[Zone].col[ii].points[0],
3482 &utm_grid.zone[Zone].col[ii].points[fp], np * sizeof(XPoint));
3483 fp = utm_grid.zone[Zone].col[ii].firstpoint = 0;
3484 }
3485 else
3486 {
3487 //fprintf(stderr,"draw_minor_utm_mgrs_grid: ii:%d, np:%d, size:%d\n",ii,np,sizeof(XPoint));
3488 //fprintf(stderr,"Problem1: in draw_minor_utm_mgrs_grid() memmove, np was %d. Skipping memmove.\n",np);
3489 }
3490 }
3491
3492 #ifdef UTM_DEBUG_VERB
3493 fprintf(stderr,"_tm_grid.zone[%d].col[%d].npoints=%d.firstpoint=%d\n",
3494 Zone, ii, np, fp);
3495 for (jj=fp; jj < fp+np; jj++)
3496 {
3497 fprintf(stderr," col[%d].points[%d] = [ %d, %d ]", ii, jj,
3498 utm_grid.zone[Zone].col[ii].points[jj].x,
3499 utm_grid.zone[Zone].col[ii].points[jj].y);
3500 if (utm_grid.zone[Zone].col[ii].points[jj].x ==
3501 utm_grid.zone[Zone].boundary_x)
3502 {
3503 puts(" Boundary");
3504 }
3505 else
3506 {
3507 puts("");
3508 }
3509 }
3510 puts("");
3511 #endif
3512 }
3513
3514 // Cleanup rows
3515 for (ii=0; ii < (int)utm_grid.zone[Zone].nrows; ii++)
3516 {
3517 int np = utm_grid.zone[Zone].row[ii].npoints;
3518 int fp = utm_grid.zone[Zone].row[ii].firstpoint;
3519 #ifdef UTM_DEBUG_VERB
3520 fprintf(stderr,"utm_grid.zone[%d].row[%d].npoints=%d.firstpoint=%d\n",
3521 Zone, ii, np, fp);
3522 if (np < 2)
3523 {
3524 puts(" Not enough points!");
3525 }
3526 else
3527 {
3528 puts("");
3529 }
3530 #endif
3531 // What's this doing? This appears to be important, as things get
3532 // really messed up if it's commented out.
3533 if (fp > 0)
3534 {
3535 if (np > 0)
3536 {
3537 memmove(&utm_grid.zone[Zone].row[ii].points[0],
3538 &utm_grid.zone[Zone].row[ii].points[fp], np * sizeof(XPoint));
3539 fp = utm_grid.zone[Zone].row[ii].firstpoint = 0;
3540 }
3541 else
3542 {
3543 //fprintf(stderr,"draw_minor_utm_mgrs_grid: ii:%d, np:%d, size:%d\n",ii,np,sizeof(XPoint));
3544 //fprintf(stderr,"Problem2: in draw_minor_utm_mgrs_grid() memmove, np was %d. Skipping memmove.\n",np);
3545 }
3546
3547 }
3548 #ifdef UTM_DEBUG_VERB
3549 for (jj=fp; jj < fp+np; jj++)
3550 {
3551 fprintf(stderr," row[%d].points[%d] = [ %d, %d ]\n", ii, jj,
3552 utm_grid.zone[Zone].row[ii].points[jj].x,
3553 utm_grid.zone[Zone].row[ii].points[jj].y);
3554 }
3555 #endif
3556 }
3557 }
3558
3559 // Rows and columns ready to go so setup hash
3560 utm_grid.hash.ul_x = NW_corner_longitude;
3561 utm_grid.hash.ul_y = NW_corner_latitude;
3562 utm_grid.hash.lr_x = SE_corner_longitude;
3563 utm_grid.hash.lr_y = SE_corner_latitude;
3564
3565 // XPoint arrays are set up. Go draw the grid.
3566 actually_draw_utm_minor_grid(w);
3567
3568 return(0);
3569
3570 } // End of draw_minor_utm_mgrs_grid() function
3571
3572
3573
3574
3575
3576 //*****************************************************************
3577 // draw_grid()
3578 //
3579 // Draws a lat/lon or UTM/UPS grid on top of the view.
3580 //
3581 //*****************************************************************
draw_grid(Widget w)3582 void draw_grid(Widget w)
3583 {
3584 int half; // Center of the white lines used to draw the borders
3585 int border_width = 14; // The width of the border to draw around the
3586 // map to place labeled tick marks into
3587 // should be an even number.
3588 // The default here is overidden by the border fontsize.
3589
3590
3591 if (!long_lat_grid) // We don't wish to draw a map grid
3592 {
3593 return;
3594 }
3595
3596 if (draw_labeled_grid_border==TRUE)
3597 {
3598 // Determine how wide the border should be.
3599 border_width = get_border_width(w);
3600 half = border_width/2;
3601 // draw a white border around the map.
3602 (void)XSetLineAttributes(XtDisplay(w),
3603 gc,
3604 border_width,
3605 LineSolid,
3606 CapRound,
3607 JoinRound);
3608 (void)XSetForeground(XtDisplay(w),
3609 gc,
3610 colors[border_foreground_color]); // white
3611 (void)XDrawLine(XtDisplay(w),
3612 pixmap_final,
3613 gc,
3614 0,
3615 l16(half),
3616 l16(screen_width),
3617 l16(half));
3618 (void)XDrawLine(XtDisplay(w),
3619 pixmap_final,
3620 gc,
3621 l16(half),
3622 0,
3623 l16(half),
3624 l16(screen_height));
3625 (void)XDrawLine(XtDisplay(w),
3626 pixmap_final,
3627 gc,
3628 0,
3629 l16(screen_height-half),
3630 l16(screen_width),
3631 l16(screen_height-half));
3632 (void)XDrawLine(XtDisplay(w),
3633 pixmap_final,
3634 gc,
3635 l16(screen_width-half),
3636 0,
3637 l16(screen_width-half),
3638 l16(screen_height));
3639 }
3640
3641 // Set the line width in the GC to 2 pixels wide for the larger
3642 // UTM grid and the complete Lat/Long grid.
3643 (void)XSetLineAttributes (XtDisplay (w), gc_tint, 2, LineOnOffDash, CapButt,JoinMiter);
3644 (void)XSetForeground (XtDisplay (w), gc_tint, colors[0x27]);
3645 (void)XSetFunction (XtDisplay (da), gc_tint, GXxor);
3646
3647 if (coordinate_system == USE_UTM
3648 || coordinate_system == USE_UTM_SPECIAL
3649 || coordinate_system == USE_MGRS)
3650 {
3651
3652 int ret_code;
3653
3654 //draw_vector_ll(w, -5.0, -5.0, 5.0, 5.0, gc_tint, pixmap_final, 0);
3655 //draw_vector_ll(w, 5.0, 5.0, -5.0, -5.0, gc_tint, pixmap_final, 0);
3656
3657 // Draw major UTM/MGRS zones
3658 draw_major_utm_mgrs_grid(w);
3659
3660 // Draw minor UTM/MGRS zones
3661 ret_code = draw_minor_utm_mgrs_grid(w);
3662 if (ret_code)
3663 {
3664 fprintf(stderr,
3665 "Encountered problem %d while calculating minor utm grid!\n",
3666 ret_code);
3667 }
3668
3669 } // End of UTM grid section
3670 else // Lat/Long coordinate system, draw lat/long lines
3671 {
3672 draw_complete_lat_lon_grid(w);
3673 } // End of Lat/Long section
3674 } // End of draw_grid()
3675
3676
3677
3678
3679
3680 /**********************************************************
3681 * get_map_ext()
3682 *
3683 * Returns the extension for the filename. We use this to
3684 * determine which sort of map file it is.
3685 **********************************************************/
get_map_ext(char * filename)3686 char *get_map_ext (char *filename)
3687 {
3688 int len;
3689 int i;
3690 char *ext;
3691
3692 ext = NULL;
3693 len = (int)strlen (filename);
3694 for (i = len; i >= 0; i--)
3695 {
3696 if (filename[i] == '.')
3697 {
3698 ext = filename + (i + 1);
3699 break;
3700 }
3701 }
3702 return (ext);
3703 }
3704
3705
3706
3707
3708
3709 /**********************************************************
3710 * get_map_dir()
3711 *
3712 * Used to snag just the pathname from a complete filename.
3713 * Modifies input parameter "fullpath".
3714 **********************************************************/
get_map_dir(char * fullpath)3715 char *get_map_dir (char *fullpath)
3716 {
3717 int len;
3718 int i;
3719
3720 len = (int)strlen (fullpath);
3721 for (i = len; i >= 0; i--)
3722 {
3723 if (fullpath[i] == '/')
3724 {
3725 fullpath[i + 1] = '\0';
3726 break;
3727 }
3728 }
3729 return (fullpath);
3730 }
3731
3732
3733
3734
3735
3736 /***********************************************************
3737 * map_visible()
3738 *
3739 * Tests whether a particular path/filename is within our
3740 * current view. We use this to decide whether to plot or
3741 * skip a particular image file (major speed-up!).
3742 * Input coordinates are in the Xastir coordinate system.
3743 *
3744 * Had to fix a bug here where the viewport glanced over the
3745 * edge of the earth, causing strange results like this.
3746 * Notice the View Edges Top value is out of range:
3747 *
3748 *
3749 * Bottom Top Left Right
3750 * View Edges: 31,017,956 4,290,923,492 35,971,339 90,104,075
3751 * Map Edges: 12,818,482 12,655,818 64,079,859 64,357,110
3752 *
3753 * Left map boundary inside view
3754 * Right map boundary inside view
3755 * map_inside_view: 1 view_inside_map: 0 parallel_edges: 0
3756 * Map not within current view.
3757 * Skipping map: /usr/local/share/xastir/maps/tif/uk/425_0525_bng.tif
3758 *
3759 *
3760 * I had to check for out-of-bounds numbers for the viewport and
3761 * set them to min or max values so that this function always
3762 * works properly. Here are the bounds of the earth (Xastir
3763 * Coordinate System):
3764 *
3765 * 0 (90 deg. or 90N)
3766 *
3767 * 0 (-180 deg. or 180W) 129,600,000 (180 deg. or 180E)
3768 *
3769 * 64,800,000 (-90 deg. or 90S)
3770 *
3771 ***********************************************************/
map_visible(unsigned long map_max_y,unsigned long map_min_y,unsigned long map_min_x,unsigned long map_max_x)3772 int map_visible (unsigned long map_max_y, // bottom_map_boundary
3773 unsigned long map_min_y, // top_map_boundary
3774 unsigned long map_min_x, // left_map_boundary
3775 unsigned long map_max_x) // right_map_boundary) {
3776 {
3777
3778 //fprintf(stderr,"map_visible\n");
3779
3780 // From computation geometry equations, intersection of two line
3781 // segments, they use the bounding box for two lines. This is
3782 // the same as what we want to do:
3783 //
3784 // http://www.cs.kent.edu/~dragan/AdvAlg/CompGeom-2x1.pdfa
3785 // http://www.gamedev.net/reference/articles/article735.asp
3786 //
3787 // The quick rejection algorithm:
3788 //
3789 if (NW_corner_latitude > (long)map_max_y)
3790 {
3791 if (debug_level & 16)
3792 {
3793 fprintf(stderr,
3794 "map_visible, rejecting: NW_corner_latitude:%ld > map_max_y:%ld\n",
3795 NW_corner_latitude,
3796 map_max_y);
3797 fprintf(stderr,
3798 "\tmap or object is above viewport\n");
3799 }
3800 return(0);
3801 }
3802
3803 if ((long)map_min_y > SE_corner_latitude)
3804 {
3805 if (debug_level & 16)
3806 {
3807 fprintf(stderr,
3808 "map_visible, rejecting: map_min_y:%ld > SE_corner_latitude:%ld\n",
3809 map_min_y,
3810 SE_corner_latitude);
3811 fprintf(stderr,
3812 "\tmap or object is below viewport\n");
3813 }
3814 return(0);
3815 }
3816
3817 if (NW_corner_longitude > (long)map_max_x)
3818 {
3819 if (debug_level & 16)
3820 {
3821 fprintf(stderr,
3822 "map_visible, rejecting: NW_corner_longitude:%ld > map_max_x:%ld\n",
3823 NW_corner_longitude,
3824 map_max_x);
3825 fprintf(stderr,
3826 "\tmap or object is left of viewport\n");
3827 }
3828 return(0);
3829 }
3830
3831 if ((long)map_min_x > SE_corner_longitude)
3832 {
3833 if (debug_level & 16)
3834 {
3835 fprintf(stderr,
3836 "map_visible, rejecting: map_min_x:%ld > SE_corner_longitude:%ld\n",
3837 map_min_x,
3838 SE_corner_longitude);
3839 fprintf(stderr,
3840 "\tmap or object is right of viewport\n");
3841 }
3842 return(0);
3843 }
3844
3845 return (1); // At least part of the map is on-screen
3846 }
3847
3848
3849
3850 /////////////////////////////////////////////////////////////////////
3851 // get_viewport_lat_lon(double *xmin, double *ymin, double* xmax, double *ymax)
3852 // Simply returns the floating point corners of the map display.
3853 /////////////////////////////////////////////////////////////////////
get_viewport_lat_lon(double * xmin,double * ymin,double * xmax,double * ymax)3854 void get_viewport_lat_lon(double *xmin,
3855 double *ymin,
3856 double* xmax,
3857 double *ymax)
3858 {
3859
3860 *xmin=(double)f_NW_corner_longitude;
3861 *ymin=(double)f_SE_corner_latitude;
3862 *xmax=(double)f_SE_corner_longitude;
3863 *ymax=(double)f_NW_corner_latitude;
3864 }
3865
3866 /////////////////////////////////////////////////////////////////////
3867 // map_inside_viewport_lat_lon()
3868 // Returns 1 if the given set of xmin,xmax, ymin,ymax defines a
3869 // rectangle entirely contained in the current viewport (as opposed to
3870 // merely partially overlapping it. Returns zero otherwise.
3871 /////////////////////////////////////////////////////////////////////
map_inside_viewport_lat_lon(double map_min_y,double map_max_y,double map_min_x,double map_max_x)3872 int map_inside_viewport_lat_lon(double map_min_y,
3873 double map_max_y,
3874 double map_min_x,
3875 double map_max_x)
3876 {
3877 int retval=0;
3878 if (map_min_x >= f_NW_corner_longitude &&
3879 map_min_y >= f_SE_corner_latitude &&
3880 map_max_x <= f_SE_corner_longitude &&
3881 map_max_y <= f_NW_corner_latitude)
3882 {
3883 retval=1;
3884 }
3885
3886 return (retval);
3887 }
3888
3889
3890 /////////////////////////////////////////////////////////////////////
3891 // map_visible_lat_lon()
3892 //
3893 // We have the center of the view in floating point format:
3894 //
3895 // float f_center_longitude; // Floating point map center longitude
3896 // float f_center_latitude; // Floating point map center latitude
3897 //
3898 // So we just need to compute the top/bottom/left/right using those
3899 // values and the scale_x/scale_y values before doing the compare.
3900 //
3901 // y scaling in 1/100 sec per pixel
3902 // x scaling in 1/100 sec per pixel, calculated from scale_y
3903 //
3904 //
3905 // 0 (90 deg. or 90N)
3906 //
3907 // 0 (-180 deg. or 180W) 129,600,000 (180 deg. or 180E)
3908 //
3909 // 64,800,000 (-90 deg. or 90S)
3910 //
3911 // ******************* ******************* max_y
3912 // *NW * + * * +
3913 // * * * *
3914 // * * * *
3915 // * View * latitude (y) * Map *
3916 // * * * *
3917 // * * * *
3918 // * SE* - * * -
3919 // ******************* ******************* min_y
3920 // - longitude(x) + - min_x max_x +
3921
3922 /////////////////////////////////////////////////////////////////////
map_visible_lat_lon(double map_min_y,double map_max_y,double map_min_x,double map_max_x)3923 int map_visible_lat_lon (double map_min_y, // f_bottom_map_boundary
3924 double map_max_y, // f_top_map_boundary
3925 double map_min_x, // f_left_map_boundary
3926 double map_max_x) // f_right_map_boundary
3927 {
3928
3929 //fprintf(stderr,"map_visible_lat_lon\n");
3930
3931 // From computation geometry equations, intersection of two line
3932 // segments, they use the bounding box for two lines. This is
3933 // the same as what we want to do:
3934 //
3935 // http://www.cs.kent.edu/~dragan/AdvAlg/CompGeom-2x1.pdfa
3936 // http://www.gamedev.net/reference/articles/article735.asp
3937 //
3938 // The quick rejection algorithm:
3939 //
3940 if (map_max_y < f_SE_corner_latitude )
3941 {
3942 return(0); // map below view
3943 }
3944 if (map_max_x < f_NW_corner_longitude)
3945 {
3946 return(0); // map left of view
3947 }
3948 if (map_min_y > f_NW_corner_latitude )
3949 {
3950 return(0); // view below map
3951 }
3952 if (map_min_x > f_SE_corner_longitude)
3953 {
3954 return(0); // view left of map
3955 }
3956
3957 return (1); // Draw this map onto the screen
3958 }
3959
3960
3961
3962
3963
3964 /**********************************************************
3965 * draw_label_text()
3966 *
3967 * Does what it says. Used to draw strings onto the
3968 * display.
3969 **********************************************************/
draw_label_text(Widget w,int x,int y,int label_length,int color,char * label_text)3970 void draw_label_text (Widget w, int x, int y, int label_length, int color, char *label_text)
3971 {
3972
3973 // This draws a gray background rectangle upon which we draw the text.
3974 // Probably not needed. It ends up obscuring details underneath.
3975 //(void)XSetForeground (XtDisplay (w), gc, colors[0x0ff]);
3976 //(void)XFillRectangle (XtDisplay (w), pixmap, gc, x - 1, (y - 10),(label_length * 6) + 2, 11);
3977
3978 (void)XSetForeground (XtDisplay (w), gc, color);
3979 (void)XDrawString (XtDisplay (w), pixmap, gc, x, y, label_text, label_length);
3980 }
3981
3982
3983
3984
3985
3986 // Must make sure that fonts are not loaded again and again, as this
3987 // takes a big chunk of memory each time. Can you say "memory
3988 // leak"?
3989
3990 XFontStruct *rotated_label_font[FONT_MAX]= {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
3991 char rotated_label_fontname[FONT_MAX][MAX_LABEL_FONTNAME];
3992 static char
3993 current_rotated_label_fontname[FONT_MAX][sizeof(rotated_label_fontname)] = {"","","","","","","","",""};
3994
3995 /**********************************************************
3996 * draw_rotated_label_text_common()
3997 * call through wrappers:
3998 * draw_rotated_label_text_to_pixmap()
3999 * draw_rotated_label_text()
4000 * draw_centered_label_text()
4001 *
4002 * Does what it says. Used to draw strings onto the
4003 * display.
4004 *
4005 * Use "xfontsel" or other tools to figure out what fonts
4006 * to use here.
4007 *
4008 * Paramenters:
4009 * target_pixmap specifies the pixmap the text is to be drawn to.
4010 * draw_outline specifies whether a 1 pixel outline around the
4011 * text, TRUE to draw outline.
4012 * outline_bg_color is the color of the outline.
4013 * color is the color of the text inside the outline, or the
4014 * color of the text itself if no outline is added.
4015 **********************************************************/
4016 /* common code used by the two entries --- a result of retrofitting a new
4017 feature (centered) */
draw_rotated_label_text_common(Widget w,float my_rotation,int x,int y,int UNUSED (label_length),int color,char * label_text,int align,int fontsize,Pixmap target_pixmap,int draw_outline,int outline_bg_color)4018 static void draw_rotated_label_text_common (Widget w, float my_rotation, int x, int y, int UNUSED(label_length), int color, char *label_text, int align, int fontsize, Pixmap target_pixmap, int draw_outline, int outline_bg_color)
4019 {
4020 // XPoint *corner;
4021 // int i;
4022 int x_outline;
4023 int y_outline;
4024
4025
4026 // Do some sanity checking
4027 if (fontsize < 0 || fontsize >= FONT_MAX)
4028 {
4029 fprintf(stderr,"Font size is out of range: %d\n", fontsize);
4030 return;
4031 }
4032
4033 /* see if fontname has changed */
4034 if (rotated_label_font[fontsize] &&
4035 strcmp(rotated_label_fontname[fontsize],current_rotated_label_fontname[fontsize]) != 0)
4036 {
4037 XFreeFont(XtDisplay(w),rotated_label_font[fontsize]);
4038 rotated_label_font[fontsize] = NULL;
4039 xastir_snprintf(current_rotated_label_fontname[fontsize],
4040 sizeof(rotated_label_fontname),
4041 "%s",
4042 rotated_label_fontname[fontsize]);
4043 }
4044 /* load font */
4045 if(!rotated_label_font[fontsize])
4046 {
4047 rotated_label_font[fontsize]=(XFontStruct *)XLoadQueryFont(XtDisplay (w),
4048 rotated_label_fontname[fontsize]);
4049 if (rotated_label_font[fontsize] == NULL) // Couldn't get the font!!!
4050 {
4051 fprintf(stderr,"draw_rotated_label_text: Couldn't get font %s\n",
4052 rotated_label_fontname[fontsize]);
4053 return;
4054 }
4055 }
4056
4057 if (draw_outline)
4058 {
4059 // make outline style
4060 (void)XSetForeground(XtDisplay(w),gc,outline_bg_color);
4061 // Draw the string repeatedly with 1 pixel offsets in the
4062 // background color to make an outline.
4063
4064 for (x_outline=-1; x_outline<2; x_outline++)
4065 {
4066 for (y_outline=-1; y_outline<2; y_outline++)
4067 {
4068 // draws one extra copy at x,y
4069 (void)XRotDrawAlignedString(XtDisplay (w),
4070 rotated_label_font[fontsize],
4071 my_rotation,
4072 target_pixmap,
4073 gc,
4074 x+x_outline,
4075 y+y_outline,
4076 label_text,
4077 align);
4078 }
4079 }
4080 }
4081
4082
4083 // Code to determine the bounding box corner points for the rotated text
4084 // corner = XRotTextExtents(w,rotated_label_font,my_rotation,x,y,label_text,BLEFT);
4085 // for (i=0;i<5;i++) {
4086 // fprintf(stderr,"%d,%d\t",corner[i].x,corner[i].y);
4087 // }
4088 // fprintf(stderr,"\n");
4089
4090 (void)XSetForeground (XtDisplay (w), gc, color);
4091
4092 //fprintf(stderr,"%0.1f\t%s\n",my_rotation,label_text);
4093
4094 (void)XRotDrawAlignedString(XtDisplay (w),
4095 rotated_label_font[fontsize],
4096 my_rotation,
4097 target_pixmap,
4098 gc,
4099 x,
4100 y,
4101 label_text,
4102 align);
4103 }
4104
4105
4106
4107
4108
4109 // Find the pixel length of an unrotated string in the rotated_label_font.
4110 // Parameters:
4111 // w - the XtDisplay.
4112 // label_text - the string of which the length is to be found.
4113 // fontsize - the fontsize in the rotated_label_font in which the string
4114 // is to be rendered.
4115 // Returns: the length in pixels of the string, -1 on an error.
get_rotated_label_text_length_pixels(Widget w,char * label_text,int fontsize)4116 int get_rotated_label_text_length_pixels(Widget w, char *label_text, int fontsize)
4117 {
4118 int dir, asc, desc; // parameters returned by XTextExtents, but not used here.
4119 XCharStruct overall; // description of the space occupied by the string.
4120 int return_value; // value to return
4121 int got_font; // flag indicating that a font is available
4122
4123 return_value = -1;
4124 got_font = TRUE;
4125
4126 /* load font */
4127 if(!rotated_label_font[fontsize])
4128 {
4129 rotated_label_font[fontsize]=(XFontStruct *)XLoadQueryFont(XtDisplay (w),
4130 rotated_label_fontname[fontsize]);
4131 if (rotated_label_font[fontsize] == NULL) // Couldn't get the font!!!
4132 {
4133 fprintf(stderr,"get_rotated_label_text_length_pixels: Couldn't get font %s\n",
4134 rotated_label_fontname[fontsize]);
4135 got_font = FALSE;
4136 }
4137 }
4138
4139 if (got_font)
4140 {
4141 // find out the width in pixels of the unrotated label_text string.
4142 XTextExtents(rotated_label_font[fontsize], label_text, strlen(label_text), &dir, &asc, &desc,
4143 &overall);
4144 return_value = overall.width;
4145 }
4146
4147 return return_value;
4148 }
4149
4150
4151
4152
4153
4154 // Find the pixel height of an unrotated string in the rotated_label_font.
4155 // Parameters:
4156 // w - the XtDisplay.
4157 // label_text - the string of which the length is to be found.
4158 // fontsize - the fontsize in the rotated_label_font in which the string
4159 // is to be rendered.
4160 // Returns: the height in pixels of the string, -1 on an error.
get_rotated_label_text_height_pixels(Widget w,char * label_text,int fontsize)4161 int get_rotated_label_text_height_pixels(Widget w, char *label_text, int fontsize)
4162 {
4163 int dir, asc, desc; // parameters returned by XTextExtents, but not used here.
4164 XCharStruct overall; // description of the space occupied by the string.
4165 int return_value; // value to return
4166 int got_font; // flag indicating that a font is available
4167
4168 return_value = -1;
4169 got_font = TRUE;
4170
4171 /* load font */
4172 if(!rotated_label_font[fontsize])
4173 {
4174 rotated_label_font[fontsize]=(XFontStruct *)XLoadQueryFont(XtDisplay (w),
4175 rotated_label_fontname[fontsize]);
4176 if (rotated_label_font[fontsize] == NULL) // Couldn't get the font!!!
4177 {
4178 fprintf(stderr,"get_rotated_label_text_height_pixels: Couldn't get font %s\n",
4179 rotated_label_fontname[fontsize]);
4180 got_font = FALSE;
4181 }
4182 }
4183
4184 if (got_font)
4185 {
4186 // find out the width in pixels of the unrotated label_text string.
4187 XTextExtents(rotated_label_font[fontsize], label_text, strlen(label_text), &dir, &asc, &desc,
4188 &overall);
4189 return_value = overall.ascent + overall.descent;
4190 }
4191
4192 return return_value;
4193 }
4194
4195
4196
4197
4198
4199 // Draw a rotated label onto the specified pixmap.
4200 // Wrapper for draw_rotated_label_text-common().
draw_rotated_label_text_to_target(Widget w,int rotation,int x,int y,int label_length,int color,char * label_text,int fontsize,Pixmap target_pixmap,int draw_outline,int outline_bg_color)4201 void draw_rotated_label_text_to_target (Widget w, int rotation, int x, int y, int label_length, int color, char *label_text, int fontsize, Pixmap target_pixmap, int draw_outline, int outline_bg_color)
4202 {
4203 float my_rotation = (float)((-rotation)-90);
4204
4205 if ( ( (my_rotation < -90.0) && (my_rotation > -270.0) )
4206 || ( (my_rotation > 90.0) && (my_rotation < 270.0) ) )
4207 {
4208 my_rotation = my_rotation + 180.0;
4209 (void)draw_rotated_label_text_common(w,
4210 my_rotation,
4211 x,
4212 y,
4213 label_length,
4214 color,
4215 label_text,
4216 BRIGHT,
4217 fontsize,
4218 target_pixmap,
4219 draw_outline,
4220 outline_bg_color);
4221 }
4222 else
4223 {
4224 (void)draw_rotated_label_text_common(w,
4225 my_rotation,
4226 x,
4227 y,
4228 label_length,
4229 color,
4230 label_text,
4231 BLEFT,
4232 fontsize,
4233 target_pixmap,
4234 draw_outline,
4235 outline_bg_color);
4236 }
4237 }
4238
4239
4240
4241
4242
draw_rotated_label_text(Widget w,int rotation,int x,int y,int label_length,int color,char * label_text,int fontsize)4243 void draw_rotated_label_text (Widget w, int rotation, int x, int y, int label_length, int color, char *label_text, int fontsize)
4244 {
4245 float my_rotation = (float)((-rotation)-90);
4246
4247 if ( ( (my_rotation < -90.0) && (my_rotation > -270.0) )
4248 || ( (my_rotation > 90.0) && (my_rotation < 270.0) ) )
4249 {
4250 my_rotation = my_rotation + 180.0;
4251 (void)draw_rotated_label_text_common(w,
4252 my_rotation,
4253 x,
4254 y,
4255 label_length,
4256 color,
4257 label_text,
4258 BRIGHT,
4259 fontsize,
4260 pixmap, 0, 0);
4261 }
4262 else
4263 {
4264 (void)draw_rotated_label_text_common(w,
4265 my_rotation,
4266 x,
4267 y,
4268 label_length,
4269 color,
4270 label_text,
4271 BLEFT,
4272 fontsize,
4273 pixmap, 0, 0);
4274 }
4275 }
4276
draw_centered_label_text(Widget w,int rotation,int x,int y,int label_length,int color,char * label_text,int fontsize)4277 void draw_centered_label_text (Widget w, int rotation, int x, int y, int label_length, int color, char *label_text, int fontsize)
4278 {
4279 float my_rotation = (float)((-rotation)-90);
4280
4281 (void)draw_rotated_label_text_common(w,
4282 my_rotation,
4283 x,
4284 y,
4285 label_length,
4286 color,
4287 label_text,
4288 BCENTRE,
4289 fontsize,
4290 pixmap, 0, 0);
4291 }
4292
4293
4294
4295
4296
Print_postscript_destroy_shell(Widget UNUSED (widget),XtPointer clientData,XtPointer UNUSED (callData))4297 static void Print_postscript_destroy_shell(Widget UNUSED(widget), XtPointer clientData, XtPointer UNUSED(callData) )
4298 {
4299 Widget shell = (Widget) clientData;
4300 char *temp_ptr;
4301
4302
4303 XtPopdown(shell);
4304
4305 begin_critical_section(&print_postscript_dialog_lock, "maps.c:Print_postscript_destroy_shell" );
4306
4307 if (print_postscript_dialog)
4308 {
4309 // Snag the path to the printer program from the print dialog
4310 temp_ptr = XmTextFieldGetString(printer_data);
4311 xastir_snprintf(printer_program,
4312 sizeof(printer_program),
4313 "%s",
4314 temp_ptr);
4315 XtFree(temp_ptr);
4316 (void)remove_trailing_spaces(printer_program);
4317
4318 // Check for empty variable
4319 if (printer_program[0] == '\0')
4320 {
4321
4322 #ifdef LPR_PATH
4323 // Path to LPR if defined
4324 xastir_snprintf(printer_program,
4325 sizeof(printer_program),
4326 "%s",
4327 LPR_PATH);
4328 #else // LPR_PATH
4329 // Empty path
4330 printer_program[0]='\0';
4331 #endif // LPR_PATH
4332 }
4333
4334 //fprintf(stderr,"%s\n", printer_program);
4335
4336 // Snag the path to the previewer program from the print dialog
4337 temp_ptr = XmTextFieldGetString(previewer_data);
4338 xastir_snprintf(previewer_program,
4339 sizeof(previewer_program),
4340 "%s",
4341 temp_ptr);
4342 XtFree(temp_ptr);
4343 (void)remove_trailing_spaces(previewer_program);
4344
4345 // Check for empty variable
4346 if (previewer_program[0] == '\0')
4347 {
4348
4349 #ifdef GV_PATH
4350 // Path to GV if defined
4351 xastir_snprintf(previewer_program,
4352 sizeof(previewer_program),
4353 "%s",
4354 GV_PATH);
4355 #else // GV_PATH
4356 // Empty string
4357 previewer_program[0] = '\0';
4358 #endif // GV_PATH
4359 }
4360 //fprintf(stderr,"%s\n", previewer_program);
4361 }
4362
4363 XtDestroyWidget(shell);
4364 print_postscript_dialog = (Widget)NULL;
4365
4366 end_critical_section(&print_postscript_dialog_lock, "maps.c:Print_postscript_destroy_shell" );
4367
4368 }
4369
4370
4371
4372
4373
Print_properties_destroy_shell(Widget UNUSED (widget),XtPointer clientData,XtPointer UNUSED (callData))4374 static void Print_properties_destroy_shell(Widget UNUSED(widget), XtPointer clientData, XtPointer UNUSED(callData) )
4375 {
4376 Widget shell = (Widget) clientData;
4377
4378 if (!shell)
4379 {
4380 return;
4381 }
4382
4383 XtPopdown(shell);
4384
4385 begin_critical_section(&print_properties_dialog_lock, "maps.c:Print_properties_destroy_shell" );
4386
4387 XtDestroyWidget(shell);
4388 print_properties_dialog = (Widget)NULL;
4389
4390 end_critical_section(&print_properties_dialog_lock, "maps.c:Print_properties_destroy_shell" );
4391
4392 }
4393
4394
4395
4396
4397
4398 // Print_window: Prints the drawing area to a Postscript file and
4399 // then sends it to the printer program (usually "lpr).
4400 //
Print_window(Widget widget,XtPointer UNUSED (clientData),XtPointer UNUSED (callData))4401 static void Print_window( Widget widget, XtPointer UNUSED(clientData), XtPointer UNUSED(callData) )
4402 {
4403
4404 #ifdef NO_XPM
4405 // fprintf(stderr,"XPM or ImageMagick support not compiled into Xastir!\n");
4406 popup_message_always(langcode("POPEM00035"),
4407 "XPM or ImageMagick support not compiled into Xastir! Cannot Print!");
4408 #else // NO_XPM
4409
4410 char xpm_filename[MAX_FILENAME];
4411 char ps_filename[MAX_FILENAME];
4412 char command[MAX_FILENAME*2];
4413 char temp[MAX_FILENAME];
4414 int xpmretval;
4415 char temp_base_dir[MAX_VALUE];
4416
4417 get_user_base_dir("tmp", temp_base_dir, sizeof(temp_base_dir));
4418
4419 xastir_snprintf(xpm_filename,
4420 sizeof(xpm_filename),
4421 "%s/print.xpm",
4422 temp_base_dir);
4423
4424 xastir_snprintf(ps_filename,
4425 sizeof(ps_filename),
4426 "%s/print.ps",
4427 temp_base_dir);
4428
4429 busy_cursor(appshell); // Show a busy cursor while we're doing all of this
4430
4431 // Get rid of the Print dialog
4432 Print_postscript_destroy_shell(widget, print_postscript_dialog, NULL );
4433
4434 if ( debug_level & 512 )
4435 {
4436 fprintf(stderr,"Creating %s\n", xpm_filename );
4437 }
4438
4439 xastir_snprintf(temp, sizeof(temp), "%s", langcode("PRINT0012") );
4440 statusline(temp,1); // Dumping image to file...
4441
4442 if (chdir(temp_base_dir) != 0)
4443 {
4444 fprintf(stderr,"Couldn't chdir to %s directory for print_window\n", temp_base_dir);
4445 return;
4446 }
4447
4448 xpmretval=XpmWriteFileFromPixmap(XtDisplay(appshell),// Display *display
4449 "print.xpm", // char *filename
4450 pixmap_final, // Pixmap pixmap
4451 (Pixmap)NULL, // Pixmap shapemask
4452 NULL );
4453
4454 if (xpmretval != XpmSuccess)
4455 {
4456 fprintf(stderr,"ERROR writing %s: %s\n", xpm_filename,
4457 XpmGetErrorString(xpmretval));
4458 popup_message_always(langcode("POPEM00035"),
4459 "Error writing xpm image file! Cannot Print!");
4460 return;
4461 }
4462 else // We now have the xpm file created on disk
4463 {
4464
4465 chmod( xpm_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
4466
4467 if ( debug_level & 512 )
4468 {
4469 fprintf(stderr,"Convert %s ==> %s\n", xpm_filename, ps_filename );
4470 }
4471
4472
4473 // Convert it to a postscript file for printing. This depends
4474 // on the ImageMagick command "convert".
4475 //
4476
4477 if (debug_level & 512)
4478 {
4479 fprintf(stderr,"Width: %ld\tHeight: %ld\n", screen_width, screen_height);
4480 }
4481
4482 xastir_snprintf(temp, sizeof(temp), "%s", langcode("PRINT0013") );
4483 statusline(temp,1); // Converting to Postscript...
4484
4485
4486 #ifdef HAVE_CONVERT
4487 strcpy(command, CONVERT_PATH);
4488 command[sizeof(command)-1] = '\0'; // Terminate string
4489 strcat(command, " -filter Point ");
4490 command[sizeof(command)-1] = '\0'; // Terminate string
4491 strcat(command, xpm_filename);
4492 command[sizeof(command)-1] = '\0'; // Terminate string
4493 strcat(command, " ");
4494 command[sizeof(command)-1] = '\0'; // Terminate string
4495 strcat(command, ps_filename);
4496 command[sizeof(command)-1] = '\0'; // Terminate string
4497
4498 if ( debug_level & 512 )
4499 {
4500 fprintf(stderr,"%s\n", command );
4501 }
4502
4503 if ( system( command ) != 0 )
4504 {
4505 // fprintf(stderr,"\n\nPrint: Couldn't convert from XPM to PS!\n\n\n");
4506 popup_message_always(langcode("POPEM00035"),
4507 "Couldn't convert from XPM to PS!");
4508 return;
4509 }
4510 #endif // HAVE_CONVERT
4511
4512 chmod( ps_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
4513
4514 // Delete temporary xpm file
4515 if ( !(debug_level & 512) )
4516 {
4517 unlink( xpm_filename );
4518 }
4519
4520 if ( debug_level & 512 )
4521 {
4522 fprintf(stderr,"Printing postscript file %s\n", ps_filename);
4523 }
4524
4525 // Note: This needs to be changed to "lp" for Solaris.
4526 // Also need to have a field to configure the printer name. One
4527 // fill-in field could do both.
4528 //
4529 // Since we could be running SUID root, we don't want to be
4530 // calling "system" anyway. Several problems with it.
4531
4532 strcpy(command, printer_program);
4533 command[sizeof(command)-1] = '\0'; // Terminate string
4534 strcat(command, " ");
4535 command[sizeof(command)-1] = '\0'; // Terminate string
4536 strcat(command, ps_filename);
4537 command[sizeof(command)-1] = '\0'; // Terminate string
4538
4539 if ( debug_level & 512 )
4540 {
4541 fprintf(stderr,"%s\n", command);
4542 }
4543
4544 if (printer_program[0] == '\0')
4545 {
4546 // fprintf(stderr,"\n\nPrint: No print program defined!\n\n\n");
4547 popup_message_always(langcode("POPEM00035"),
4548 "No print program defined!");
4549 return;
4550 }
4551
4552 if ( system( command ) != 0 )
4553 {
4554 // fprintf(stderr,"\n\nPrint: Couldn't send to the printer!\n\n\n");
4555 popup_message_always(langcode("POPEM00035"),
4556 "Couldn't send to the printer!");
4557 return;
4558 }
4559
4560 /*
4561 if ( !(debug_level & 512) )
4562 unlink( ps_filename );
4563 */
4564
4565 if ( debug_level & 512 )
4566 {
4567 fprintf(stderr," Done printing.\n");
4568 }
4569 }
4570
4571 xastir_snprintf(temp, sizeof(temp), "%s", langcode("PRINT0014") );
4572 statusline(temp,1); // Finished creating print file.
4573
4574 //popup_message( langcode("PRINT0015"), langcode("PRINT0014") );
4575
4576 #endif // NO_XPM
4577
4578 }
4579
4580
4581
4582
4583
4584 // Print_preview: Prints the drawing area to a Postscript file. If
4585 // previewer_program has "gv" in it, then use the various options
4586 // selected by the user. If not, skip those options.
4587 //
Print_preview(Widget widget,XtPointer UNUSED (clientData),XtPointer UNUSED (callData))4588 static void Print_preview( Widget widget, XtPointer UNUSED(clientData), XtPointer UNUSED(callData) )
4589 {
4590
4591 #ifdef NO_XPM
4592 // fprintf(stderr,"XPM or ImageMagick support not compiled into Xastir!\n");
4593 popup_message_always(langcode("POPEM00035"),
4594 "XPM or ImageMagick support not compiled into Xastir! Cannot Print!");
4595 #else // NO_GRAPHICS
4596
4597 char xpm_filename[MAX_FILENAME];
4598 char ps_filename[MAX_FILENAME];
4599 char mono[50] = "";
4600 char invert[50] = "";
4601 char rotate[50] = "";
4602 char scale[50] = "";
4603 char density[50] = "";
4604 char command[MAX_FILENAME*2];
4605 char temp[MAX_FILENAME];
4606 char format[100] = " ";
4607 int xpmretval;
4608 char temp_base_dir[MAX_VALUE];
4609
4610 get_user_base_dir("tmp", temp_base_dir, sizeof(temp_base_dir));
4611
4612
4613 xastir_snprintf(xpm_filename,
4614 sizeof(xpm_filename),
4615 "%s/print.xpm",
4616 temp_base_dir);
4617
4618 xastir_snprintf(ps_filename,
4619 sizeof(ps_filename),
4620 "%s/print.ps",
4621 temp_base_dir);
4622
4623 busy_cursor(appshell); // Show a busy cursor while we're doing all of this
4624
4625 // Get rid of the Print Properties dialog if it exists
4626 Print_properties_destroy_shell(widget, print_properties_dialog, NULL );
4627
4628 if ( debug_level & 512 )
4629 {
4630 fprintf(stderr,"Creating %s\n", xpm_filename );
4631 }
4632
4633 xastir_snprintf(temp, sizeof(temp), "%s", langcode("PRINT0012") );
4634 statusline(temp,1); // Dumping image to file...
4635
4636 if (chdir(temp_base_dir) != 0)
4637 {
4638 fprintf(stderr,"Couldn't chdir to %s directory for print_preview\n", temp_base_dir);
4639 return;
4640 }
4641
4642 xpmretval=XpmWriteFileFromPixmap(XtDisplay(appshell),// Display *display
4643 "print.xpm", // char *filename
4644 pixmap_final, // Pixmap pixmap
4645 (Pixmap)NULL, // Pixmap shapemask
4646 NULL );
4647
4648 if (xpmretval != XpmSuccess)
4649 {
4650 fprintf(stderr,"ERROR writing %s: %s\n", xpm_filename,
4651 XpmGetErrorString(xpmretval));
4652 popup_message_always(langcode("POPEM00035"),
4653 "Error writing XPM file!");
4654 return;
4655 }
4656 else // We now have the xpm file created on disk
4657 {
4658
4659 chmod( xpm_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
4660
4661 if ( debug_level & 512 )
4662 {
4663 fprintf(stderr,"Convert %s ==> %s\n", xpm_filename, ps_filename );
4664 }
4665
4666
4667 // If we're not using "gv", skip most of the code below and
4668 // go straight to the previewer program portion of the code.
4669 //
4670 if ( strstr(previewer_program,"gv") )
4671 {
4672
4673 // Convert it to a postscript file for printing. This
4674 // depends on the ImageMagick command "convert".
4675 //
4676 // Other options to try in the future:
4677 // -label
4678 //
4679 if ( print_auto_scale )
4680 {
4681 // sprintf(scale, "-geometry 612x792 -page 612x792 "); // "Letter" size at 72 dpi
4682 // sprintf(scale, "-sample 612x792 -page 612x792 "); // "Letter" size at 72 dpi
4683 xastir_snprintf(scale, sizeof(scale), "-page 1275x1650+0+0 "); // "Letter" size at 150 dpi
4684 }
4685 else
4686 {
4687 scale[0] = '\0'; // Empty string
4688 }
4689
4690
4691 if ( print_in_monochrome )
4692 {
4693 xastir_snprintf(mono, sizeof(mono), "-monochrome +dither " ); // Monochrome
4694 }
4695 else
4696 {
4697 xastir_snprintf(mono, sizeof(mono), "+dither "); // Color
4698 }
4699
4700
4701 if ( print_invert )
4702 {
4703 xastir_snprintf(invert, sizeof(invert), "-negate " ); // Reverse Colors
4704 }
4705 else
4706 {
4707 invert[0] = '\0'; // Empty string
4708 }
4709
4710
4711 if (debug_level & 512)
4712 {
4713 fprintf(stderr,"Width: %ld\tHeight: %ld\n", screen_width, screen_height);
4714 }
4715
4716
4717 if ( print_rotated )
4718 {
4719 xastir_snprintf(rotate, sizeof(rotate), "-rotate -90 " );
4720
4721 #ifdef HAVE_OLD_GV
4722 xastir_snprintf(format, sizeof(format), "-landscape " );
4723 #else // HAVE_OLD_GV
4724 xastir_snprintf(format, sizeof(format), "--orientation=landscape " );
4725 #endif // HAVE_OLD_GV
4726
4727 }
4728 else if ( print_auto_rotation )
4729 {
4730 // Check whether the width or the height of the
4731 // pixmap is greater. If width is greater than
4732 // height, rotate the image by 270 degrees.
4733 if (screen_width > screen_height)
4734 {
4735 xastir_snprintf(rotate, sizeof(rotate), "-rotate -90 " );
4736
4737 #ifdef HAVE_OLD_GV
4738 xastir_snprintf(format, sizeof(format), "-landscape " );
4739 #else // HAVE_OLD_GV
4740 xastir_snprintf(format, sizeof(format), "--orientation=landscape " );
4741 #endif // HAVE_OLD_GV
4742
4743 if (debug_level & 512)
4744 {
4745 fprintf(stderr,"Rotating\n");
4746 }
4747 }
4748 else
4749 {
4750 rotate[0] = '\0'; // Empty string
4751 if (debug_level & 512)
4752 {
4753 fprintf(stderr,"Not Rotating\n");
4754 }
4755 }
4756 }
4757 else
4758 {
4759 rotate[0] = '\0'; // Empty string
4760 if (debug_level & 512)
4761 {
4762 fprintf(stderr,"Not Rotating\n");
4763 }
4764 }
4765
4766
4767 // Higher print densities require more memory and time
4768 // to process
4769 xastir_snprintf(density, sizeof(density), "-density %dx%d", print_resolution,
4770 print_resolution );
4771
4772 xastir_snprintf(temp, sizeof(temp), "%s", langcode("PRINT0013") );
4773 statusline(temp,1); // Converting to Postscript...
4774
4775
4776 // Filters:
4777 // Point (ok at higher dpi's)
4778 // Box (not too bad)
4779 // Triangle (no)
4780 // Hermite (no)
4781 // Hanning (no)
4782 // Hamming (no)
4783 // Blackman (better but still not good)
4784 // Gaussian (no)
4785 // Quadratic (no)
4786 // Cubic (no)
4787 // Catrom (not too bad)
4788 // Mitchell (no)
4789 // Lanczos (no)
4790 // Bessel (no)
4791 // Sinc (not too bad)
4792
4793 }
4794
4795 #ifdef HAVE_CONVERT
4796 strcpy(command, CONVERT_PATH);
4797 command[sizeof(command)-1] = '\0'; // Terminate string
4798 strcat(command, " -filter Point ");
4799 command[sizeof(command)-1] = '\0'; // Terminate string
4800 strcat(command, mono);
4801 command[sizeof(command)-1] = '\0'; // Terminate string
4802 strcat(command, invert);
4803 command[sizeof(command)-1] = '\0'; // Terminate string
4804 strcat(command, rotate);
4805 command[sizeof(command)-1] = '\0'; // Terminate string
4806 strcat(command, scale);
4807 command[sizeof(command)-1] = '\0'; // Terminate string
4808 strcat(command, density);
4809 command[sizeof(command)-1] = '\0'; // Terminate string
4810 strcat(command, " ");
4811 command[sizeof(command)-1] = '\0'; // Terminate string
4812 strcat(command, xpm_filename);
4813 command[sizeof(command)-1] = '\0'; // Terminate string
4814 strcat(command, " ");
4815 command[sizeof(command)-1] = '\0'; // Terminate string
4816 strcat(command, ps_filename);
4817 command[sizeof(command)-1] = '\0'; // Terminate string
4818
4819 if ( debug_level & 512 )
4820 {
4821 fprintf(stderr,"%s\n", command );
4822 }
4823
4824 if ( system( command ) != 0 )
4825 {
4826 // fprintf(stderr,"\n\nPrint: Couldn't convert from XPM to PS!\n\n\n");
4827 popup_message_always(langcode("POPEM00035"),
4828 "Couldn't convert from XPM to PS!");
4829 return;
4830 }
4831 #endif // HAVE_CONVERT
4832
4833 chmod( ps_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
4834
4835 // Delete temporary xpm file
4836 if ( !(debug_level & 512) )
4837 {
4838 unlink( xpm_filename );
4839 }
4840
4841 if ( debug_level & 512 )
4842 {
4843 fprintf(stderr,"Printing postscript file %s\n", ps_filename);
4844 }
4845
4846 // Since we could be running SUID root, we don't want to be
4847 // calling "system" anyway. Several problems with it.
4848
4849 // Bring up the postscript viewer
4850 strcpy(command, previewer_program);
4851 command[sizeof(command)-1] = '\0'; // Terminate string
4852 strcat(command, " ");
4853 command[sizeof(command)-1] = '\0'; // Terminate string
4854 strcat(command, format);
4855 command[sizeof(command)-1] = '\0'; // Terminate string
4856 strcat(command, " ");
4857 command[sizeof(command)-1] = '\0'; // Terminate string
4858 strcat(command, ps_filename);
4859 command[sizeof(command)-1] = '\0'; // Terminate string
4860 strcat(command, " &");
4861 command[sizeof(command)-1] = '\0'; // Terminate string
4862
4863 if ( debug_level & 512 )
4864 {
4865 fprintf(stderr,"%s\n", command);
4866 }
4867
4868 if (previewer_program[0] == '\0')
4869 {
4870 // fprintf(stderr,"\n\nPrint: No print previewer defined!\n\n\n");
4871 popup_message_always(langcode("POPEM00035"),
4872 "No print previewer defined!");
4873 return;
4874 }
4875
4876 if ( system( command ) != 0 )
4877 {
4878 // fprintf(stderr,"\n\nPrint: Couldn't bring up the postscript viewer!\n\n\n");
4879 popup_message_always(langcode("POPEM00035"),
4880 "Couldn't bring up the viewer!");
4881 return;
4882 }
4883
4884 /*
4885 if ( !(debug_level & 512) )
4886 unlink( ps_filename );
4887 */
4888
4889 if ( debug_level & 512 )
4890 {
4891 fprintf(stderr," Done printing.\n");
4892 }
4893 }
4894
4895 xastir_snprintf(temp, sizeof(temp), "%s", langcode("PRINT0014") );
4896 statusline(temp,1); // Finished creating print file.
4897
4898 //popup_message( langcode("PRINT0015"), langcode("PRINT0014") );
4899
4900 #endif // NO_XPM
4901
4902 }
4903
4904
4905
4906
4907
4908 /*
4909 * Auto_rotate
4910 *
4911 */
Auto_rotate(Widget UNUSED (widget),XtPointer clientData,XtPointer callData)4912 static void Auto_rotate( Widget UNUSED(widget), XtPointer clientData, XtPointer callData)
4913 {
4914 char *which = (char *)clientData;
4915 XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *)callData;
4916
4917 if(state->set)
4918 {
4919 print_auto_rotation = atoi(which);
4920 print_rotated = 0;
4921 XmToggleButtonSetState(rotate_90, FALSE, FALSE);
4922 }
4923 else
4924 {
4925 print_auto_rotation = 0;
4926 }
4927 }
4928
4929
4930
4931
4932
4933 /*
4934 * Rotate_90
4935 *
4936 */
Rotate_90(Widget UNUSED (widget),XtPointer clientData,XtPointer callData)4937 static void Rotate_90( Widget UNUSED(widget), XtPointer clientData, XtPointer callData)
4938 {
4939 char *which = (char *)clientData;
4940 XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *)callData;
4941
4942 if(state->set)
4943 {
4944 print_rotated = atoi(which);
4945 print_auto_rotation = 0;
4946 XmToggleButtonSetState(auto_rotate, FALSE, FALSE);
4947 }
4948 else
4949 {
4950 print_rotated = 0;
4951 }
4952 }
4953
4954
4955
4956
4957
4958 /*
4959 * Auto_scale
4960 *
4961 */
Auto_scale(Widget UNUSED (widget),XtPointer clientData,XtPointer callData)4962 static void Auto_scale( Widget UNUSED(widget), XtPointer clientData, XtPointer callData)
4963 {
4964 char *which = (char *)clientData;
4965 XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *)callData;
4966
4967 if(state->set)
4968 {
4969 print_auto_scale = atoi(which);
4970 }
4971 else
4972 {
4973 print_auto_scale = 0;
4974 }
4975 }
4976
4977
4978
4979
4980
4981 /*
4982 * Monochrome
4983 *
4984 */
Monochrome(Widget UNUSED (widget),XtPointer clientData,XtPointer callData)4985 void Monochrome( Widget UNUSED(widget), XtPointer clientData, XtPointer callData)
4986 {
4987 char *which = (char *)clientData;
4988 XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *)callData;
4989
4990 if(state->set)
4991 {
4992 print_in_monochrome = atoi(which);
4993 }
4994 else
4995 {
4996 print_in_monochrome = 0;
4997 }
4998 }
4999
5000
5001
5002
5003
5004 /*
5005 * Invert
5006 *
5007 */
Invert(Widget UNUSED (widget),XtPointer clientData,XtPointer callData)5008 static void Invert( Widget UNUSED(widget), XtPointer clientData, XtPointer callData)
5009 {
5010 char *which = (char *)clientData;
5011 XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *)callData;
5012
5013 if(state->set)
5014 {
5015 print_invert = atoi(which);
5016 }
5017 else
5018 {
5019 print_invert = 0;
5020 }
5021 }
5022
5023
5024
5025
5026
5027 // Print_properties: Prints the drawing area to a PostScript file.
5028 // Provides various togglebuttons for configuring the "gv" previewer
5029 // only.
5030 //
5031 // Perhaps later:
5032 // 1) Select an area on the screen to print
5033 // 2) -label
5034 //
Print_properties(Widget w,XtPointer UNUSED (clientData),XtPointer UNUSED (callData))5035 void Print_properties( Widget w, XtPointer UNUSED(clientData), XtPointer UNUSED(callData) )
5036 {
5037 static Widget pane, scrollwindow, form, button_ok, button_cancel,
5038 sep, auto_scale,
5039 // paper_size, paper_size_data, scale, scale_data, blank_background,
5040 // res_label1, res_label2, res_x, res_y,
5041 monochrome, invert;
5042 Atom delw;
5043
5044 // Get rid of the Print dialog
5045 Print_postscript_destroy_shell(w, print_postscript_dialog, NULL );
5046
5047
5048 // If we're not using "gv", skip the entire dialog below and go
5049 // straight to the actual previewer function.
5050 //
5051 if ( !strstr(previewer_program,"gv") )
5052 {
5053 Print_preview(w, NULL, NULL);
5054 return;
5055 }
5056
5057
5058 if (!print_properties_dialog)
5059 {
5060
5061
5062 begin_critical_section(&print_properties_dialog_lock, "maps.c:Print_properties" );
5063
5064
5065 print_properties_dialog = XtVaCreatePopupShell(langcode("PRINT0001"),
5066 xmDialogShellWidgetClass, appshell,
5067 XmNdeleteResponse, XmDESTROY,
5068 XmNdefaultPosition, FALSE,
5069 XmNfontList, fontlist1,
5070 NULL);
5071
5072 pane = XtVaCreateWidget("Print_properties pane",xmPanedWindowWidgetClass, print_properties_dialog,
5073 XmNbackground, colors[0xff],
5074 NULL);
5075
5076 scrollwindow = XtVaCreateManagedWidget("scrollwindow",
5077 xmScrolledWindowWidgetClass,
5078 pane,
5079 XmNscrollingPolicy, XmAUTOMATIC,
5080 NULL);
5081
5082 form = XtVaCreateWidget("Print_properties form",
5083 xmFormWidgetClass,
5084 scrollwindow,
5085 XmNfractionBase, 2,
5086 XmNbackground, colors[0xff],
5087 XmNautoUnmanage, FALSE,
5088 XmNshadowThickness, 1,
5089 NULL);
5090
5091 /*
5092 paper_size = XtVaCreateManagedWidget(langcode("PRINT0002"),xmLabelWidgetClass, form,
5093 XmNtopAttachment, XmATTACH_FORM,
5094 XmNtopOffset, 10,
5095 XmNbottomAttachment, XmATTACH_NONE,
5096 XmNleftAttachment, XmATTACH_FORM,
5097 XmNleftOffset, 10,
5098 XmNrightAttachment, XmATTACH_NONE,
5099 XmNbackground, colors[0xff],
5100 XmNfontList, fontlist1,
5101 NULL);
5102 XtSetSensitive(paper_size,FALSE);
5103
5104
5105 paper_size_data = XtVaCreateManagedWidget("Print_properties paper_size_data", xmTextFieldWidgetClass, form,
5106 XmNeditable, TRUE,
5107 XmNcursorPositionVisible, TRUE,
5108 XmNsensitive, TRUE,
5109 XmNshadowThickness, 1,
5110 XmNcolumns, 15,
5111 XmNwidth, ((15*7)+2),
5112 XmNmaxLength, 15,
5113 XmNbackground, colors[0x0f],
5114 XmNtopAttachment,XmATTACH_FORM,
5115 XmNtopOffset, 5,
5116 XmNbottomAttachment,XmATTACH_NONE,
5117 XmNleftAttachment, XmATTACH_WIDGET,
5118 XmNleftWidget, paper_size,
5119 XmNleftOffset, 10,
5120 XmNrightAttachment,XmATTACH_FORM,
5121 XmNrightOffset, 10,
5122 XmNnavigationType, XmTAB_GROUP,
5123 XmNtraversalOn, TRUE,
5124 XmNfontList, fontlist1,
5125 NULL);
5126 XtSetSensitive(paper_size_data,FALSE);
5127 */
5128
5129
5130 auto_rotate = XtVaCreateManagedWidget(langcode("PRINT0003"),xmToggleButtonWidgetClass,form,
5131 // XmNtopAttachment, XmATTACH_WIDGET,
5132 // XmNtopWidget, paper_size_data,
5133 // XmNtopOffset, 5,
5134 XmNtopAttachment, XmATTACH_FORM,
5135 XmNtopOffset, 10,
5136 XmNbottomAttachment, XmATTACH_NONE,
5137 XmNleftAttachment, XmATTACH_FORM,
5138 XmNleftOffset,10,
5139 XmNrightAttachment, XmATTACH_NONE,
5140 XmNbackground, colors[0xff],
5141 XmNnavigationType, XmTAB_GROUP,
5142 XmNtraversalOn, TRUE,
5143 XmNfontList, fontlist1,
5144 NULL);
5145 XtAddCallback(auto_rotate,XmNvalueChangedCallback,Auto_rotate,"1");
5146
5147
5148 rotate_90 = XtVaCreateManagedWidget(langcode("PRINT0004"),xmToggleButtonWidgetClass,form,
5149 // XmNtopAttachment, XmATTACH_WIDGET,
5150 // XmNtopWidget, paper_size_data,
5151 // XmNtopOffset, 5,
5152 XmNtopAttachment, XmATTACH_FORM,
5153 XmNtopOffset, 10,
5154 XmNbottomAttachment, XmATTACH_NONE,
5155 XmNleftAttachment, XmATTACH_WIDGET,
5156 XmNleftWidget, auto_rotate,
5157 XmNleftOffset,10,
5158 XmNrightAttachment, XmATTACH_FORM,
5159 XmNrightOffset, 10,
5160 XmNbackground, colors[0xff],
5161 XmNnavigationType, XmTAB_GROUP,
5162 XmNtraversalOn, TRUE,
5163 XmNfontList, fontlist1,
5164 NULL);
5165 XtAddCallback(rotate_90,XmNvalueChangedCallback,Rotate_90,"1");
5166
5167
5168 auto_scale = XtVaCreateManagedWidget(langcode("PRINT0005"),xmToggleButtonWidgetClass,form,
5169 XmNtopAttachment, XmATTACH_WIDGET,
5170 XmNtopWidget, auto_rotate,
5171 XmNtopOffset, 5,
5172 XmNbottomAttachment, XmATTACH_NONE,
5173 XmNleftAttachment, XmATTACH_FORM,
5174 XmNleftOffset,10,
5175 XmNrightAttachment, XmATTACH_NONE,
5176 XmNbackground, colors[0xff],
5177 XmNnavigationType, XmTAB_GROUP,
5178 XmNtraversalOn, TRUE,
5179 XmNfontList, fontlist1,
5180 NULL);
5181 XtAddCallback(auto_scale,XmNvalueChangedCallback,Auto_scale,"1");
5182
5183
5184 /*
5185 scale = XtVaCreateManagedWidget(langcode("PRINT0006"),xmLabelWidgetClass, form,
5186 XmNtopAttachment, XmATTACH_WIDGET,
5187 XmNtopWidget, auto_rotate,
5188 XmNtopOffset, 10,
5189 XmNbottomAttachment, XmATTACH_NONE,
5190 XmNleftAttachment, XmATTACH_WIDGET,
5191 XmNleftWidget, auto_scale,
5192 XmNleftOffset, 10,
5193 XmNrightAttachment, XmATTACH_NONE,
5194 XmNbackground, colors[0xff],
5195 XmNfontList, fontlist1,
5196 NULL);
5197 XtSetSensitive(scale,FALSE);
5198
5199
5200 scale_data = XtVaCreateManagedWidget("Print_properties scale_data", xmTextFieldWidgetClass, form,
5201 XmNeditable, TRUE,
5202 XmNcursorPositionVisible, TRUE,
5203 XmNsensitive, TRUE,
5204 XmNshadowThickness, 1,
5205 XmNcolumns, 15,
5206 XmNwidth, ((15*7)+2),
5207 XmNmaxLength, 15,
5208 XmNbackground, colors[0x0f],
5209 XmNtopAttachment,XmATTACH_WIDGET,
5210 XmNtopWidget, auto_rotate,
5211 XmNtopOffset, 5,
5212 XmNbottomAttachment,XmATTACH_NONE,
5213 XmNleftAttachment, XmATTACH_WIDGET,
5214 XmNleftWidget, scale,
5215 XmNleftOffset, 10,
5216 XmNrightAttachment,XmATTACH_FORM,
5217 XmNrightOffset, 10,
5218 XmNnavigationType, XmTAB_GROUP,
5219 XmNtraversalOn, TRUE,
5220 XmNfontList, fontlist1,
5221 NULL);
5222 XtSetSensitive(scale_data,FALSE);
5223 */
5224
5225
5226 /*
5227 blank_background = XtVaCreateManagedWidget(langcode("PRINT0007"),xmToggleButtonWidgetClass,form,
5228 XmNtopAttachment, XmATTACH_WIDGET,
5229 XmNtopWidget, scale_data,
5230 XmNtopWidget, auto_rotate,
5231 XmNtopOffset, 5,
5232 XmNbottomAttachment, XmATTACH_NONE,
5233 XmNleftAttachment, XmATTACH_FORM,
5234 XmNleftOffset ,10,
5235 XmNrightAttachment, XmATTACH_NONE,
5236 XmNbackground, colors[0xff],
5237 XmNnavigationType, XmTAB_GROUP,
5238 XmNtraversalOn, TRUE,
5239 XmNfontList, fontlist1,
5240 NULL);
5241 XtSetSensitive(blank_background,FALSE);
5242 */
5243
5244
5245 monochrome = XtVaCreateManagedWidget(langcode("PRINT0008"),xmToggleButtonWidgetClass,form,
5246 XmNtopAttachment, XmATTACH_WIDGET,
5247 // XmNtopWidget, blank_background,
5248 XmNtopWidget, auto_scale,
5249 XmNtopOffset, 5,
5250 XmNbottomAttachment, XmATTACH_NONE,
5251 XmNleftAttachment, XmATTACH_FORM,
5252 XmNleftOffset,10,
5253 XmNrightAttachment, XmATTACH_NONE,
5254 XmNbackground, colors[0xff],
5255 XmNnavigationType, XmTAB_GROUP,
5256 XmNtraversalOn, TRUE,
5257 XmNfontList, fontlist1,
5258 NULL);
5259 XtAddCallback(monochrome,XmNvalueChangedCallback,Monochrome,"1");
5260
5261
5262 invert = XtVaCreateManagedWidget(langcode("PRINT0016"),xmToggleButtonWidgetClass,form,
5263 XmNtopAttachment, XmATTACH_WIDGET,
5264 XmNtopWidget, monochrome,
5265 XmNtopOffset, 5,
5266 XmNbottomAttachment, XmATTACH_NONE,
5267 XmNleftAttachment, XmATTACH_FORM,
5268 XmNleftOffset,10,
5269 XmNrightAttachment, XmATTACH_NONE,
5270 XmNbackground, colors[0xff],
5271 XmNnavigationType, XmTAB_GROUP,
5272 XmNtraversalOn, TRUE,
5273 XmNfontList, fontlist1,
5274 NULL);
5275 XtAddCallback(invert,XmNvalueChangedCallback,Invert,"1");
5276
5277
5278 /*
5279 res_label1 = XtVaCreateManagedWidget(langcode("PRINT0009"),xmLabelWidgetClass, form,
5280 XmNtopAttachment, XmATTACH_WIDGET,
5281 XmNtopWidget, invert,
5282 XmNtopOffset, 10,
5283 XmNbottomAttachment, XmATTACH_NONE,
5284 XmNleftAttachment, XmATTACH_FORM,
5285 XmNleftOffset, 10,
5286 XmNrightAttachment, XmATTACH_NONE,
5287 XmNbackground, colors[0xff],
5288 XmNfontList, fontlist1,
5289 NULL);
5290 XtSetSensitive(res_label1,FALSE);
5291
5292
5293 res_x = XtVaCreateManagedWidget("Print_properties resx_data", xmTextFieldWidgetClass, form,
5294 XmNeditable, TRUE,
5295 XmNcursorPositionVisible, TRUE,
5296 XmNsensitive, TRUE,
5297 XmNshadowThickness, 1,
5298 XmNcolumns, 15,
5299 XmNwidth, ((15*7)+2),
5300 XmNmaxLength, 15,
5301 XmNbackground, colors[0x0f],
5302 XmNtopAttachment,XmATTACH_WIDGET,
5303 XmNtopWidget, invert,
5304 XmNtopOffset, 5,
5305 XmNbottomAttachment,XmATTACH_NONE,
5306 XmNleftAttachment, XmATTACH_WIDGET,
5307 XmNleftWidget, res_label1,
5308 XmNleftOffset, 10,
5309 XmNrightAttachment,XmATTACH_NONE,
5310 XmNnavigationType, XmTAB_GROUP,
5311 XmNtraversalOn, TRUE,
5312 XmNfontList, fontlist1,
5313 NULL);
5314 XtSetSensitive(res_x,FALSE);
5315
5316
5317 res_label2 = XtVaCreateManagedWidget("X",xmLabelWidgetClass, form,
5318 XmNtopAttachment, XmATTACH_WIDGET,
5319 XmNtopWidget, invert,
5320 XmNtopOffset, 10,
5321 XmNbottomAttachment, XmATTACH_NONE,
5322 XmNleftAttachment, XmATTACH_WIDGET,
5323 XmNleftWidget, res_x,
5324 XmNleftOffset, 10,
5325 XmNrightAttachment, XmATTACH_NONE,
5326 XmNbackground, colors[0xff],
5327 XmNfontList, fontlist1,
5328 NULL);
5329 XtSetSensitive(res_label2,FALSE);
5330
5331
5332 res_y = XtVaCreateManagedWidget("Print_properties res_y_data", xmTextFieldWidgetClass, form,
5333 XmNeditable, TRUE,
5334 XmNcursorPositionVisible, TRUE,
5335 XmNsensitive, TRUE,
5336 XmNshadowThickness, 1,
5337 XmNcolumns, 15,
5338 XmNwidth, ((15*7)+2),
5339 XmNmaxLength, 15,
5340 XmNbackground, colors[0x0f],
5341 XmNtopAttachment,XmATTACH_WIDGET,
5342 XmNtopWidget, invert,
5343 XmNtopOffset, 5,
5344 XmNbottomAttachment,XmATTACH_NONE,
5345 XmNleftAttachment, XmATTACH_WIDGET,
5346 XmNleftWidget, res_label2,
5347 XmNleftOffset, 10,
5348 XmNrightAttachment,XmATTACH_FORM,
5349 XmNrightOffset, 10,
5350 XmNnavigationType, XmTAB_GROUP,
5351 XmNtraversalOn, TRUE,
5352 XmNfontList, fontlist1,
5353 NULL);
5354 XtSetSensitive(res_y,FALSE);
5355 */
5356
5357
5358 sep = XtVaCreateManagedWidget("Print_properties sep", xmSeparatorGadgetClass,form,
5359 XmNorientation, XmHORIZONTAL,
5360 XmNtopAttachment,XmATTACH_WIDGET,
5361 // XmNtopWidget, res_y,
5362 XmNtopWidget, invert,
5363 XmNtopOffset, 10,
5364 XmNbottomAttachment,XmATTACH_NONE,
5365 XmNleftAttachment, XmATTACH_FORM,
5366 XmNrightAttachment,XmATTACH_FORM,
5367 XmNbackground, colors[0xff],
5368 XmNfontList, fontlist1,
5369 NULL);
5370
5371
5372 // button_ok = XtVaCreateManagedWidget(langcode("PRINT0011"),xmPushButtonGadgetClass, form,
5373 button_ok = XtVaCreateManagedWidget(langcode("PRINT0010"),xmPushButtonGadgetClass, form,
5374 XmNtopAttachment, XmATTACH_WIDGET,
5375 XmNtopWidget, sep,
5376 XmNtopOffset, 5,
5377 XmNbottomAttachment, XmATTACH_FORM,
5378 XmNbottomOffset, 5,
5379 XmNleftAttachment, XmATTACH_POSITION,
5380 XmNleftPosition, 0,
5381 XmNleftOffset, 3,
5382 XmNrightAttachment, XmATTACH_POSITION,
5383 XmNrightPosition, 1,
5384 XmNrightOffset, 2,
5385 XmNbackground, colors[0xff],
5386 XmNnavigationType, XmTAB_GROUP,
5387 XmNtraversalOn, TRUE,
5388 XmNfontList, fontlist1,
5389 NULL);
5390
5391
5392 button_cancel = XtVaCreateManagedWidget(langcode("UNIOP00002"),xmPushButtonGadgetClass, form,
5393 XmNtopAttachment, XmATTACH_WIDGET,
5394 XmNtopWidget, sep,
5395 XmNtopOffset, 5,
5396 XmNbottomAttachment, XmATTACH_FORM,
5397 XmNbottomOffset, 5,
5398 XmNleftAttachment, XmATTACH_POSITION,
5399 XmNleftPosition, 1,
5400 XmNleftOffset, 3,
5401 XmNrightAttachment, XmATTACH_POSITION,
5402 XmNrightPosition, 2,
5403 XmNrightOffset, 5,
5404 XmNbackground, colors[0xff],
5405 XmNnavigationType, XmTAB_GROUP,
5406 XmNtraversalOn, TRUE,
5407 XmNfontList, fontlist1,
5408 NULL);
5409
5410
5411 XtAddCallback(button_ok, XmNactivateCallback, Print_preview, NULL );
5412 XtAddCallback(button_cancel, XmNactivateCallback, Print_properties_destroy_shell, print_properties_dialog);
5413
5414
5415 XmToggleButtonSetState(rotate_90,FALSE,FALSE);
5416 XmToggleButtonSetState(auto_rotate,TRUE,FALSE);
5417
5418
5419 if (print_auto_rotation)
5420 {
5421 XmToggleButtonSetState(auto_rotate, TRUE, TRUE);
5422 }
5423 else
5424 {
5425 XmToggleButtonSetState(auto_rotate, FALSE, TRUE);
5426 }
5427
5428
5429 if (print_rotated)
5430 {
5431 XmToggleButtonSetState(rotate_90, TRUE, TRUE);
5432 }
5433 else
5434 {
5435 XmToggleButtonSetState(rotate_90, FALSE, TRUE);
5436 }
5437
5438
5439 if (print_in_monochrome)
5440 {
5441 XmToggleButtonSetState(monochrome, TRUE, FALSE);
5442 }
5443 else
5444 {
5445 XmToggleButtonSetState(monochrome, FALSE, FALSE);
5446 }
5447
5448
5449 if (print_invert)
5450 {
5451 XmToggleButtonSetState(invert, TRUE, FALSE);
5452 }
5453 else
5454 {
5455 XmToggleButtonSetState(invert, FALSE, FALSE);
5456 }
5457
5458
5459 if (print_auto_scale)
5460 {
5461 XmToggleButtonSetState(auto_scale, TRUE, TRUE);
5462 }
5463 else
5464 {
5465 XmToggleButtonSetState(auto_scale, FALSE, TRUE);
5466 }
5467
5468
5469 // XmTextFieldSetString(paper_size_data,print_paper_size);
5470
5471
5472 end_critical_section(&print_properties_dialog_lock, "maps.c:Print_properties" );
5473
5474
5475 pos_dialog(print_properties_dialog);
5476
5477
5478 delw = XmInternAtom(XtDisplay(print_properties_dialog),"WM_DELETE_WINDOW", FALSE);
5479 XmAddWMProtocolCallback(print_properties_dialog, delw, Print_properties_destroy_shell, (XtPointer)print_properties_dialog);
5480
5481
5482 XtManageChild(form);
5483 XtManageChild(pane);
5484
5485 resize_dialog(form, print_properties_dialog);
5486
5487 XtPopup(print_properties_dialog,XtGrabNone);
5488
5489
5490 // Move focus to the Cancel button. This appears to highlight the
5491 // button fine, but we're not able to hit the <Enter> key to
5492 // have that default function happen. Note: We _can_ hit the
5493 // <SPACE> key, and that activates the option.
5494 // XmUpdateDisplay(print_properties_dialog);
5495 XmProcessTraversal(button_cancel, XmTRAVERSE_CURRENT);
5496
5497
5498 }
5499 else
5500 {
5501 (void)XRaiseWindow(XtDisplay(print_properties_dialog), XtWindow(print_properties_dialog));
5502 }
5503 }
5504
5505
5506
5507
5508
5509 // General print dialog. From here we can either print Postscript
5510 // files to the device selected in this dialog, or head off to a
5511 // print preview program that might allow us a variety of print
5512 // options. From here we should be able to set the print device
5513 // and the print preview program & path.
5514 //
Print_Postscript(Widget UNUSED (w),XtPointer UNUSED (clientData),XtPointer UNUSED (callData))5515 void Print_Postscript( Widget UNUSED(w), XtPointer UNUSED(clientData), XtPointer UNUSED(callData) )
5516 {
5517 static Widget pane, scrollwindow, form, button_print, button_cancel,
5518 sep, button_preview;
5519 Atom delw;
5520
5521 if (!print_postscript_dialog)
5522 {
5523
5524
5525 begin_critical_section(&print_postscript_dialog_lock, "maps.c:Print_Postscript" );
5526
5527
5528 print_postscript_dialog = XtVaCreatePopupShell(langcode("PULDNFI015"),
5529 xmDialogShellWidgetClass, appshell,
5530 XmNdeleteResponse, XmDESTROY,
5531 XmNdefaultPosition, FALSE,
5532 XmNfontList, fontlist1,
5533 NULL);
5534
5535 pane = XtVaCreateWidget("Print_postscript pane",xmPanedWindowWidgetClass, print_postscript_dialog,
5536 XmNbackground, colors[0xff],
5537 NULL);
5538
5539 scrollwindow = XtVaCreateManagedWidget("scrollwindow",
5540 xmScrolledWindowWidgetClass,
5541 pane,
5542 XmNscrollingPolicy, XmAUTOMATIC,
5543 NULL);
5544
5545 form = XtVaCreateWidget("Print_postscript form",
5546 xmFormWidgetClass,
5547 scrollwindow,
5548 XmNfractionBase, 3,
5549 XmNbackground, colors[0xff],
5550 XmNautoUnmanage, FALSE,
5551 XmNshadowThickness, 1,
5552 NULL);
5553
5554 // "Direct to:"
5555 button_print = XtVaCreateManagedWidget(langcode("PRINT1001"),xmPushButtonGadgetClass, form,
5556 XmNtopAttachment, XmATTACH_FORM,
5557 XmNtopOffset, 5,
5558 XmNbottomAttachment, XmATTACH_NONE,
5559 XmNleftAttachment, XmATTACH_FORM,
5560 XmNleftOffset, 5,
5561 XmNrightAttachment, XmATTACH_NONE,
5562 XmNbackground, colors[0xff],
5563 XmNnavigationType, XmTAB_GROUP,
5564 XmNtraversalOn, TRUE,
5565 XmNfontList, fontlist1,
5566 NULL);
5567
5568
5569 printer_data = XtVaCreateManagedWidget("Print_Postscript printer_data", xmTextFieldWidgetClass, form,
5570 XmNeditable, TRUE,
5571 XmNcursorPositionVisible, TRUE,
5572 XmNsensitive, TRUE,
5573 XmNshadowThickness, 1,
5574 XmNcolumns, 40,
5575 XmNwidth, ((40*7)+2),
5576 XmNmaxLength, MAX_FILENAME,
5577 XmNbackground, colors[0x0f],
5578 XmNtopAttachment,XmATTACH_FORM,
5579 XmNtopOffset, 5,
5580 XmNbottomAttachment,XmATTACH_NONE,
5581 XmNleftAttachment, XmATTACH_WIDGET,
5582 XmNleftWidget, button_print,
5583 XmNleftOffset, 10,
5584 XmNrightAttachment,XmATTACH_FORM,
5585 XmNrightOffset, 5,
5586 XmNnavigationType, XmTAB_GROUP,
5587 XmNtraversalOn, TRUE,
5588 XmNfontList, fontlist1,
5589 NULL);
5590
5591
5592 // "Via Previewer:"
5593 button_preview = XtVaCreateManagedWidget(langcode("PRINT1002"),xmPushButtonGadgetClass, form,
5594 XmNtopAttachment, XmATTACH_WIDGET,
5595 XmNtopWidget, button_print,
5596 XmNtopOffset, 5,
5597 XmNbottomAttachment, XmATTACH_NONE,
5598 XmNleftAttachment, XmATTACH_FORM,
5599 XmNleftOffset, 5,
5600 XmNrightAttachment, XmATTACH_NONE,
5601 XmNbackground, colors[0xff],
5602 XmNnavigationType, XmTAB_GROUP,
5603 XmNtraversalOn, TRUE,
5604 XmNfontList, fontlist1,
5605 NULL);
5606
5607
5608 previewer_data = XtVaCreateManagedWidget("Print_Postscript previewer_data", xmTextFieldWidgetClass, form,
5609 XmNeditable, TRUE,
5610 XmNcursorPositionVisible, TRUE,
5611 XmNsensitive, TRUE,
5612 XmNshadowThickness, 1,
5613 XmNcolumns, 40,
5614 XmNwidth, ((40*7)+2),
5615 XmNmaxLength, MAX_FILENAME,
5616 XmNbackground, colors[0x0f],
5617 XmNtopAttachment,XmATTACH_WIDGET,
5618 XmNtopWidget, button_print,
5619 XmNtopOffset, 5,
5620 XmNbottomAttachment,XmATTACH_NONE,
5621 XmNleftAttachment, XmATTACH_WIDGET,
5622 XmNleftWidget, button_preview,
5623 XmNleftOffset, 10,
5624 XmNrightAttachment,XmATTACH_FORM,
5625 XmNrightOffset, 5,
5626 XmNnavigationType, XmTAB_GROUP,
5627 XmNtraversalOn, TRUE,
5628 XmNfontList, fontlist1,
5629 NULL);
5630
5631
5632 sep = XtVaCreateManagedWidget("Print_postscript sep", xmSeparatorGadgetClass,form,
5633 XmNorientation, XmHORIZONTAL,
5634 XmNtopAttachment,XmATTACH_WIDGET,
5635 XmNtopWidget, button_preview,
5636 XmNtopOffset, 10,
5637 XmNbottomAttachment,XmATTACH_NONE,
5638 XmNleftAttachment, XmATTACH_FORM,
5639 XmNrightAttachment,XmATTACH_FORM,
5640 XmNbackground, colors[0xff],
5641 XmNfontList, fontlist1,
5642 NULL);
5643
5644
5645 button_cancel = XtVaCreateManagedWidget(langcode("UNIOP00002"),xmPushButtonGadgetClass, form,
5646 XmNtopAttachment, XmATTACH_WIDGET,
5647 XmNtopWidget, sep,
5648 XmNtopOffset, 5,
5649 XmNbottomAttachment, XmATTACH_FORM,
5650 XmNbottomOffset, 5,
5651 XmNleftAttachment, XmATTACH_FORM,
5652 XmNleftOffset, 5,
5653 XmNrightAttachment, XmATTACH_FORM,
5654 XmNrightOffset, 5,
5655 XmNbackground, colors[0xff],
5656 XmNnavigationType, XmTAB_GROUP,
5657 XmNtraversalOn, TRUE,
5658 XmNfontList, fontlist1,
5659 NULL);
5660
5661
5662 XtAddCallback(button_preview, XmNactivateCallback, Print_properties, NULL );
5663 XtAddCallback(button_print, XmNactivateCallback, Print_window, NULL );
5664 XtAddCallback(button_cancel, XmNactivateCallback, Print_postscript_destroy_shell, print_postscript_dialog);
5665
5666 // Fill in the text fields from persistent variables out of the config file.
5667 XmTextFieldSetString(printer_data, printer_program);
5668 XmTextFieldSetString(previewer_data, previewer_program);
5669
5670 end_critical_section(&print_postscript_dialog_lock, "maps.c:Print_Postscript" );
5671
5672
5673 pos_dialog(print_postscript_dialog);
5674
5675
5676 delw = XmInternAtom(XtDisplay(print_postscript_dialog),"WM_DELETE_WINDOW", FALSE);
5677 XmAddWMProtocolCallback(print_postscript_dialog, delw, Print_postscript_destroy_shell, (XtPointer)print_postscript_dialog);
5678
5679
5680 XtManageChild(form);
5681 XtManageChild(pane);
5682
5683 resize_dialog(form, print_postscript_dialog);
5684
5685 XtPopup(print_postscript_dialog,XtGrabNone);
5686
5687 // Move focus to the Cancel button. This appears to highlight the
5688 // button fine, but we're not able to hit the <Enter> key to
5689 // have that default function happen. Note: We _can_ hit the
5690 // <SPACE> key, and that activates the option.
5691 // XmUpdateDisplay(print_postscript_dialog);
5692 XmProcessTraversal(button_cancel, XmTRAVERSE_CURRENT);
5693
5694
5695 }
5696 else
5697 {
5698 (void)XRaiseWindow(XtDisplay(print_postscript_dialog), XtWindow(print_postscript_dialog));
5699 }
5700 }
5701
5702
5703
5704
5705
5706 // Create png image (for use in web browsers??). Requires that "convert"
5707 // from the ImageMagick package be installed on the system. At the
5708 // point this thread is started, the XPM file has already been
5709 // created. We now create a .geo file to go with the .png file.
5710 //
5711 #ifndef NO_XPM
snapshot_thread(void * UNUSED (arg))5712 static void* snapshot_thread(void * UNUSED(arg) )
5713 {
5714 char xpm_filename[MAX_FILENAME];
5715 char png_filename[MAX_FILENAME];
5716 char geo_filename[MAX_FILENAME];
5717 char kml_filename[MAX_FILENAME]; // filename for kml file that describes the png file in keyhole markup language
5718 char timestring[101]; // string representation of the time heard or the current time
5719 FILE *f;
5720 FILE *fk; // file handle for kml file
5721 time_t expire_time;
5722 #ifdef HAVE_CONVERT
5723 char command[MAX_FILENAME*2];
5724 #endif // HAVE_CONVERT
5725 char temp_base_dir[MAX_VALUE];
5726
5727 get_user_base_dir("tmp", temp_base_dir, sizeof(temp_base_dir));
5728
5729
5730 // The pthread_detach() call means we don't care about the
5731 // return code and won't use pthread_join() later. Makes
5732 // threading more efficient.
5733 (void)pthread_detach(pthread_self());
5734
5735 xastir_snprintf(xpm_filename,
5736 sizeof(xpm_filename),
5737 "%s/snapshot.xpm",
5738 temp_base_dir);
5739
5740 xastir_snprintf(png_filename,
5741 sizeof(png_filename),
5742 "%s/snapshot.png",
5743 temp_base_dir);
5744
5745 // Same for the .geo filename
5746 xastir_snprintf(geo_filename,
5747 sizeof(geo_filename),
5748 "%s/snapshot.geo",
5749 temp_base_dir);
5750
5751 // Same for the .kml filename
5752 xastir_snprintf(kml_filename,
5753 sizeof(kml_filename),
5754 "%s/snapshot.kml",
5755 temp_base_dir);
5756
5757
5758 // Create a .geo file to match the new png image
5759 // Likewise for a matching .kml file
5760 f = fopen(geo_filename,"w"); // Overwrite whatever file
5761 // is there.
5762 fk = fopen(kml_filename,"w");
5763
5764 if (f == NULL || fk == NULL)
5765 {
5766 if (f==NULL)
5767 {
5768 fprintf(stderr,"Couldn't open %s\n",geo_filename);
5769 }
5770 if (fk==NULL)
5771 {
5772 fprintf(stderr,"Couldn't open %s\n",kml_filename);
5773 }
5774 }
5775 else
5776 {
5777 float lat1, long1, lat2, long2;
5778
5779
5780 long1 = f_NW_corner_longitude;
5781 lat1 = f_NW_corner_latitude;
5782 long2 = f_SE_corner_longitude;
5783 lat2 = f_SE_corner_latitude;
5784
5785 // FILENAME world1.xpm
5786 // # x y lon lat
5787 // TIEPOINT 0 0 -180 90
5788 // TIEPOINT 639 319 180 -90
5789 // IMAGESIZE 640 320
5790 // REFRESH 250
5791
5792 fprintf(f,"FILENAME snapshot.png\n");
5793 fprintf(f,"# x y lon lat\n");
5794 fprintf(f,"TIEPOINT 0 0 %8.5f %8.5f\n",
5795 long1, lat1);
5796 fprintf(f,"TIEPOINT %-4d %-4d %8.5f %8.5f\n",
5797 (int)screen_width-1, (int)screen_height-1, long2, lat2);
5798
5799 fprintf(f,"IMAGESIZE %-4d %-4d\n",
5800 (int)screen_width, (int)screen_height);
5801 fprintf(f,"REFRESH 250\n");
5802 fclose(f);
5803
5804 // Write a matching kml file that describes the location of the snapshot on
5805 // the Earth's surface.
5806 // Another kml file pointing to the location of this file with a networklinkcontrol element
5807 // and an update element loaded into a kml application should be able to reload this file
5808 // at regular intervals.
5809 // See kml documentation of:
5810 // <kml><NetworkLinkControl><linkName/><refreshMode/>
5811 //
5812 // <?xml version="1.0" encoding="UTF-8"?>
5813 // <kml xmlns="http://earth.google.com/kml/2.1">
5814 // <Document>
5815 // <NetworkLink>
5816 // <Link>
5817 // <href>http://www.example.com/cgi-bin/screenshot.kml</href>
5818 // <refreshMode>onExpire</refreshMode>
5819 // </Link>
5820 // </NetworkLink>
5821 // </Document>
5822 // </kml>
5823 //
5824 // TODO: Calculate a suitable range and tilt for viewing the snapshot draped on the
5825 // underlying terrain.
5826
5827 fprintf(fk,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
5828 fprintf(fk,"<kml xmlns=\"http://earth.google.com/kml/2.2\">\n");
5829 // Add an expire time matching the time when the next snapshot should
5830 // be produced, so that a network link with an onExpire refresh mode
5831 // will check for the next snapshot.
5832 expire_time = sec_now() + (time_t)(snapshot_interval * 60);
5833 if (get_w3cdtf_datetime(expire_time, timestring, False, False))
5834 {
5835 if (strlen(timestring) > 0)
5836 {
5837 fprintf(fk," <NetworkLinkControl>\n");
5838 fprintf(fk," <expires>%s</expires>\n",timestring);
5839 fprintf(fk," </NetworkLinkControl>\n");
5840 }
5841 }
5842 fprintf(fk," <Document>\n");
5843 fprintf(fk," <name>XASTIR Snapshot from %s</name>\n",my_callsign);
5844 fprintf(fk," <open>1</open>\n");
5845 fprintf(fk," <GroundOverlay>\n");
5846 fprintf(fk," <name>Xastir snapshot</name>\n");
5847 fprintf(fk," <visibility>1</visibility>\n");
5848 // timestamp the overlay with the current time
5849 if (get_w3cdtf_datetime(sec_now(), timestring, True, True))
5850 {
5851 if (strlen(timestring) > 0)
5852 {
5853 fprintf(fk," <TimeStamp><when>%s</when></TimeStamp>\n",timestring);
5854 fprintf(fk," <description>Overlay shows screen visible for %s in Xastir at %s.</description>\n",my_callsign,timestring);
5855 }
5856 }
5857 else
5858 {
5859 fprintf(fk," <description>Overlay shows screen visible for %s in Xastir.</description>\n",my_callsign);
5860 }
5861 fprintf(fk," <LookAt>\n");
5862 fprintf(fk," <longitude>%8.5f</longitude>\n",f_center_longitude);
5863 fprintf(fk," <latitude>%8.5f</latitude>\n",f_center_latitude);
5864 fprintf(fk," <altitude>0</altitude>\n");
5865 fprintf(fk," <range>30350.36838438907</range>\n"); // range in meters from viewer to lookat point
5866 fprintf(fk," <tilt>0</tilt>\n"); // 0 is looking straight down
5867 fprintf(fk," <altitudeMode>clampToGround</altitudeMode>\n");
5868 fprintf(fk," <heading>0</heading>\n"); // 0 is north at top, 90 east at top
5869 fprintf(fk," </LookAt>\n");
5870 fprintf(fk," <Icon>\n");
5871 fprintf(fk," <href>snapshot.png</href>\n");
5872 fprintf(fk," </Icon>\n");
5873 fprintf(fk," <LatLonBox>\n");
5874 fprintf(fk," <north>%8.5f</north>\n",lat1);
5875 fprintf(fk," <south>%8.5f</south>\n",lat2);
5876 fprintf(fk," <east>%8.5f</east>\n",long2);
5877 fprintf(fk," <west>%8.5f</west>\n",long1);
5878 fprintf(fk," <rotation>0</rotation>\n");
5879 fprintf(fk," </LatLonBox>\n");
5880 fprintf(fk," </GroundOverlay>\n");
5881 fprintf(fk," </Document>\n");
5882 fprintf(fk,"</kml>\n");
5883
5884 fclose(fk);
5885
5886 chmod( geo_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
5887 chmod( kml_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
5888 }
5889
5890
5891 if ( debug_level & 512 )
5892 {
5893 fprintf(stderr,"Convert %s ==> %s\n", xpm_filename, png_filename );
5894 }
5895
5896
5897 #ifdef HAVE_CONVERT
5898 // Convert it to a png file. This depends upon having the
5899 // ImageMagick command "convert" installed.
5900 strcpy(command, CONVERT_PATH);
5901 command[sizeof(command)-1] = '\0'; // Terminate string
5902 strcat(command, " -quality 100 -colors 256 ");
5903 command[sizeof(command)-1] = '\0'; // Terminate string
5904 strcat(command, xpm_filename);
5905 command[sizeof(command)-1] = '\0'; // Terminate string
5906 strcat(command, " ");
5907 command[sizeof(command)-1] = '\0'; // Terminate string
5908 strcat(command, png_filename);
5909 command[sizeof(command)-1] = '\0'; // Terminate string
5910
5911 if ( system( command ) != 0 )
5912 {
5913 // We _may_ have had an error. Check errno to make
5914 // sure.
5915 if (errno)
5916 {
5917 fprintf(stderr, "%s\n", strerror(errno));
5918 fprintf(stderr,
5919 "Failed to convert snapshot: %s -> %s\n",
5920 xpm_filename,
5921 png_filename);
5922 }
5923 else
5924 {
5925 fprintf(stderr,
5926 "System call return error: convert: %s -> %s\n",
5927 xpm_filename,
5928 png_filename);
5929 }
5930 }
5931 else
5932 {
5933 chmod( png_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
5934
5935 // // Delete temporary xpm file
5936 // unlink( xpm_filename );
5937
5938 if ( debug_level & 512 )
5939 {
5940 fprintf(stderr," Done creating png.\n");
5941 }
5942 }
5943
5944 #endif // HAVE_CONVERT
5945
5946 // Signify that we're all done and that another snapshot can
5947 // occur.
5948 doing_snapshot = 0;
5949
5950 return(NULL);
5951 }
5952 #endif // NO_XPM
5953
5954
5955
5956
5957
5958 // Starts a separate thread that creates a png image from the
5959 // current displayed image.
5960 //
Snapshot(void)5961 void Snapshot(void)
5962 {
5963 #ifndef NO_XPM
5964 pthread_t snapshot_thread_id;
5965 char xpm_filename[MAX_FILENAME];
5966 int xpmretval;
5967 #endif // NO_XPM
5968 char temp_base_dir[MAX_VALUE];
5969
5970 get_user_base_dir("tmp", temp_base_dir, sizeof(temp_base_dir));
5971
5972
5973 // Check whether we're already doing a snapshot
5974 if (doing_snapshot)
5975 {
5976 return;
5977 }
5978
5979 // Time to take another snapshot?
5980 // New snapshot interval based on slider in Configure Timing
5981 // dialog (in minutes)
5982 if (sec_now() < (last_snapshot + (snapshot_interval * 60)) )
5983 {
5984 return;
5985 }
5986
5987 last_snapshot = sec_now(); // Set up timer for next time
5988
5989
5990 #ifndef NO_XPM
5991
5992 if (debug_level & 512)
5993 {
5994 fprintf(stderr,"Taking Snapshot\n");
5995 }
5996
5997 doing_snapshot++;
5998
5999 // Set up the XPM filename that we'll use
6000 xastir_snprintf(xpm_filename,
6001 sizeof(xpm_filename),
6002 "%s/snapshot.xpm",
6003 temp_base_dir);
6004
6005
6006 if ( debug_level & 512 )
6007 {
6008 fprintf(stderr,"Creating %s\n", xpm_filename );
6009 }
6010
6011 // Create an XPM file from pixmap_final.
6012 if (chdir(temp_base_dir) != 0)
6013 {
6014 fprintf(stderr,"Couldn't chdir to %s directory for snapshot\n", temp_base_dir);
6015 return;
6016 }
6017
6018 xpmretval=XpmWriteFileFromPixmap(XtDisplay(appshell), // Display *display
6019 "snapshot.xpm", // char *filename
6020 pixmap_final, // Pixmap pixmap
6021 (Pixmap)NULL, // Pixmap shapemask
6022 NULL );
6023
6024 if (xpmretval != XpmSuccess)
6025 {
6026 fprintf(stderr,"ERROR writing %s: %s\n", xpm_filename,
6027 XpmGetErrorString(xpmretval));
6028 return;
6029 }
6030
6031 chmod( xpm_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
6032
6033
6034 //----- Start New Thread -----
6035
6036 //
6037 // Here we start a new thread. We'll communicate with the main
6038 // thread via global variables. Use mutex locks if there might
6039 // be a conflict as to when/how we're updating those variables.
6040 //
6041
6042 if (pthread_create(&snapshot_thread_id, NULL, snapshot_thread, NULL))
6043 {
6044 fprintf(stderr,"Error creating snapshot thread\n");
6045 }
6046 else
6047 {
6048 // We're off and running with the new thread!
6049 }
6050 #endif // NO_XPM
6051 }
6052
6053
6054
6055
6056
6057 // Function to remove double-quote characters and spaces that occur
6058 // outside of the double-quote characters.
clean_string(char * input)6059 void clean_string(char *input)
6060 {
6061 char *i;
6062 char *j;
6063
6064
6065 //fprintf(stderr,"|%s|\t",input);
6066
6067 // Remove any double quote characters
6068 i = index(input,'"'); // Find first quote character, if any
6069
6070 if (i != NULL)
6071 {
6072 j = index(i+1,'"'); // Find second quote character, if any
6073
6074 if (j != NULL) // Found two quote characters
6075 {
6076 j[0] = '\0'; // Terminate the string at the 2nd quote
6077 // Can't use strcpy here because it can't work with
6078 // overlapping strings. strcpy is a dangerous function
6079 // anyway and shouldn't be used.
6080 memmove(input, i+1, j-i);
6081 }
6082 else // We only found one quote character. What to do?
6083 {
6084 // fprintf(stderr,"clean_string: Only one quote found!\n");
6085 }
6086 }
6087 //fprintf(stderr,"|%s|\n",input);
6088
6089 // Remove leading/trailing spaces?
6090 }
6091
6092
6093
6094
6095
6096
6097
6098 // Test map visibility (on screen)
6099 //
6100 // Input parameters are in Xastir coordinate system (fastest for us)
6101 // check_percentage:
6102 // 0 = don't check
6103 // 1 = check map size versus viewport scale. Return 0 if map
6104 // is too large/small (percentage-wise) to be displayed.
6105 //
6106 // Returns: MAP_NOT_VIS if map is _not_ visible
6107 // MAP_IS_VIS if map _is_ visible
6108 //
6109 // Xastir Coordinate System:
6110 //
6111 // 0 (90 deg. or 90N)
6112 //
6113 // 0 (-180 deg. or 180W) 129,600,000 (180 deg. or 180E)
6114 //
6115 // 64,800,000 (-90 deg. or 90S)
6116 //
6117 // Note that we already have map_visible() and map_visible_lat_lon()
6118 // routines.
6119 //
map_onscreen(long left,long right,long top,long bottom,int UNUSED (check_percentage))6120 enum map_onscreen_enum map_onscreen(long left,
6121 long right,
6122 long top,
6123 long bottom,
6124 int UNUSED(check_percentage) )
6125 {
6126
6127 enum map_onscreen_enum in_window = MAP_NOT_VIS;
6128
6129
6130 if (map_visible((unsigned long)bottom,
6131 (unsigned long)top,
6132 (unsigned long)left,
6133 (unsigned long)right))
6134 {
6135 in_window = MAP_IS_VIS;
6136 //fprintf(stderr,"map_onscreen:Map is visible\n");
6137 }
6138
6139
6140 #ifdef MAP_SCALE_CHECK
6141 // Check whether map is too large/small for our current scale?
6142 // Check whether the map extents are < XX% of viewscreen size
6143 // (both directions), or viewscreen is > XX% of map extents
6144 // (either direction). This will knock out maps that are too
6145 // large/small to be displayed at this zoom level.
6146 //WE7U
6147 if (in_window && check_percentage)
6148 {
6149 long map_x, map_y, view_x, view_y;
6150 float percentage = 0.04;
6151
6152
6153 map_x = right - left;
6154 if (map_x < 0)
6155 {
6156 map_x = 0;
6157 }
6158
6159 map_y = bottom - top;
6160 if (map_y < 0)
6161 {
6162 map_y = 0;
6163 }
6164
6165 view_x = max_NW_corner_longitude - NW_corner_longitude;
6166 if (view_x < 0)
6167 {
6168 view_x = 0;
6169 }
6170
6171 view_y = max_NW_corner_latitude - NW_corner_latitude;
6172 if (view_y < 0)
6173 {
6174 view_y = 0;
6175 }
6176
6177 //fprintf(stderr,"\n map_x: %d\n", map_x);
6178 //fprintf(stderr," map_y: %d\n", map_y);
6179 //fprintf(stderr,"view_x: %d\n", view_x);
6180 //fprintf(stderr,"view_y: %d\n", view_y);
6181
6182 if ((map_x < (view_x * percentage )) && (map_y < (view_x * percentage)))
6183 {
6184 in_window = 0; // Send back "not-visible" flag
6185 fprintf(stderr,"map too small for view: %d%%\n",(int)(percentage * 100));
6186 }
6187
6188 // if ((view_x < (map_x * percentage)) && (view_y < (map_x * percentage))) {
6189 // in_window = 0; // Send back "not-visible" flag
6190 //fprintf(stderr,"view too small for map: %d%%\n",(int)(percentage * 100));
6191 // }
6192
6193 }
6194 #endif // MAP_SCALE_CHECK
6195
6196 //fprintf(stderr,"map_onscreen returning %d\n", in_window);
6197 return (in_window);
6198 }
6199
6200
6201
6202
6203
6204 // Function which checks whether a map is onscreen, but does so by
6205 // finding the map boundaries from the map index. The only input
6206 // parameter is the complete path/filename.
6207 //
6208 // Returns: MAP_NOT_VIS if map is _not_ visible
6209 // MAP_IS_VIS if map _is_ visible
6210 // MAP_NOT_INDEXED if the map is not in the index
6211 //
map_onscreen_index(char * filename)6212 enum map_onscreen_enum map_onscreen_index(char *filename)
6213 {
6214 unsigned long top, bottom, left, right;
6215 enum map_onscreen_enum onscreen = MAP_NOT_INDEXED;
6216 int max_zoom, min_zoom;
6217 int map_layer, draw_filled, usgs_drg, auto_maps; // Unused in this function
6218
6219
6220 if (index_retrieve(filename, &bottom, &top, &left, &right,
6221 &max_zoom, &min_zoom, &map_layer,
6222 &draw_filled, &usgs_drg, &auto_maps) )
6223 {
6224
6225 //fprintf(stderr, "Map found in index: %s\n", filename);
6226
6227 // Map was in the index, check for visibility and scale
6228 // Check whether the map extents are < XX% of viewscreen
6229 // size (both directions), or viewscreen is > XX% of map
6230 // extents (either direction). This will knock out maps
6231 // that are too large/small to be displayed at this zoom
6232 // level.
6233 if (map_onscreen(left, right, top, bottom, 1))
6234 {
6235
6236 //fprintf(stderr, "Map found in index and onscreen: %s\n", filename);
6237
6238 if (((max_zoom == 0) ||
6239 ((max_zoom != 0) && (scale_y <= max_zoom))) &&
6240 ((min_zoom == 0) ||
6241 ((min_zoom != 0) && (scale_y >= min_zoom))))
6242 {
6243
6244 onscreen = MAP_IS_VIS;
6245 //fprintf(stderr,"Map in the zoom zone: %s\n",filename);
6246 }
6247 else
6248 {
6249 onscreen = MAP_NOT_VIS;
6250 //fprintf(stderr,"Map not in the zoom zone: %s\n",filename);
6251 }
6252
6253 // Check whether the map extents are < XX% of viewscreen size (both
6254 // directions), or viewscreen is > XX% of map extents (either
6255 // direction). This will knock out maps that are too large/small to
6256 // be displayed at this zoom level.
6257
6258
6259 }
6260 else // Map is not visible
6261 {
6262 onscreen = MAP_NOT_VIS;
6263 //fprintf(stderr,"Map found in index but not onscreen: %s\n",filename);
6264 }
6265 }
6266 else // Map is not in the index
6267 {
6268 onscreen = MAP_NOT_INDEXED;
6269 //fprintf(stderr,"Map not found in index: %s\n",filename);
6270 }
6271 return(onscreen);
6272 }
6273
6274
6275
6276
6277
6278 /**********************************************************
6279 * draw_map()
6280 *
6281 * Function which tries to figure out what type of map or
6282 * image file we're dealing with, and takes care of getting
6283 * it onto the screen. Calls other functions to deal with
6284 * .geo/.tif/.shp maps.
6285 *
6286 * If destination_pixmap == DRAW_NOT, then we'll not draw
6287 * the map anywhere, but we'll determine the map extents
6288 * and write them to the map index file.
6289 **********************************************************/
6290 /* table of map drivers, selected by filename extension */
6291 extern void draw_dos_map(Widget w,
6292 char *dir,
6293 char *filenm,
6294 alert_entry *alert,
6295 u_char alert_color,
6296 int destination_pixmap,
6297 map_draw_flags *draw_flags);
6298
6299 extern void draw_palm_image_map(Widget w,
6300 char *dir,
6301 char *filenm,
6302 alert_entry *alert,
6303 u_char alert_color,
6304 int destination_pixmap,
6305 map_draw_flags *draw_flags);
6306
6307 #ifdef HAVE_LIBSHP
6308 extern void draw_shapefile_map (Widget w,
6309 char *dir,
6310 char *filenm,
6311 alert_entry *alert,
6312 u_char alert_color,
6313 int destination_pixmap,
6314 map_draw_flags *draw_flags);
6315 extern void clear_dbfawk_sigs(void);
6316 #endif /* HAVE_LIBSHP */
6317 #ifdef HAVE_LIBGEOTIFF
6318 extern void draw_geotiff_image_map(Widget w,
6319 char *dir,
6320 char *filenm,
6321 alert_entry *alert,
6322 u_char alert_color,
6323 int destination_pixmap,
6324 map_draw_flags *draw_flags);
6325 #endif /* HAVE_LIBGEOTIFF */
6326 extern void draw_geo_image_map(Widget w,
6327 char *dir,
6328 char *filenm,
6329 alert_entry *alert,
6330 u_char alert_color,
6331 int destination_pixmap,
6332 map_draw_flags *draw_flags);
6333
6334 extern void draw_gnis_map(Widget w,
6335 char *dir,
6336 char *filenm,
6337 alert_entry *alert,
6338 u_char alert_color,
6339 int destination_pixmap,
6340 map_draw_flags *draw_flags);
6341
6342 extern void draw_pop_map(Widget w,
6343 char *dir,
6344 char *filenm,
6345 alert_entry *alert,
6346 u_char alert_color,
6347 int destination_pixmap,
6348 map_draw_flags *draw_flags);
6349
6350 struct
6351 {
6352 char *ext;
6353 enum {none=0, map, tif, geo, gnis, shp, pop} type;
6354 void (*func)(Widget w,
6355 char *dir,
6356 char *filenm,
6357 alert_entry *alert,
6358 u_char alert_color,
6359 int destination_pixmap,
6360 map_draw_flags *draw_flags);
6361 } map_driver[] =
6362 {
6363 {"map",map,draw_dos_map},
6364
6365 #ifdef HAVE_LIBGEOTIFF
6366 {"tif",tif,draw_geotiff_image_map},
6367 #endif /* HAVE_LIBGEOTIFF */
6368
6369 {"geo",geo,draw_geo_image_map},
6370 {"gnis",gnis,draw_gnis_map},
6371 {"pop",pop,draw_pop_map},
6372
6373 #ifdef HAVE_LIBSHP
6374 {"shp",shp,draw_shapefile_map},
6375 #endif /* HAVE_LIBSHP */
6376
6377 {NULL,none,NULL}
6378 }, *map_driver_ptr;
6379
6380
6381
6382
6383
draw_map(Widget w,char * dir,char * filenm,alert_entry * alert,u_char alert_color,int destination_pixmap,map_draw_flags * draw_flags)6384 void draw_map (Widget w, char *dir, char *filenm, alert_entry *alert,
6385 u_char alert_color, int destination_pixmap,
6386 map_draw_flags *draw_flags)
6387 {
6388 enum map_onscreen_enum onscreen;
6389 char *ext;
6390 char file[MAX_FILENAME];
6391
6392 if ((ext = get_map_ext(filenm)) == NULL)
6393 {
6394 return;
6395 }
6396
6397 if (debug_level & 16)
6398 {
6399 fprintf(stderr,"draw_map: Searching for map driver\n");
6400 }
6401
6402 for (map_driver_ptr = map_driver; map_driver_ptr->ext; map_driver_ptr++)
6403 {
6404 if (strcasecmp(ext,map_driver_ptr->ext) == 0)
6405 {
6406 if (debug_level & 16)
6407 fprintf(stderr,
6408 "draw_map: Found map driver: %s: %d\n",
6409 ext,
6410 map_driver_ptr->type);
6411 break; /* found our map_driver */
6412 }
6413 }
6414 if (map_driver_ptr->type == none) /* fall thru: unknown map driver */
6415 {
6416 // Check whether we're indexing or drawing the map
6417 if ( (destination_pixmap != INDEX_CHECK_TIMESTAMPS)
6418 && (destination_pixmap != INDEX_NO_TIMESTAMPS) )
6419 {
6420 // We're drawing, not indexing. Output a warning
6421 // message.
6422 fprintf(stderr,"*** draw_map: Unknown map type: %s ***\n", filenm);
6423 }
6424 else // We're indexing
6425 {
6426 if (debug_level & 16)
6427 {
6428 fprintf(stderr,"draw_map: No map driver found\n");
6429 }
6430 }
6431 return;
6432 }
6433
6434 onscreen = map_onscreen_index(filenm); // Check map index
6435
6436 // Check whether we're indexing or drawing the map
6437 if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
6438 || (destination_pixmap == INDEX_NO_TIMESTAMPS) )
6439 {
6440
6441 // We're indexing maps
6442 if (onscreen != MAP_NOT_INDEXED) // We already have an index entry for this map.
6443 // This is where we pick up a big speed increase:
6444 // Refusing to index a map that's already indexed.
6445 {
6446 return; // Skip it.
6447 }
6448 }
6449 else // We're drawing maps
6450 {
6451 // See if map is visible. If not, skip it.
6452 if (onscreen == MAP_NOT_VIS) // Map is not visible, skip it.
6453 {
6454 //fprintf(stderr,"map not visible\n");
6455 if (alert)
6456 {
6457 alert->flags[on_screen] = 'N';
6458 }
6459 return;
6460 }
6461 }
6462
6463
6464 xastir_snprintf(file, sizeof(file), "%s/%s", dir, filenm);
6465
6466 // Used for debugging. If we get a segfault on a map, this is
6467 // often the only way of finding out which map file we can't
6468 // handle.
6469 if (debug_level & 16)
6470 {
6471 fprintf(stderr,"draw_map: %s\n",file);
6472 }
6473
6474 /* XXX - aren't alerts just shp maps? Why was there special case code? */
6475
6476 if (map_driver_ptr->func)
6477 {
6478 map_driver_ptr->func(w,
6479 dir,
6480 filenm,
6481 alert,
6482 alert_color,
6483 destination_pixmap,
6484 draw_flags);
6485 }
6486
6487 XmUpdateDisplay (XtParent (da));
6488 } // End of draw_map()
6489
6490
6491
6492
6493
6494 static void index_update_directory(char *directory);
6495 static void index_update_accessed(char *filename);
6496
6497
6498
6499
6500
6501 /////////////////////////////////////////////////////////////////////
6502 // map_search()
6503 //
6504 // Function which recurses through map directories, finding map
6505 // files. It's called from load_auto_maps and load_alert_maps. If
6506 // a map file is found, it is drawn. We can also call this function
6507 // in indexing mode rather than draw mode, specified by the
6508 // destination_pixmap parameter.
6509 //
6510 // If alert == NULL, we looking for a regular map file to draw.
6511 // If alert != NULL, we have a weather alert to draw.
6512 //
6513 // For alert maps, we need to do things a bit differently, as there
6514 // should be only a few maps that contain all of the alert maps, and we
6515 // can compute which map some of them might be in. We need to fill in
6516 // the alert structure with the filename that alert is found in.
6517 // For alerts we're not drawing the maps, we're just computing the
6518 // full filename for the alert and filling that struct field in.
6519 //
6520 // The "warn" parameter specifies whether to warn the operator about
6521 // the alert on the console as well. If it was received locally or
6522 // via local RF, then the answer is yes. The severe weather may be
6523 // nearby.
6524 //
6525 // We have the timestamp of the map_index.sys file stored away in
6526 // the global: time_t map_index_timestamp;
6527 // Use that timestamp to compare the map file or GEO file timestamps
6528 // to. Re-index the map if map_index_timestamp is older.
6529 //
6530 /////////////////////////////////////////////////////////////////////
map_search(Widget w,char * dir,alert_entry * alert,int * alert_count,int warn,int destination_pixmap)6531 static void map_search (Widget w, char *dir, alert_entry * alert, int *alert_count,int warn, int destination_pixmap)
6532 {
6533 struct dirent *dl = NULL;
6534 DIR *dm;
6535 char fullpath[MAX_FILENAME];
6536 struct stat nfile;
6537 // const time_t *ftime;
6538 // char this_time[40];
6539 char *ptr;
6540 char *map_dir;
6541 int map_dir_length;
6542 map_draw_flags mdf;
6543
6544 // We'll use the weather alert directory if it's an alert
6545 map_dir = alert ? ALERT_MAP_DIR : SELECTED_MAP_DIR;
6546
6547 map_dir_length = (int)strlen (map_dir);
6548
6549 if (alert) // We're doing weather alerts
6550 {
6551 // First check whether the alert->filename variable is filled
6552 // in. If so, we've already found the file and can just display
6553 // that shape in the file
6554 if (alert->filename[0] == '\0') // No filename in struct, so will have
6555 {
6556 // to search for the shape in the files.
6557 switch (alert->title[3])
6558 {
6559 case 'F': // 'F' in 4th char means fire alert
6560 // Use fire alert file fz_??????
6561 //fprintf(stderr,"%c:Fire Alert file\n",alert->title[3]);
6562 xastir_snprintf(alert->filename,
6563 sizeof(alert->filename),
6564 "fz");
6565 break;
6566
6567 case 'C': // 'C' in 4th char means county
6568 // Use County file c_??????
6569 //fprintf(stderr,"%c:County file\n",alert->title[3]);
6570 xastir_snprintf(alert->filename,
6571 sizeof(alert->filename),
6572 "c_");
6573 break;
6574 case 'A': // 'A' in 4th char means county warning area
6575 // Use County warning area w_?????
6576 //fprintf(stderr,"%c:County warning area file\n",alert->title[3]);
6577 xastir_snprintf(alert->filename,
6578 sizeof(alert->filename),
6579 "w_");
6580 break;
6581 case 'Z':
6582 // Zone, coastal or offshore marine zone file z_????? or mz?????? or oz??????
6583 // oz: ANZ081-086,088,PZZ081-085
6584 // mz: AM,AN,GM,LC,LE,LH,LM,LO,LS,PH,PK,PM,PS,PZ,SL
6585 // z_: All others
6586 if (strncasecmp(alert->title,"AM",2) == 0)
6587 {
6588 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6589 xastir_snprintf(alert->filename,
6590 sizeof(alert->filename),
6591 "mz");
6592 }
6593 else if (strncasecmp(alert->title,"AN",2) == 0)
6594 {
6595 // Need to check for Z081-Z086, Z088, if so use
6596 // oz??????, else use mz??????
6597 if ( (strncasecmp(&alert->title[3],"Z081",4) == 0)
6598 || (strncasecmp(&alert->title[3],"Z082",4) == 0)
6599 || (strncasecmp(&alert->title[3],"Z083",4) == 0)
6600 || (strncasecmp(&alert->title[3],"Z084",4) == 0)
6601 || (strncasecmp(&alert->title[3],"Z085",4) == 0)
6602 || (strncasecmp(&alert->title[3],"Z086",4) == 0)
6603 || (strncasecmp(&alert->title[3],"Z088",4) == 0) )
6604 {
6605 //fprintf(stderr,"%c:Offshore marine zone file\n",alert->title[3]);
6606 xastir_snprintf(alert->filename,
6607 sizeof(alert->filename),
6608 "oz");
6609 }
6610 else
6611 {
6612 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6613 xastir_snprintf(alert->filename,
6614 sizeof(alert->filename),
6615 "mz");
6616 }
6617 }
6618 else if (strncasecmp(alert->title,"GM",2) == 0)
6619 {
6620 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6621 xastir_snprintf(alert->filename,
6622 sizeof(alert->filename),
6623 "mz");
6624 }
6625 else if (strncasecmp(alert->title,"LC",2) == 0)
6626 {
6627 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6628 xastir_snprintf(alert->filename,
6629 sizeof(alert->filename),
6630 "mz");
6631 }
6632 else if (strncasecmp(alert->title,"LE",2) == 0)
6633 {
6634 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6635 xastir_snprintf(alert->filename,
6636 sizeof(alert->filename),
6637 "mz");
6638 }
6639 else if (strncasecmp(alert->title,"LH",2) == 0)
6640 {
6641 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6642 xastir_snprintf(alert->filename,
6643 sizeof(alert->filename),
6644 "mz");
6645 }
6646 else if (strncasecmp(alert->title,"LM",2) == 0)
6647 {
6648 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6649 xastir_snprintf(alert->filename,
6650 sizeof(alert->filename),
6651 "mz");
6652 }
6653 else if (strncasecmp(alert->title,"LO",2) == 0)
6654 {
6655 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6656 xastir_snprintf(alert->filename,
6657 sizeof(alert->filename),
6658 "mz");
6659 }
6660 else if (strncasecmp(alert->title,"LS",2) == 0)
6661 {
6662 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6663 xastir_snprintf(alert->filename,
6664 sizeof(alert->filename),
6665 "mz");
6666 }
6667 else if (strncasecmp(alert->title,"PH",2) == 0)
6668 {
6669 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6670 xastir_snprintf(alert->filename,
6671 sizeof(alert->filename),
6672 "mz");
6673 }
6674 else if (strncasecmp(alert->title,"PK",2) == 0)
6675 {
6676 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6677 xastir_snprintf(alert->filename,
6678 sizeof(alert->filename),
6679 "mz");
6680 }
6681 else if (strncasecmp(alert->title,"PM",2) == 0)
6682 {
6683 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6684 xastir_snprintf(alert->filename,
6685 sizeof(alert->filename),
6686 "mz");
6687 }
6688 else if (strncasecmp(alert->title,"PS",2) == 0)
6689 {
6690 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6691 xastir_snprintf(alert->filename,
6692 sizeof(alert->filename),
6693 "mz");
6694 }
6695 else if (strncasecmp(alert->title,"PZ",2) == 0)
6696 {
6697 // Need to check for PZZ081-085, if so use oz??????, else use mz??????
6698 if ( (strncasecmp(&alert->title[3],"Z081",4) == 0)
6699 || (strncasecmp(&alert->title[3],"Z082",4) == 0)
6700 || (strncasecmp(&alert->title[3],"Z083",4) == 0)
6701 || (strncasecmp(&alert->title[3],"Z084",4) == 0)
6702 || (strncasecmp(&alert->title[3],"Z085",4) == 0) )
6703 {
6704 //fprintf(stderr,"%c:Offshore marine zone file\n",alert->title[3]);
6705 xastir_snprintf(alert->filename,
6706 sizeof(alert->filename),
6707 "oz");
6708 }
6709 else
6710 {
6711 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6712 xastir_snprintf(alert->filename,
6713 sizeof(alert->filename),
6714 "mz");
6715 }
6716 }
6717 else if (strncasecmp(alert->title,"SL",2) == 0)
6718 {
6719 //fprintf(stderr,"%c:Coastal marine zone file\n",alert->title[3]);
6720 xastir_snprintf(alert->filename,
6721 sizeof(alert->filename),
6722 "mz");
6723 }
6724 else
6725 {
6726 // Must be regular zone file instead of coastal
6727 // marine zone or offshore marine zone.
6728 //fprintf(stderr,"%c:Zone file\n",alert->title[3]);
6729 xastir_snprintf(alert->filename,
6730 sizeof(alert->filename),
6731 "z_");
6732 }
6733 break;
6734
6735 default:
6736 // VK2XJG
6737 // This section could most likely be moved so that it's not called as part of the default, but in order
6738 // to get the shapefiles for BOM working this was the best spot at the time...
6739 // Australian BOM alerts use the following shapefiles:
6740 // PW = Public Warning = gfe_public_weather
6741 // MW = Coastal Waters = gfe_coastal_waters
6742 // CW = Coastal Waters Warnings = gfe_coastal_waters_warnings
6743 // FW = Fire Weather = gfe_fire_weather
6744 // ME = Metro Effects = gfe_metro_areas
6745 // Note - Need to cater for both 2 and 3 character state designators
6746 // Shapefile filenames are static - there is no datestamp in the filename.
6747 if ((strncasecmp(&alert->title[4],"MW",2) == 0) || (strncasecmp(&alert->title[3],"MW",2) == 0))
6748 {
6749 //fprintf(stderr,"%c:BOM Coastal Waters file\n",alert->title[4]);
6750 xastir_snprintf(alert->filename,
6751 sizeof(alert->filename),
6752 "gfe_coastal_waters.shp");
6753 }
6754 else if ((strncasecmp(&alert->title[4],"CW",2) == 0) || (strncasecmp(&alert->title[3],"CW",2) == 0))
6755 {
6756 //fprintf(stderr,"%c:BOM Coastal waters warning file\n",alert->title[3]);
6757 xastir_snprintf(alert->filename,
6758 sizeof(alert->filename),
6759 "gfe_coastal_waters_warnings.shp");
6760 }
6761 else if ((strncasecmp(&alert->title[4],"PW",2) == 0) || (strncasecmp(&alert->title[3],"PW",2) == 0))
6762 {
6763 //fprintf(stderr,"%c:BOM Public Weather file\n",alert->title[3]);
6764 xastir_snprintf(alert->filename,
6765 sizeof(alert->filename),
6766 "gfe_public_weather.shp");
6767 }
6768 else if ((strncasecmp(&alert->title[4],"FW",2) == 0) || (strncasecmp(&alert->title[3],"FW",2) == 0))
6769 {
6770 //fprintf(stderr,"%c:BOM Fire Weather file\n",alert->title[3]);
6771 xastir_snprintf(alert->filename,
6772 sizeof(alert->filename),
6773 "gfe_fire_weather.shp");
6774 }
6775 else if ((strncasecmp(&alert->title[4],"ME",2) == 0) || (strncasecmp(&alert->title[3],"ME",2) == 0))
6776 {
6777 //fprintf(stderr,"%c:BOM Metro Areas file\n",alert->title[3]);
6778 xastir_snprintf(alert->filename,
6779 sizeof(alert->filename),
6780 "gfe_metro_areas.shp");
6781 }
6782
6783
6784
6785 // Unknown type
6786 //fprintf(stderr,"%c:Can't match weather warning to a Shapefile:%s\n",alert->title[3],alert->title);
6787 break;
6788 }
6789 // fprintf(stderr,"%s\t%s\t%s\n",alert->activity,alert->alert_status,alert->title);
6790 //fprintf(stderr,"File: %s\n",alert->filename);
6791 }
6792
6793 // NOTE: Need to skip this part if we have a full filename.
6794
6795 if (alert->filename[0]) // We have at least a partial filename
6796 {
6797 int done = 0;
6798
6799 if (strlen(alert->filename) > 3)
6800 {
6801 done++; // We already have a filename
6802 }
6803
6804 if (!done) // We don't have a filename yet
6805 {
6806
6807 // Look through the warning directory to find a match for
6808 // the first few characters that we already figured out.
6809 // This is designed so that we don't need to know the exact
6810 // filename, but only the lead three characters in order to
6811 // figure out which shapefile to use.
6812 dm = opendir (dir);
6813 if (!dm) // Couldn't open directory
6814 {
6815 xastir_snprintf(fullpath, sizeof(fullpath), "aprsmap %s", dir);
6816 // If local alert, warn the operator via the
6817 // console as well.
6818 if (warn)
6819 {
6820 perror (fullpath);
6821 }
6822 }
6823 else // We could open the directory just fine
6824 {
6825 while ( (dl = readdir(dm)) && !done )
6826 {
6827 int i;
6828
6829 // Check the file/directory name for control
6830 // characters
6831 for (i = 0; i < (int)strlen(dl->d_name); i++)
6832 {
6833 // Dump out a warning if control
6834 // characters other than LF or CR are
6835 // found.
6836 if ( (dl->d_name[i] != '\n')
6837 && (dl->d_name[i] != '\r')
6838 && (dl->d_name[i] < 0x20) )
6839 {
6840
6841 fprintf(stderr,"\nmap_search: Found control char 0x%02x in alert file/alert directory name. Line was:\n",
6842 dl->d_name[i]);
6843 fprintf(stderr,"%s\n",dl->d_name);
6844 }
6845 /*
6846 // This part might not work 'cuz we'd be changing a memory area that
6847 // we might have only read access to. Check this.
6848 if (dl->d_name[i] < 0x20) {
6849 // Terminate string at any control character
6850 dl->d_name[i] = '\0';
6851 }
6852 */
6853 }
6854
6855 xastir_snprintf(fullpath, sizeof(fullpath), "%s%s", dir, dl->d_name);
6856 /*fprintf(stderr,"FULL PATH %s\n",fullpath); */
6857 if (stat (fullpath, &nfile) == 0)
6858 {
6859 // ftime = (time_t *)&nfile.st_ctime;
6860 switch (nfile.st_mode & S_IFMT)
6861 {
6862 case (S_IFDIR): // It's a directory, skip it
6863 break;
6864
6865 case (S_IFREG): // It's a file, check it
6866 /*fprintf(stderr,"FILE %s\n",dl->d_name); */
6867 // Here we look for a match for the
6868 // first 2 characters of the filename.
6869 //
6870 if (strncasecmp(alert->filename,dl->d_name,2) == 0)
6871 {
6872 // We have a match for the
6873 // first few characters.
6874 // Check that last three are
6875 // "shp"
6876
6877 //fprintf(stderr,"%s\n",fullpath);
6878
6879 if ( (dl->d_name[strlen(dl->d_name)-3] == 's'
6880 || dl->d_name[strlen(dl->d_name)-3] == 'S')
6881 && (dl->d_name[strlen(dl->d_name)-2] == 'h'
6882 || dl->d_name[strlen(dl->d_name)-2] == 'H')
6883 && (dl->d_name[strlen(dl->d_name)-1] == 'p'
6884 || dl->d_name[strlen(dl->d_name)-1] == 'P') )
6885 {
6886 // We have an exact match.
6887 // Save the filename in the alert
6888 memcpy(alert->filename,
6889 dl->d_name,
6890 sizeof(alert->filename));
6891 // Terminate string
6892 alert->filename[sizeof(alert->filename)-1] = '\0';
6893 done++;
6894 //fprintf(stderr,"%s\n",dl->d_name);
6895 }
6896 }
6897 break;
6898
6899 default: // Not dir or file, skip it
6900 break;
6901 }
6902 }
6903 }
6904 }
6905 (void)closedir (dm);
6906 }
6907
6908 if (done) // We found a filename match for the alert
6909 {
6910 // Go draw the weather alert (kind'a)
6911 //WE7U
6912 mdf.draw_filled=1;
6913 mdf.usgs_drg=0;
6914
6915 if (debug_level & 16)
6916 {
6917 fprintf(stderr,"map_search: calling draw_map for an alert\n");
6918 }
6919
6920 draw_map (w,
6921 dir, // Alert directory
6922 alert->filename, // Shapefile filename
6923 alert,
6924 -1, // Signifies "DON'T DRAW THE SHAPE"
6925 destination_pixmap,
6926 &mdf );
6927
6928 if (debug_level & 16)
6929 {
6930 fprintf(stderr,"map_search: returned from draw_map\n");
6931 }
6932
6933 }
6934 else // No filename found that matches the first two
6935 {
6936 // characters that we already computed.
6937
6938 //
6939 // Need code here
6940 //
6941
6942 }
6943 }
6944 else // Still no filename for the weather alert.
6945 {
6946 // Output an error message?
6947 //
6948 // Need code here
6949 //
6950
6951 }
6952 }
6953
6954
6955 // MAPS, not alerts
6956
6957 else // We're doing regular maps, not weather alerts
6958 {
6959 time_t map_timestamp;
6960
6961
6962 dm = opendir (dir);
6963 if (!dm) // Couldn't open directory
6964 {
6965 xastir_snprintf(fullpath, sizeof(fullpath), "aprsmap %s", dir);
6966 if (warn)
6967 {
6968 perror (fullpath);
6969 }
6970 }
6971 else
6972 {
6973 int count = 0;
6974 while ((dl = readdir (dm)))
6975 {
6976 int i;
6977
6978 // Check the file/directory name for control
6979 // characters
6980 for (i = 0; i < (int)strlen(dl->d_name); i++)
6981 {
6982 // Dump out a warning if control characters
6983 // other than LF or CR are found.
6984 if ( (dl->d_name[i] != '\n')
6985 && (dl->d_name[i] != '\r')
6986 && (dl->d_name[i] < 0x20) )
6987 {
6988
6989 fprintf(stderr,"\nmap_search: Found control char 0x%02x in map file/map directory name. Line was:\n",
6990 dl->d_name[i]);
6991 fprintf(stderr,"%s\n",dl->d_name);
6992 }
6993 /*
6994 // This part might not work 'cuz we'd be changing a memory area that
6995 // we might have only read access to. Check this.
6996 if (dl->d_name[i] < 0x20) {
6997 // Terminate string at any control character
6998 dl->d_name[i] = '\0';
6999 }
7000 */
7001 }
7002
7003 xastir_snprintf(fullpath, sizeof(fullpath), "%s/%s", dir, dl->d_name);
7004 //fprintf(stderr,"FULL PATH %s\n",fullpath);
7005 if (stat (fullpath, &nfile) == 0)
7006 {
7007 // ftime = (time_t *)&nfile.st_ctime;
7008 switch (nfile.st_mode & S_IFMT)
7009 {
7010 case (S_IFDIR): // It's a directory, recurse
7011 //fprintf(stderr,"file %c letter %c\n",dl->d_name[0],letter);
7012 if ((strcmp (dl->d_name, ".") != 0) && (strcmp (dl->d_name, "..") != 0))
7013 {
7014
7015 //fprintf(stderr,"FULL PATH %s\n",fullpath);
7016
7017 // If we're indexing, throw the
7018 // directory into the map index as
7019 // well.
7020 if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
7021 || (destination_pixmap == INDEX_NO_TIMESTAMPS) )
7022 {
7023 char temp_dir[MAX_FILENAME];
7024
7025 // Drop off the base part of the
7026 // path for the indexing,
7027 // usually
7028 // "/usr/local/share/xastir/maps".
7029 // Add a '/' to the end.
7030 xastir_snprintf(temp_dir,
7031 sizeof(temp_dir),
7032 "%s/",
7033 &fullpath[map_dir_length+1]);
7034
7035 // Add the directory to the
7036 // in-memory map index.
7037 index_update_directory(temp_dir);
7038 }
7039
7040 // xastir_snprintf(this_time,
7041 // sizeof(this_time),
7042 // "%s",
7043 // ctime(ftime));
7044 map_search(w, fullpath, alert, alert_count, warn, destination_pixmap);
7045 }
7046 break;
7047
7048 case (S_IFREG): // It's a file, draw the map
7049 /*fprintf(stderr,"FILE %s\n",dl->d_name); */
7050
7051 // Get the last-modified timestamp for the map file
7052 //map_timestamp = (time_t)nfile.st_mtime;
7053 map_timestamp =
7054 (time_t)( (nfile.st_mtime>nfile.st_ctime) ? nfile.st_mtime : nfile.st_ctime );
7055
7056
7057 // Check whether we're doing indexing or
7058 // map drawing. If indexing, we only
7059 // want to index if the map timestamp is
7060 // newer than the index timestamp.
7061 if (destination_pixmap == INDEX_CHECK_TIMESTAMPS
7062 || destination_pixmap == INDEX_NO_TIMESTAMPS)
7063 {
7064 // We're doing indexing, not map drawing
7065 char temp_dir[MAX_FILENAME];
7066
7067 // Drop off the base part of the
7068 // path for the indexing, usually
7069 // "/usr/local/share/xastir/maps".
7070 xastir_snprintf(temp_dir,
7071 sizeof(temp_dir),
7072 "%s",
7073 &fullpath[map_dir_length+1]);
7074
7075 // Update the "accessed"
7076 // variable in the record
7077 index_update_accessed(temp_dir);
7078
7079 // Note: This is not as efficient as it should be, as we're looking
7080 // through the in-memory map index here just to update the
7081 // "accessed" variable, then in some cases looking through it again
7082 // in the next section for updated maps, or if we're ignoring
7083 // timestamps while indexing. Looking through a linear linked list
7084 // too many times overall.
7085
7086 if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
7087 && (map_timestamp < map_index_timestamp) )
7088 {
7089 // Map is older than index _and_
7090 // we're supposed to check
7091 // timestamps.
7092 count++;
7093 break; // Skip indexing this file
7094 }
7095 else // Map is newer or we're ignoring timestamps.
7096 {
7097 // We'll index the map
7098 if (debug_level & 16)
7099 {
7100 fprintf(stderr,"Indexing map: %s\n",fullpath);
7101 }
7102 }
7103 }
7104
7105 // Check whether the file is in a subdirectory
7106 if (strncmp (fullpath, map_dir, (size_t)map_dir_length) != 0)
7107 {
7108
7109 if (debug_level & 16)
7110 {
7111 fprintf(stderr,"Calling draw_map\n");
7112 }
7113 mdf.draw_filled=1;
7114 mdf.usgs_drg=0;
7115
7116 draw_map (w,
7117 dir,
7118 dl->d_name,
7119 alert ? &alert[*alert_count] : NULL,
7120 '\0',
7121 destination_pixmap,
7122 &mdf );
7123
7124 if (debug_level & 16)
7125 {
7126 fprintf(stderr,"Returned from draw_map\n");
7127 }
7128 if (alert_count && *alert_count)
7129 {
7130 (*alert_count)--;
7131 }
7132 }
7133 else
7134 {
7135 // File is in the main map directory
7136 // Find the '/' character
7137 for (ptr = &fullpath[map_dir_length]; *ptr == '/'; ptr++) ;
7138 mdf.draw_filled=1;
7139 mdf.usgs_drg=0;
7140
7141 if (debug_level & 16)
7142 {
7143 fprintf(stderr,"Calling draw_map\n");
7144 }
7145
7146 draw_map (w,
7147 map_dir,
7148 ptr,
7149 alert ? &alert[*alert_count] : NULL,
7150 '\0',
7151 destination_pixmap,
7152 &mdf );
7153
7154 if (alert_count && *alert_count)
7155 {
7156 (*alert_count)--;
7157 }
7158 }
7159 count++;
7160 break;
7161
7162 default:
7163 break;
7164 }
7165 }
7166 }
7167 if (debug_level & 16)
7168 {
7169 fprintf(stderr,"Number of maps queried: %d\n", count);
7170 }
7171
7172 (void)closedir (dm);
7173 }
7174 }
7175 }
7176
7177
7178
7179
7180
7181 // List pointer for the map index linked list.
7182 map_index_record *map_index_head = NULL;
7183
7184 // Might wish to have another variable in the index which is used to
7185 // record that a file has been indexed recently. This could be used
7186 // to prune old entries out of the index if a full indexing didn't
7187 // touch a file entry. Could also delete an entry from the index
7188 // if/when a file can't be opened?
7189
7190
7191
7192
7193
7194 // Function to dissect and free all of the records in a map index
7195 // linked list, leaving it totally empty.
7196 //
free_map_index(map_index_record * index_list_head)7197 static void free_map_index(map_index_record *index_list_head)
7198 {
7199 map_index_record *current;
7200 map_index_record *temp;
7201
7202
7203 current = index_list_head;
7204
7205 while (current != NULL)
7206 {
7207 temp = current;
7208 if (current->XmStringPtr != NULL)
7209 {
7210 XmStringFree(current->XmStringPtr);
7211 }
7212 current = current->next;
7213 free(temp);
7214 }
7215
7216 index_list_head = NULL;
7217 }
7218
7219
7220
7221
7222
7223 // Function to copy just the properties fields from the backup map
7224 // index to the primary index. Must match each record before
7225 // copying. Once it's done, it frees the backup map index.
7226 //
map_index_copy_properties(map_index_record * primary_index_head,map_index_record * backup_index_head)7227 static void map_index_copy_properties(map_index_record *primary_index_head,
7228 map_index_record *backup_index_head)
7229 {
7230 map_index_record *primary;
7231 map_index_record *backup;
7232
7233
7234 backup = backup_index_head;
7235
7236 // Walk the backup list, comparing the filename field with the
7237 // primary list. When a match is found, copy just the
7238 // Properties fields (map_layer/draw_filled/auto_maps/selected)
7239 // across to the primary record.
7240 //
7241 while (backup != NULL)
7242 {
7243 int done = 0;
7244
7245 primary = primary_index_head;
7246
7247 while (!done && primary != NULL)
7248 {
7249
7250 if (strcmp(primary->filename, backup->filename) == 0) // If match
7251 {
7252
7253 if (debug_level & 16)
7254 {
7255 fprintf(stderr,"Match: %s\t%s\n",
7256 primary->filename,
7257 backup->filename);
7258 }
7259
7260 // Copy the Properties across
7261 primary->max_zoom = backup->max_zoom;
7262 primary->min_zoom = backup->min_zoom;
7263 primary->map_layer = backup->map_layer;
7264 primary->draw_filled = backup->draw_filled;
7265 primary->usgs_drg = backup->usgs_drg;
7266 primary->auto_maps = backup->auto_maps;
7267 primary->selected = backup->selected;
7268
7269 // Done copying this backup record. Go on to the
7270 // next. Skip the rest of the primary list for this
7271 // iteration.
7272 done++;
7273 }
7274 else // No match, walk the primary list looking for one.
7275 {
7276 primary = primary->next;
7277 }
7278 }
7279
7280 // Walk the backup list
7281 backup = backup->next;
7282 }
7283
7284 // We're done copying. Free the backup list.
7285 free_map_index(backup_index_head);
7286 }
7287
7288
7289
7290
7291
7292 // Function used to add map directories to the in-memory map index.
7293 // Causes an update of the index list in memory. Input Records are
7294 // inserted in alphanumerical order. We mark directories in the
7295 // index with a '/' on the end of the name, and zero entries for
7296 // top/bottom/left/right.
7297 // The input directory to this routine MUST have a '/' character on
7298 // the end of it. This is how we differentiate directories from
7299 // files in the list.
index_update_directory(char * directory)7300 static void index_update_directory(char *directory)
7301 {
7302
7303 map_index_record *current = map_index_head;
7304 map_index_record *previous = map_index_head;
7305 map_index_record *temp_record = NULL;
7306 int done = 0;
7307 int i;
7308
7309
7310 //fprintf(stderr,"index_update_directory: %s\n", directory );
7311
7312 // Check for initial bad input
7313 if ( (directory == NULL)
7314 || (directory[0] == '\0')
7315 || (directory[strlen(directory) - 1] != '/')
7316 || ( (directory[1] == '/') && (strlen(directory) == 1)) )
7317 {
7318 fprintf(stderr,"index_update_directory: Bad input: %s\n",directory);
7319 return;
7320 }
7321 // Make sure there aren't any weird characters in the directory
7322 // that might cause problems later. Look for control characters
7323 // and convert them to string-end characters.
7324 for ( i = 0; i < (int)strlen(directory); i++ )
7325 {
7326 // Change any control characters to '\0' chars
7327 if (directory[i] < 0x20)
7328 {
7329
7330 fprintf(stderr,"\nindex_update_directory: Found control char 0x%02x in map file/map directory name:\n%s\n",
7331 directory[i],
7332 directory);
7333
7334 directory[i] = '\0'; // Terminate it here
7335 }
7336 }
7337 // Check if the string is _now_ bogus
7338 if ( (directory[0] == '\0')
7339 || (directory[strlen(directory) - 1] != '/')
7340 || ( (directory[1] == '/') && (strlen(directory) == 1)))
7341 {
7342 fprintf(stderr,"index_update_directory: Bad input: %s\n",directory);
7343 return;
7344 }
7345
7346 //if (map_index_head == NULL)
7347 // fprintf(stderr,"Empty list\n");
7348
7349 // Search for a matching directory name in the linked list
7350 while ((current != NULL) && !done)
7351 {
7352 int test;
7353
7354 //fprintf(stderr,"Comparing %s to\n %s\n",
7355 // current->filename, directory);
7356
7357 test = strcmp(current->filename, directory);
7358 if (test == 0)
7359 {
7360 // Found a match!
7361 //fprintf(stderr,"Found: Updating entry for %s\n",directory);
7362 temp_record = current;
7363 done++; // Exit loop, "current" points to found record
7364 }
7365 else if (test > 0) // Found a string past us in the
7366 {
7367 // alphabet. Insert ahead of this
7368 // last record.
7369
7370 //fprintf(stderr,"\n%s\n%s\n",current->filename,directory);
7371
7372 //fprintf(stderr,"Not Found: Inserting an index record for %s\n",directory);
7373 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
7374 CHECKMALLOC(temp_record);
7375
7376 if (current == map_index_head) // Start of list!
7377 {
7378 // Insert new record at head of list
7379 temp_record->next = map_index_head;
7380 map_index_head = temp_record;
7381 //fprintf(stderr,"Inserting at head of list\n");
7382 }
7383 else // Insert between "previous" and "current"
7384 {
7385 // Insert new record before "current"
7386 previous->next = temp_record;
7387 temp_record->next = current;
7388 //fprintf(stderr,"Inserting before current\n");
7389 }
7390
7391 //fprintf(stderr,"Adding:%d:%s\n",strlen(directory),directory);
7392
7393 // Fill in some default values for the new record.
7394 temp_record->selected = 0;
7395 temp_record->auto_maps = 0;
7396 temp_record->XmStringPtr = NULL;
7397
7398 //current = current->next;
7399 done++;
7400 }
7401 else // Haven't gotten to the correct insertion point yet
7402 {
7403 previous = current; // Save ptr to last record
7404 current = current->next;
7405 }
7406 }
7407
7408 if (!done) // Matching record not found, add a record to
7409 {
7410 // the end of the list. "previous" points to
7411 // the last record in the list or NULL (empty
7412 // list).
7413 //fprintf(stderr,"Not Found: Adding an index record for %s\n",directory);
7414 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
7415 CHECKMALLOC(temp_record);
7416
7417 temp_record->next = NULL;
7418
7419 if (previous == NULL) // Empty list
7420 {
7421 map_index_head = temp_record;
7422 //fprintf(stderr,"First record in new list\n");
7423 }
7424 else // Else at end of list
7425 {
7426 previous->next = temp_record;
7427 //fprintf(stderr,"Adding to end of list: %s\n",directory);
7428 }
7429
7430 //fprintf(stderr,"Adding:%d:%s\n",strlen(directory),directory);
7431
7432 // Fill in some default values for the new record.
7433 temp_record->selected = 0;
7434 temp_record->auto_maps = 0;
7435 temp_record->XmStringPtr = NULL;
7436 }
7437
7438 // Update the values. By this point we have a struct to fill
7439 // in, whether it's a new or old struct doesn't matter. Convert
7440 // the values from lat/long to Xastir coordinate system.
7441 xastir_snprintf(temp_record->filename,MAX_FILENAME,"%s",directory);
7442
7443 temp_record->bottom = 0;
7444 temp_record->top = 0;
7445 temp_record->left = 0;
7446 temp_record->right = 0;
7447 temp_record->accessed = 1;
7448 temp_record->max_zoom = 0;
7449 temp_record->min_zoom = 0;
7450 temp_record->map_layer = 0;
7451 temp_record->draw_filled = 0;
7452 temp_record->usgs_drg = 2;
7453 }
7454
7455
7456
7457
7458
7459 // Function called by the various draw_* functions when in indexing
7460 // mode. Causes an update of the index list in memory. Input
7461 // parameters are in the Xastir coordinate system due to speed
7462 // considerations. Records are inserted in alphanumerical order.
index_update_xastir(char * filename,unsigned long bottom,unsigned long top,unsigned long left,unsigned long right,int default_map_layer)7463 void index_update_xastir(char *filename,
7464 unsigned long bottom,
7465 unsigned long top,
7466 unsigned long left,
7467 unsigned long right,
7468 int default_map_layer)
7469 {
7470
7471 map_index_record *current = map_index_head;
7472 map_index_record *previous = map_index_head;
7473 map_index_record *temp_record = NULL;
7474 int done = 0;
7475 int i;
7476
7477
7478 // Check for initial bad input
7479 if ( (filename == NULL)
7480 || (filename[0] == '\0')
7481 || (filename[strlen(filename) - 1] == '/') )
7482 {
7483 fprintf(stderr,"index_update_xastir: Bad input: %s\n",filename);
7484 return;
7485 }
7486 // Make sure there aren't any weird characters in the filename
7487 // that might cause problems later. Look for control characters
7488 // and convert them to string-end characters.
7489 for ( i = 0; i < (int)strlen(filename); i++ )
7490 {
7491 // Change any control characters to '\0' chars
7492 if (filename[i] < 0x20)
7493 {
7494
7495 fprintf(stderr,"\nindex_update_xastir: Found control char 0x%02x in map file/map directory name:\n%s\n",
7496 filename[i],
7497 filename);
7498
7499 filename[i] = '\0'; // Terminate it here
7500 }
7501 }
7502 // Check if the string is _now_ bogus
7503 if (filename[0] == '\0')
7504 {
7505 fprintf(stderr,"index_update_xastir: Bad input: %s\n",filename);
7506 return;
7507 }
7508
7509 //fprintf(stderr,"index_update_xastir: (%lu,%lu)\t(%lu,%lu)\t%s\n",
7510 // bottom, top, left, right, filename );
7511
7512 //if (map_index_head == NULL)
7513 // fprintf(stderr,"Empty list\n");
7514
7515 // Skip dbf and shx map extensions. Really should make this
7516 // case-independent...
7517 if ( strstr(filename,"shx")
7518 || strstr(filename,"dbf")
7519 || strstr(filename,"SHX")
7520 || strstr(filename,"DBF") )
7521 {
7522 return;
7523 }
7524
7525 // Search for a matching filename in the linked list
7526 while ((current != NULL) && !done)
7527 {
7528 int test;
7529
7530 //fprintf(stderr,"Comparing %s to\n %s\n",current->filename,filename);
7531
7532 test = strcmp(current->filename,filename);
7533 if (test == 0)
7534 {
7535 // Found a match!
7536 //fprintf(stderr,"Found: Updating entry for %s\n",filename);
7537 temp_record = current;
7538 done++; // Exit the while loop
7539 }
7540 else if (test > 0) // Found a string past us in the
7541 {
7542 // alphabet. Insert ahead of this
7543 // last record.
7544
7545 //fprintf(stderr,"\n%s\n%s\n",current->filename,filename);
7546
7547 //fprintf(stderr,"Not Found: Inserting an index record for %s\n",filename);
7548 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
7549 CHECKMALLOC(temp_record);
7550
7551 if (current == map_index_head) // Start of list!
7552 {
7553 // Insert new record at head of list
7554 temp_record->next = map_index_head;
7555 map_index_head = temp_record;
7556 //fprintf(stderr,"Inserting at head of list\n");
7557 }
7558 else
7559 {
7560 // Insert new record before "current"
7561 previous->next = temp_record;
7562 temp_record->next = current;
7563 //fprintf(stderr,"Inserting before current\n");
7564 }
7565
7566 //fprintf(stderr,"Adding:%d:%s\n",strlen(filename),filename);
7567
7568 // Fill in some default values for the new record
7569 //WE7U
7570 // Here's where we might look at the file extension and assign
7571 // default map_layer fields based on that.
7572 temp_record->max_zoom = 0;
7573 temp_record->min_zoom = 0;
7574 temp_record->map_layer = default_map_layer;
7575 temp_record->selected = 0;
7576 temp_record->XmStringPtr = NULL;
7577
7578 if ( strstr(filename,".geo")
7579 || strstr(filename,".GEO")
7580 || strstr(filename,".Geo"))
7581 {
7582 temp_record->auto_maps = 0;
7583 }
7584 else
7585 {
7586 temp_record->auto_maps = 1;
7587 }
7588
7589 if ( strstr(filename,".shp")
7590 || strstr(filename,".SHP")
7591 || strstr(filename,".Shp") )
7592 {
7593 temp_record->draw_filled = 2; // Auto
7594 }
7595 else
7596 {
7597 temp_record->draw_filled = 0; // No-Fill
7598 }
7599
7600 if ( strstr(filename,".tif")
7601 || strstr(filename,".TIF")
7602 || strstr(filename,".Tif") )
7603 {
7604 temp_record->usgs_drg = 2; // Auto
7605 }
7606 else
7607 {
7608 temp_record->usgs_drg = 0; // No
7609 }
7610
7611 //current = current->next;
7612 done++;
7613 }
7614 else // Haven't gotten to the correct insertion point yet
7615 {
7616 previous = current; // Save ptr to last record
7617 current = current->next;
7618 }
7619 }
7620
7621 if (!done) // Matching record not found, add a
7622 {
7623 // record to the end of the list
7624 //fprintf(stderr,"Not Found: Adding an index record for %s\n",filename);
7625 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
7626 CHECKMALLOC(temp_record);
7627
7628 temp_record->next = NULL;
7629
7630 if (previous == NULL) // Empty list
7631 {
7632 map_index_head = temp_record;
7633 //fprintf(stderr,"First record in new list\n");
7634 }
7635 else // Else at end of list
7636 {
7637 previous->next = temp_record;
7638 //fprintf(stderr,"Adding to end of list: %s\n",filename);
7639 }
7640
7641 //fprintf(stderr,"Adding:%d:%s\n",strlen(filename),filename);
7642
7643 // Fill in some default values for the new record
7644 //WE7U
7645 // Here's where we might look at the file extension and assign
7646 // default map_layer fields based on that.
7647 temp_record->max_zoom = 0;
7648 temp_record->min_zoom = 0;
7649 temp_record->map_layer = default_map_layer;
7650 temp_record->selected = 0;
7651 temp_record->XmStringPtr = NULL;
7652
7653 if ( strstr(filename,".geo")
7654 || strstr(filename,".GEO")
7655 || strstr(filename,".Geo"))
7656 {
7657 temp_record->auto_maps = 0;
7658 }
7659 else
7660 {
7661 temp_record->auto_maps = 1;
7662 }
7663
7664 if ( strstr(filename,".shp")
7665 || strstr(filename,".SHP")
7666 || strstr(filename,".Shp") )
7667 {
7668 temp_record->draw_filled = 2; // Auto
7669 }
7670 else
7671 {
7672 temp_record->draw_filled = 0; // No-Fill
7673 }
7674
7675 if ( strstr(filename,".tif")
7676 || strstr(filename,".TIF")
7677 || strstr(filename,".Tif") )
7678 {
7679 temp_record->usgs_drg = 2; // Auto
7680 }
7681 else
7682 {
7683 temp_record->usgs_drg = 0; // No
7684 }
7685
7686 }
7687
7688 // Update the values. By this point we have a struct to fill
7689 // in, whether it's a new or old struct doesn't matter. Convert
7690 // the values from lat/long to Xastir coordinate system.
7691 xastir_snprintf(temp_record->filename,MAX_FILENAME,"%s",filename);
7692
7693 temp_record->bottom = bottom;
7694 temp_record->top = top;
7695 temp_record->left = left;
7696 temp_record->right = right;
7697 temp_record->accessed = 1;
7698 }
7699
7700
7701
7702
7703
7704 // Function called by the various draw_* functions when in indexing
7705 // mode. Causes an update of the index list in memory. Input
7706 // parameters are in lat/long, which are converted to Xastir
7707 // coordinates for storage due to speed considerations. Records are
7708 // inserted in alphanumerical order.
index_update_ll(char * filename,double bottom,double top,double left,double right,int default_map_layer)7709 void index_update_ll(char *filename,
7710 double bottom,
7711 double top,
7712 double left,
7713 double right,
7714 int default_map_layer)
7715 {
7716
7717 map_index_record *current = map_index_head;
7718 map_index_record *previous = map_index_head;
7719 map_index_record *temp_record = NULL;
7720 int done = 0;
7721 unsigned long temp_left, temp_right, temp_top, temp_bottom;
7722 int ok;
7723 int i;
7724
7725
7726 // Check for initial bad input
7727 if ( (filename == NULL)
7728 || (filename[0] == '\0')
7729 || (filename[strlen(filename) - 1] == '/') )
7730 {
7731 fprintf(stderr,"index_update_ll: Bad input: %s\n",filename);
7732 return;
7733 }
7734 // Make sure there aren't any weird characters in the filename
7735 // that might cause problems later. Look for control characters
7736 // and convert them to string-end characters.
7737 for ( i = 0; i < (int)strlen(filename); i++ )
7738 {
7739 // Change any control characters to '\0' chars
7740 if (filename[i] < 0x20)
7741 {
7742
7743 fprintf(stderr,"\nindex_update_ll: Found control char 0x%02x in map file/map directory name:\n%s\n",
7744 filename[i],
7745 filename);
7746
7747 filename[i] = '\0'; // Terminate it here
7748 }
7749 }
7750 // Check if the string is _now_ bogus
7751 if (filename[0] == '\0')
7752 {
7753 fprintf(stderr,"index_update_ll: Bad input: %s\n",filename);
7754 return;
7755 }
7756
7757 //fprintf(stderr,"index_update_ll: (%15.10g,%15.10g)\t(%15.10g,%15.10g)\t%s\n",
7758 // bottom, top, left, right, filename );
7759
7760 //if (map_index_head == NULL)
7761 // fprintf(stderr,"Empty list\n");
7762
7763 // Skip dbf and shx map extensions. Really should make this
7764 // case-independent...
7765 if ( strstr(filename,"shx")
7766 || strstr(filename,"dbf")
7767 || strstr(filename,"SHX")
7768 || strstr(filename,"DBF") )
7769 {
7770 return;
7771 }
7772
7773 // Search for a matching filename in the linked list
7774 while ((current != NULL) && !done)
7775 {
7776 int test;
7777
7778 //fprintf(stderr,"Comparing %s to\n %s\n",current->filename,filename);
7779
7780 test = strcmp(current->filename,filename);
7781
7782 if (test == 0)
7783 {
7784 // Found a match!
7785 //fprintf(stderr,"Found: Updating entry for %s\n",filename);
7786 temp_record = current;
7787 done++; // Exit the while loop
7788 }
7789
7790 else if (test > 0)
7791 {
7792 // Found a string past us in the alphabet. Insert ahead
7793 // of this last record.
7794
7795 //fprintf(stderr,"\n%s\n%s\n",current->filename,filename);
7796
7797 //fprintf(stderr,"Not Found: Inserting an index record for %s\n",filename);
7798 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
7799 CHECKMALLOC(temp_record);
7800
7801 if (current == map_index_head) // Start of list!
7802 {
7803 // Insert new record at head of list
7804 temp_record->next = map_index_head;
7805 map_index_head = temp_record;
7806 //fprintf(stderr,"Inserting at head of list\n");
7807 }
7808 else
7809 {
7810 // Insert new record before "current"
7811 previous->next = temp_record;
7812 temp_record->next = current;
7813 //fprintf(stderr,"Inserting before current\n");
7814 }
7815
7816 //fprintf(stderr,"Adding:%d:%s\n",strlen(filename),filename);
7817
7818 // Fill in some default values for the new record
7819 //WE7U
7820 // Here's where we might look at the file extension and assign
7821 // default map_layer fields based on that.
7822 temp_record->max_zoom = 0;
7823 temp_record->min_zoom = 0;
7824 temp_record->map_layer = default_map_layer;
7825 temp_record->selected = 0;
7826 temp_record->XmStringPtr = NULL;
7827
7828 if ( strstr(filename,".geo")
7829 || strstr(filename,".GEO")
7830 || strstr(filename,".Geo"))
7831 {
7832 temp_record->auto_maps = 0;
7833 }
7834 else
7835 {
7836 temp_record->auto_maps = 1;
7837 }
7838
7839 if ( strstr(filename,".shp")
7840 || strstr(filename,".SHP")
7841 || strstr(filename,".Shp") )
7842 {
7843 temp_record->draw_filled = 2; // Auto
7844 }
7845 else
7846 {
7847 temp_record->draw_filled = 0; // No-Fill
7848 }
7849
7850 if ( strstr(filename,".tif")
7851 || strstr(filename,".TIF")
7852 || strstr(filename,".Tif") )
7853 {
7854 temp_record->usgs_drg = 2; // Auto
7855 }
7856 else
7857 {
7858 temp_record->usgs_drg = 0; // No
7859 }
7860
7861 //current = current->next;
7862 done++;
7863 }
7864 else // Haven't gotten to the correct insertion point yet
7865 {
7866 previous = current; // Save ptr to last record
7867 current = current->next;
7868 }
7869 }
7870
7871 if (!done) // Matching record not found, didn't find alpha
7872 {
7873 // chars after our string either, add record to
7874 // the end of the list.
7875
7876 //fprintf(stderr,"Not Found: Adding an index record for %s\n",filename);
7877 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
7878 CHECKMALLOC(temp_record);
7879
7880 temp_record->next = NULL;
7881
7882 if (previous == NULL) // Empty list
7883 {
7884 map_index_head = temp_record;
7885 //fprintf(stderr,"First record in new list\n");
7886 }
7887 else // Else at end of list
7888 {
7889 previous->next = temp_record;
7890 //fprintf(stderr,"Adding to end of list: %s\n",filename);
7891 }
7892
7893 //fprintf(stderr,"Adding:%d:%s\n",strlen(filename),filename);
7894
7895 // Fill in some default values for the new record
7896 //WE7U
7897 // Here's where we might look at the file extension and assign
7898 // default map_layer fields based on that.
7899 temp_record->max_zoom = 0;
7900 temp_record->min_zoom = 0;
7901 temp_record->map_layer = default_map_layer;
7902 temp_record->selected = 0;
7903 temp_record->XmStringPtr = NULL;
7904
7905 if ( strstr(filename,".geo")
7906 || strstr(filename,".GEO")
7907 || strstr(filename,".Geo"))
7908 {
7909 temp_record->auto_maps = 0;
7910 }
7911 else
7912 {
7913 temp_record->auto_maps = 1;
7914 }
7915
7916 if ( strstr(filename,".shp")
7917 || strstr(filename,".SHP")
7918 || strstr(filename,".Shp") )
7919 {
7920 temp_record->draw_filled = 2; // Auto
7921 }
7922 else
7923 {
7924 temp_record->draw_filled = 0; // No-Fill
7925 }
7926
7927 if ( strstr(filename,".tif")
7928 || strstr(filename,".TIF")
7929 || strstr(filename,".Tif") )
7930 {
7931 temp_record->usgs_drg = 2; // Auto
7932 }
7933 else
7934 {
7935 temp_record->usgs_drg = 0; // No
7936 }
7937
7938 }
7939
7940 // Update the values. By this point we have a struct to fill
7941 // in, whether it's a new or old struct doesn't matter. Convert
7942 // the values from lat/long to Xastir coordinate system.
7943
7944 // In this case the struct uses MAX_FILENAME for the length of
7945 // the field, so the below statement is ok.
7946 xastir_snprintf(temp_record->filename,MAX_FILENAME,"%s",filename);
7947
7948 ok = convert_to_xastir_coordinates( &temp_left,
7949 &temp_top,
7950 (float)left,
7951 (float)top);
7952 if (!ok)
7953 {
7954 fprintf(stderr,"%s\n\n",filename);
7955 }
7956
7957 ok = convert_to_xastir_coordinates( &temp_right,
7958 &temp_bottom,
7959 (float)right,
7960 (float)bottom);
7961 if (!ok)
7962 {
7963 fprintf(stderr,"%s\n\n",filename);
7964 }
7965
7966 temp_record->bottom = temp_bottom;
7967 temp_record->top = temp_top;
7968 temp_record->left = temp_left;
7969 temp_record->right = temp_right;
7970 temp_record->accessed = 1;
7971 }
7972
7973
7974
7975
7976
7977 // Function which will update the "accessed" variable on either a
7978 // directory or a filename in the map index.
index_update_accessed(char * filename)7979 static void index_update_accessed(char *filename)
7980 {
7981 map_index_record *current = map_index_head;
7982 int done = 0;
7983 int i;
7984
7985
7986 // Check for initial bad input
7987 if ( (filename == NULL) || (filename[0] == '\0') )
7988 {
7989 fprintf(stderr,"index_update_accessed: Bad input: %s\n",filename);
7990 return;
7991 }
7992
7993 // Make sure there aren't any weird characters in the filename
7994 // that might cause problems later. Look for control characters
7995 // and convert them to string-end characters.
7996 for ( i = 0; i < (int)strlen(filename); i++ )
7997 {
7998 // Change any control characters to '\0' chars
7999 if (filename[i] < 0x20)
8000 {
8001
8002 fprintf(stderr,"\nindex_update_accessed: Found control char 0x%02x in map file/map directory name:\n%s\n",
8003 filename[i],
8004 filename);
8005
8006 filename[i] = '\0'; // Terminate it here
8007 }
8008 }
8009 // Check if the string is _now_ bogus
8010 if (filename[0] == '\0')
8011 {
8012 fprintf(stderr,"index_update_accessed: Bad input: %s\n",filename);
8013 return;
8014 }
8015
8016 // Skip dbf and shx map extensions. Really should make this
8017 // case-independent...
8018 if ( strstr(filename,"shx")
8019 || strstr(filename,"dbf")
8020 || strstr(filename,"SHX")
8021 || strstr(filename,"DBF") )
8022 {
8023 return;
8024 }
8025
8026 // Search for a matching filename in the linked list
8027 while ((current != NULL) && !done)
8028 {
8029 int test;
8030
8031 //fprintf(stderr,"Comparing %s to\n %s\n",current->filename,filename);
8032
8033 test = strcmp(current->filename,filename);
8034
8035 if (test == 0)
8036 {
8037 // Found a match!
8038 //fprintf(stderr,"Found: Updating entry for %s\n\n",filename);
8039 current->accessed = 1;
8040 done++; // Exit the while loop
8041 }
8042
8043 else // Haven't gotten to the correct insertion point yet
8044 {
8045 current = current->next;
8046 }
8047 }
8048 }
8049
8050
8051
8052
8053
8054 // Function called by map_onscreen_index()
8055 //
8056 // This function returns:
8057 // 0 if the map isn't in the index
8058 // 1 if the map is listed in the index
8059 // Four parameters listing the extents of the map
8060 //
8061 // The updated parameters are in the Xastir coordinate system for
8062 // speed reasons.
8063 //
8064 // Note that the index retrieval could be made much faster by
8065 // storing the data in a hash instead of a linked list. This is
8066 // just an initial implementation to see what speedups are possible.
8067 // Hashing might be next. --we7u
8068 //
8069 // Note that since we've alphanumerically ordered the list, we can
8070 // stop when we hit something after this filename in the alphabet.
8071 // It speeds things up quite a bit.
8072 //
8073 // In order to speed this up slightly for the general case, we'll
8074 // assume that we'll be fetching indexes in alphabetical order, as
8075 // that's how we store them everywhere. We'll save the last map
8076 // index pointer away and start searching there each time. That
8077 // should make all but the _first_ lookup much faster.
8078 //
8079 map_index_record *last_index_lookup = NULL;
8080
index_retrieve(char * filename,unsigned long * bottom,unsigned long * top,unsigned long * left,unsigned long * right,int * max_zoom,int * min_zoom,int * map_layer,int * draw_filled,int * usgs_drg,int * auto_maps)8081 int index_retrieve(char *filename,
8082 unsigned long *bottom,
8083 unsigned long *top,
8084 unsigned long *left,
8085 unsigned long *right,
8086 int *max_zoom,
8087 int *min_zoom,
8088 int *map_layer,
8089 int *draw_filled,
8090 int *usgs_drg,
8091 int *auto_maps)
8092 {
8093
8094 map_index_record *current;
8095 int status = 0;
8096
8097
8098 if ( (filename == NULL)
8099 || (strlen(filename) >= MAX_FILENAME) )
8100 {
8101 return(status);
8102 }
8103
8104 // Attempt to start where we left off last time
8105 if (last_index_lookup != NULL)
8106 {
8107 current = last_index_lookup;
8108 }
8109 else
8110 {
8111 current = map_index_head;
8112 //fprintf(stderr,"Start at beginning:%s\t", filename);
8113 }
8114
8115 // Check to see if we're past the correct area. If so, start at
8116 // the beginning of the index instead.
8117 //
8118 if (current
8119 && ((current->filename[0] > filename[0])
8120 || (strcmp(current->filename, filename) > 0)))
8121 {
8122 //
8123 // We're past the correct point. Start at the beginning of
8124 // the list unless we're already there.
8125 //
8126 if (current != map_index_head)
8127 {
8128 current = map_index_head;
8129 }
8130 //fprintf(stderr,"Start at beginning:%s\t", filename);
8131 }
8132
8133 //
8134 // Search for a matching filename in the linked list.
8135 //
8136
8137 // Check the first char only. Loop until they match or go past.
8138 // This is our high-speed method to get to the correct search
8139 // area.
8140 //
8141 while (current && (current->filename[0] < filename[0]))
8142 {
8143 // Save the pointer away for next time. There's a reason we
8144 // save it before we increment the counter: For "z" weather
8145 // alerts, it's nice to have it scan just the very last of
8146 // the list before it fails, instead of scanning the entire
8147 // list each time and then failing. Need to find out why
8148 // weather alerts always fail, and therefore why this
8149 // routine gets called every time for them.
8150 //
8151 last_index_lookup = current;
8152 current = current->next;
8153 //fprintf(stderr,"1");
8154 }
8155
8156 // Stay in this loop while the first char matches. This is our
8157 // active search area.
8158 //
8159 while (current && (current->filename[0] == filename[0]))
8160 {
8161 int result;
8162
8163 // Check the entire string
8164 result = strcmp(current->filename, filename);
8165
8166 if (result == 0)
8167 {
8168 // Found a match!
8169 status = 1;
8170 *bottom = current->bottom;
8171 *top = current->top;
8172 *left = current->left;
8173 *right = current->right;
8174 *max_zoom = current->max_zoom;
8175 *min_zoom = current->min_zoom;
8176 *map_layer = current->map_layer;
8177 *draw_filled = current->draw_filled;
8178 *usgs_drg = current->usgs_drg;
8179 *auto_maps = current->auto_maps;
8180 //fprintf(stderr," Found it\n");
8181 return(status);
8182 }
8183 else if (result > 0)
8184 {
8185 // We're past it in the index. We didn't find it in the
8186 // index.
8187 //fprintf(stderr," Did not find1\n");
8188 return(status);
8189 }
8190 else // Not found yet, look at the next
8191 {
8192 // Save the pointer away for next time. There's a
8193 // reason we save it before we increment the counter:
8194 // For "z" weather alerts, it's nice to have it scan
8195 // just the very last of the list before it fails,
8196 // instead of scanning the entire list each time and
8197 // then failing. Need to find out why weather alerts
8198 // always fail, and therefore why this routine gets
8199 // called every time for them.
8200 //
8201 last_index_lookup = current;
8202 current = current->next;
8203 //fprintf(stderr,"2");
8204 }
8205 }
8206
8207 // We're past the correct search area and didn't find it.
8208 //fprintf(stderr," Did not find2\n");
8209
8210 return(status);
8211 }
8212
8213
8214
8215
8216
8217 // Saves the linked list pointed to by map_index_head to a file.
8218 // Keeps the same order as the memory linked list. Delete records
8219 // in the in-memory linked list for which the "accessed" variable is
8220 // 0 or filename is empty.
8221 //
index_save_to_file(void)8222 void index_save_to_file(void)
8223 {
8224 FILE *f;
8225 map_index_record *current;
8226 // map_index_record *last;
8227 char out_string[MAX_FILENAME*2];
8228 char map_index_path[MAX_VALUE];
8229
8230 get_user_base_dir(MAP_INDEX_DATA, map_index_path, sizeof(map_index_path));
8231
8232
8233 //fprintf(stderr,"Saving map index to file\n");
8234
8235 f = fopen( map_index_path, "w" );
8236
8237 if (f == NULL)
8238 {
8239 fprintf(stderr,"Couldn't create/update map index file: %s\n",
8240 map_index_path );
8241 return;
8242 }
8243
8244 current = map_index_head;
8245 // last = current;
8246
8247 while (current != NULL)
8248 {
8249 int i;
8250
8251 // Make sure there aren't any weird characters in the
8252 // filename that might cause problems later. Look for
8253 // control characters and convert them to string-end
8254 // characters.
8255 for ( i = 0; i < (int)strlen(current->filename); i++ )
8256 {
8257 // Change any control characters to '\0' chars
8258 if (current->filename[i] < 0x20)
8259 {
8260
8261 fprintf(stderr,"\nindex_save_to_file: Found control char 0x%02x in map name:\n%s\n",
8262 current->filename[i],
8263 current->filename);
8264
8265 current->filename[i] = '\0'; // Terminate it here
8266 }
8267 }
8268
8269 // Save to file if filename non-blank and record has the
8270 // accessed field set.
8271 if ( (current->filename[0] != '\0')
8272 && (current->accessed != 0) )
8273 {
8274
8275 // Write each object out to the file as one
8276 // comma-delimited line
8277 xastir_snprintf(out_string,
8278 sizeof(out_string),
8279 "%010lu,%010lu,%010lu,%010lu,%05d,%01d,%01d,%01d,%05d,%05d,%s\n",
8280 current->bottom,
8281 current->top,
8282 current->left,
8283 current->right,
8284 current->map_layer,
8285 current->draw_filled,
8286 current->usgs_drg,
8287 current->auto_maps,
8288 current->max_zoom,
8289 current->min_zoom,
8290 current->filename);
8291
8292 if (fprintf(f,"%s",out_string) < (int)strlen(out_string))
8293 {
8294 // Failed to write
8295 fprintf(stderr,"Couldn't write objects to map index file: %s\n",
8296 map_index_path );
8297 current = NULL; // All done
8298 }
8299 // Set up pointers for next loop iteration
8300 // last = current;
8301
8302 if (current != NULL)
8303 {
8304 current = current->next;
8305 }
8306 }
8307
8308
8309 else
8310 {
8311 // last = current;
8312 current = current->next;
8313 }
8314 /*
8315 //WE7U
8316 else { // Delete this record from our list! It's a record
8317 // for a map file that doesn't exist in the
8318 // filesystem anymore.
8319 if (last == current) { // We're at the head of the list
8320 map_index_head = current->next;
8321
8322 // Remember to free the XmStringPtr if we use this bit of code
8323 // again.
8324
8325 free(current);
8326
8327 // Set up pointers for next loop iteration
8328 current = map_index_head;
8329 last = current;
8330 }
8331 else { // Not the first record in the list
8332 map_index_record *gone;
8333
8334 gone = current; // Save ptr to record we wish to delete
8335 last->next = current->next; // Unlink from list
8336
8337 // Remember to free the XmStringPtr if we use this bit of code
8338 // again.
8339
8340 free(gone);
8341
8342 // Set up pointers for next loop iteration
8343 // "last" is still ok
8344 current = last->next;
8345 }
8346 }
8347 */
8348 }
8349 (void)fclose(f);
8350 }
8351
8352
8353
8354
8355
8356 // This function is currently not used.
8357 //
8358 // Function used to add map directories/files to the in-memory map
8359 // index. Causes an update of the index list in memory. Input
8360 // records are inserted in alphanumerical order. This function is
8361 // called from the index_restore_from_file() function below. When
8362 // this function is called the new record has all of the needed
8363 // information in it.
8364 //
8365 /*
8366 static void index_insert_sorted(map_index_record *new_record) {
8367
8368 map_index_record *current = map_index_head;
8369 map_index_record *previous = map_index_head;
8370 int done = 0;
8371 int i;
8372
8373
8374 //fprintf(stderr,"index_insert_sorted: %s\n", new_record->filename );
8375
8376 // Check for bad input.
8377 if (new_record == NULL) {
8378 fprintf(stderr,"index_insert_sorted: Bad input.\n");
8379 return;
8380 }
8381 // Make sure there aren't any weird characters in the filename
8382 // that might cause problems later. Look for any control
8383 // characters and convert them to string-end characters.
8384 for ( i = 0; i < (int)strlen(new_record->filename); i++ ) {
8385 if (new_record->filename[i] < 0x20) {
8386
8387 fprintf(stderr,"\nindex_insert_sorted: Found control char 0x%02x in map name:\n%s\n",
8388 new_record->filename[i],
8389 new_record->filename);
8390
8391 new_record->filename[i] = '\0'; // Terminate it here
8392 }
8393 }
8394 // Check if the string is _now_ bogus
8395 if (new_record->filename[0] == '\0') {
8396 fprintf(stderr,"index_insert_sorted: Bad input.\n");
8397 return;
8398 }
8399
8400 //if (map_index_head == NULL)
8401 // fprintf(stderr,"Empty list\n");
8402
8403 // Search for a matching filename in the linked list
8404 while ((current != NULL) && !done) {
8405 int test;
8406
8407 //fprintf(stderr,"Comparing %s to\n %s\n",
8408 // current->filename, new_record->filename);
8409
8410 test = strcmp(current->filename, new_record->filename);
8411
8412 if (test == 0) { // Found a match!
8413 int selected;
8414
8415 //fprintf(stderr,"Found a match: Updating entry for %s\n",new_record->filename);
8416
8417 // Save this away temporarily.
8418 selected = current->selected;
8419
8420 // Copy the fields across and then free new_record. We
8421 // overwrite the contents of the existing record.
8422 xastir_snprintf(current->filename,
8423 MAX_FILENAME,
8424 "%s",
8425 new_record->filename);
8426 current->bottom = new_record->bottom;
8427 current->top = new_record->top;
8428 current->left = new_record->left;
8429 current->right = new_record->right;
8430 current->accessed = 1;
8431 current->max_zoom = new_record->max_zoom;
8432 current->min_zoom = new_record->min_zoom;
8433 current->map_layer = new_record->map_layer;
8434 current->draw_filled = new_record->draw_filled;
8435 current->usgs_drg = new_record->usgs_drg;
8436 current->selected = selected; // Restore it
8437 current->auto_maps = new_record->auto_maps;
8438
8439 // Remember to free the XmStringPtr if we use this bit of code
8440 // again.
8441
8442 free(new_record); // Don't need it anymore
8443
8444 done++; // Exit loop, "current" points to found record
8445 }
8446 else if (test > 0) { // Found a string past us in the
8447 // alphabet. Insert ahead of this
8448 // last record.
8449
8450 //fprintf(stderr,"Not Found, inserting: %s\n", new_record->filename);
8451 //fprintf(stderr," Before record: %s\n", current->filename);
8452
8453 if (current == map_index_head) { // Start of list!
8454 // Insert new record at head of list
8455 new_record->next = map_index_head;
8456 map_index_head = new_record;
8457 //fprintf(stderr,"Inserting at head of list\n");
8458 }
8459 else { // Insert between "previous" and "current"
8460 // Insert new record before "current"
8461 previous->next = new_record;
8462 new_record->next = current;
8463 //fprintf(stderr,"Inserting before current\n");
8464 }
8465
8466 //fprintf(stderr,"Adding:%d:%s\n",strlen(filename),filename);
8467
8468 // Fill in some default values for the new record that
8469 // don't exist in the map_index.sys file.
8470 new_record->selected = 0;
8471
8472 if ( strstr(new_record->filename,".geo")
8473 || strstr(new_record->filename,".GEO")
8474 || strstr(new_record->filename,".Geo") ) {
8475 new_record->auto_maps = 0;
8476 }
8477 else {
8478 new_record->auto_maps = 1;
8479 }
8480
8481 //current = current->next;
8482 done++;
8483 }
8484 else { // Haven't gotten to the correct insertion point yet
8485 previous = current; // Save ptr to last record
8486 current = current->next;
8487 }
8488 }
8489
8490 if (!done) { // Matching record not found, add the record to
8491 // the end of the list. "previous" points to the last
8492 // record in the list or NULL (empty list).
8493
8494 //fprintf(stderr,"Not Found: Adding to end: %s\n",new_record->filename);
8495
8496 new_record->next = NULL;
8497
8498 if (previous == NULL) { // Empty list
8499 map_index_head = new_record;
8500 //fprintf(stderr,"First record in new list\n");
8501 }
8502 else { // Else at end of list
8503 previous->next = new_record;
8504 //fprintf(stderr,"Adding to end of list: %s\n",new_record->filename);
8505 }
8506
8507 //fprintf(stderr,"Adding:%d:%s\n",strlen(new_record->filename),new_record->filename);
8508
8509 // Fill in some default values for the new record.
8510 new_record->selected = 0;
8511
8512 if ( strstr(new_record->filename,".geo")
8513 || strstr(new_record->filename,".GEO")
8514 || strstr(new_record->filename,".Geo") ) {
8515 new_record->auto_maps = 0;
8516 }
8517 else {
8518 new_record->auto_maps = 1;
8519 }
8520 }
8521 }
8522 */
8523
8524
8525
8526
8527
8528 // sort map index
8529 // simple bubble sort, since we should be sorted already
8530 //
index_sort(void)8531 static void index_sort(void)
8532 {
8533 map_index_record *current, *previous, *next;
8534 int changed = 1;
8535 int loops = 0; // for debug stats
8536
8537 previous = map_index_head;
8538 next = NULL;
8539 // fprintf(stderr, "index_sort: start.\n");
8540 // check if we have any records at all, and at least two
8541 if ( (previous != NULL) && (previous->next != NULL) )
8542 {
8543 current = previous->next;
8544 while ( changed == 1)
8545 {
8546 changed = 0;
8547 if (current->next != NULL)
8548 {
8549 next = current->next;
8550 }
8551 if ( strcmp( previous->filename, current->filename) >= 0 )
8552 {
8553 // out of order - swap them
8554 current->next = previous;
8555 previous->next = next;
8556 map_index_head = current;
8557 current = previous;
8558 previous = map_index_head;
8559 changed = 1;
8560 }
8561
8562 while ( next != NULL )
8563 {
8564 if ( strcmp( current->filename, next->filename) >= 0 )
8565 {
8566 // out of order - swap them
8567 current->next = next->next;
8568 previous->next = next;
8569 next->next = current;
8570 // get ready for the next iteration
8571 previous = next; // current already moved ahead from the swap
8572 next = current->next;
8573 changed = 1;
8574 }
8575 else
8576 {
8577 previous = current;
8578 current = next;
8579 next = current->next;
8580 }
8581 }
8582 previous = map_index_head;
8583 current = previous->next;
8584 next = current->next;
8585 loops++;
8586 }
8587 }
8588 // debug stats
8589 // fprintf(stderr, "index_sort: ran %d loops.\n", loops);
8590 }
8591
8592
8593
8594
8595
8596 // Snags the file and creates the linked list pointed to by the
8597 // map_index_head pointer. The memory linked list keeps the same
8598 // order as the entries in the file.
8599 //
8600 // NOTE: If we're converting from the old format to the new, we
8601 // need to call index_save_to_file() in order to write out the new
8602 // format once we're done.
8603 //
index_restore_from_file(void)8604 void index_restore_from_file(void)
8605 {
8606 FILE *f;
8607 map_index_record *temp_record;
8608 map_index_record *last_record;
8609 char in_string[MAX_FILENAME*2];
8610 int doing_migration = 0;
8611 char map_index_path[MAX_VALUE];
8612
8613 get_user_base_dir(MAP_INDEX_DATA, map_index_path, sizeof(map_index_path));
8614
8615
8616 //fprintf(stderr,"\nRestoring map index from file\n");
8617
8618 if (map_index_head != NULL)
8619 {
8620 fprintf(stderr,"Warning: index_restore_from_file(): map_index_head was non-null!\n");
8621 }
8622
8623 map_index_head = NULL; // Starting with empty list
8624 last_record = NULL;
8625
8626 f = fopen( map_index_path, "r" );
8627 if (f == NULL) // No map_index file yet
8628 {
8629 return;
8630 }
8631
8632 while (!feof (f)) // Loop through entire map_index file
8633 {
8634
8635 // Read one line from the file
8636 if ( get_line (f, in_string, MAX_FILENAME*2) )
8637 {
8638
8639 if (strlen(in_string) >= 15) // We have some data.
8640 {
8641 // Try to process the
8642 // line.
8643 char scanf_format[50];
8644 char old_scanf_format[50];
8645 char older_scanf_format[50];
8646 int processed;
8647 int i, jj;
8648
8649 //fprintf(stderr,"%s\n",in_string);
8650
8651 // Tweaked the string below so that it will track
8652 // along with MAX_FILENAME-1. We're constructing
8653 // the string "%lu,%lu,%lu,%lu,%d,%d,%2000c", where
8654 // the 2000 example number is from MAX_FILENAME.
8655 xastir_snprintf(scanf_format,
8656 sizeof(scanf_format),
8657 "%s%d%s",
8658 "%lu,%lu,%lu,%lu,%d,%d,%d,%d,%d,%d,%",
8659 MAX_FILENAME,
8660 "c");
8661 //fprintf(stderr,"%s\n",scanf_format);
8662
8663 // index predates addition of usgs_drg flag (26 Jul 2005)
8664 xastir_snprintf(old_scanf_format,
8665 sizeof(old_scanf_format),
8666 "%s%d%s",
8667 "%lu,%lu,%lu,%lu,%d,%d,%d,%d,%d,%",
8668 MAX_FILENAME,
8669 "c");
8670
8671 // index predates addition of min/max zoom (29 Oct 2003)
8672 xastir_snprintf(older_scanf_format,
8673 sizeof(older_scanf_format),
8674 "%s%d%s",
8675 "%lu,%lu,%lu,%lu,%d,%d,%d,%",
8676 MAX_FILENAME,
8677 "c");
8678
8679 // Malloc an index record. We'll add it to the list
8680 // only if the data looks reasonable.
8681 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
8682 CHECKMALLOC(temp_record);
8683
8684 memset(temp_record->filename, 0, sizeof(temp_record->filename));
8685 temp_record->next = NULL;
8686 temp_record->bottom = 64800001l;// Too high
8687 temp_record->top = 64800001l; // Too high
8688 temp_record->left = 129600001l; // Too high
8689 temp_record->right = 129600001l;// Too high
8690 temp_record->map_layer = -1; // Too low
8691 temp_record->draw_filled = -1; // Too low
8692 temp_record->usgs_drg = -1; // Too low
8693 temp_record->auto_maps = -1; // Too low
8694 temp_record->max_zoom = -1; // Too low
8695 temp_record->min_zoom = -1; // Too low
8696 temp_record->filename[0] = '\0';// Empty
8697
8698 processed = sscanf(in_string,
8699 scanf_format,
8700 &temp_record->bottom,
8701 &temp_record->top,
8702 &temp_record->left,
8703 &temp_record->right,
8704 &temp_record->map_layer,
8705 &temp_record->draw_filled,
8706 &temp_record->usgs_drg,
8707 &temp_record->auto_maps,
8708 &temp_record->max_zoom,
8709 &temp_record->min_zoom,
8710 temp_record->filename);
8711
8712 if (processed < 11)
8713 {
8714 // We're upgrading from an old format index file
8715 // that doesn't have usgs_drg. Try the
8716 // old_scanf_format string instead.
8717
8718 doing_migration = 1;
8719
8720 processed = sscanf(in_string,
8721 old_scanf_format,
8722 &temp_record->bottom,
8723 &temp_record->top,
8724 &temp_record->left,
8725 &temp_record->right,
8726 &temp_record->map_layer,
8727 &temp_record->draw_filled,
8728 &temp_record->auto_maps,
8729 &temp_record->max_zoom,
8730 &temp_record->min_zoom,
8731 temp_record->filename);
8732 if (processed < 10)
8733 {
8734 // It's really old, doesn't have min/max zoom either
8735 temp_record->max_zoom = -1; // Too low
8736 temp_record->min_zoom = -1; // Too low
8737
8738 processed = sscanf(in_string,
8739 older_scanf_format,
8740 &temp_record->bottom,
8741 &temp_record->top,
8742 &temp_record->left,
8743 &temp_record->right,
8744 &temp_record->map_layer,
8745 &temp_record->draw_filled,
8746 &temp_record->auto_maps,
8747 temp_record->filename);
8748 }
8749 // either way, it doesn't have usgs_drg, so add one
8750 // defaulting to Auto if it's a tif file, no if not
8751 if ( strstr(temp_record->filename,".tif")
8752 || strstr(temp_record->filename,".TIF")
8753 || strstr(temp_record->filename,".Tif") )
8754 {
8755 temp_record->usgs_drg = 2; // Auto
8756 }
8757 else
8758 {
8759 temp_record->usgs_drg = 0; // No
8760 }
8761 }
8762
8763 temp_record->XmStringPtr = NULL;
8764
8765 // Do some reasonableness checking on the parameters
8766 // we just parsed.
8767 //WE7U: First comparison here is always false
8768 // if ( (temp_record->bottom < 0l)
8769 // || (temp_record->bottom > 64800000l) ) {
8770 if (temp_record->bottom > 64800000l)
8771 {
8772
8773 processed = 0; // Reject this record
8774 fprintf(stderr,"\nindex_restore_from_file: bottom extent incorrect %lu in map name:\n%s\n",
8775 temp_record->bottom,
8776 temp_record->filename);
8777 }
8778
8779
8780 //WE7U: First comparison here is always false
8781 // if ( (temp_record->top < 0l)
8782 // || (temp_record->top > 64800000l) ) {
8783 if (temp_record->top > 64800000l)
8784 {
8785
8786 processed = 0; // Reject this record
8787 fprintf(stderr,"\nindex_restore_from_file: top extent incorrect %lu in map name:\n%s\n",
8788 temp_record->top,
8789 temp_record->filename);
8790 }
8791
8792 //WE7U: First comparison here is always false
8793 // if ( (temp_record->left < 0l)
8794 // || (temp_record->left > 129600000l) ) {
8795 if (temp_record->left > 129600000l)
8796 {
8797
8798 processed = 0; // Reject this record
8799 fprintf(stderr,"\nindex_restore_from_file: left extent incorrect %lu in map name:\n%s\n",
8800 temp_record->left,
8801 temp_record->filename);
8802 }
8803
8804 //WE7U: First comparison here is always false
8805 // if ( (temp_record->right < 0l)
8806 // || (temp_record->right > 129600000l) ) {
8807 if (temp_record->right > 129600000l)
8808 {
8809
8810 processed = 0; // Reject this record
8811 fprintf(stderr,"\nindex_restore_from_file: right extent incorrect %lu in map name:\n%s\n",
8812 temp_record->right,
8813 temp_record->filename);
8814 }
8815
8816 if ( (temp_record->max_zoom < 0)
8817 || (temp_record->max_zoom > 99999) )
8818 {
8819 // processed = 0; // Reject this record
8820 // fprintf(stderr,"\nindex_restore_from_file: max_zoom field incorrect %d in map name:\n%s\n",
8821 // temp_record->max_zoom,
8822 // temp_record->filename);
8823 // Assign a reasonable value
8824 temp_record->max_zoom = 0;
8825 //fprintf(stderr,"Assigning max_zoom of 0\n");
8826 }
8827
8828 if ( (temp_record->min_zoom < 0)
8829 || (temp_record->min_zoom > 99999) )
8830 {
8831 // processed = 0; // Reject this record
8832 // fprintf(stderr,"\nindex_restore_from_file: min_zoom field incorrect %d in map name:\n%s\n",
8833 // temp_record->min_zoom,
8834 // temp_record->filename);
8835 // Assign a reasonable value
8836 temp_record->min_zoom = 0;
8837 //fprintf(stderr,"Assigning min_zoom of 0\n");
8838 }
8839
8840 if ( (temp_record->map_layer < -99999)
8841 || (temp_record->map_layer > 99999) )
8842 {
8843 processed = 0; // Reject this record
8844 fprintf(stderr,"\nindex_restore_from_file: map_layer field incorrect %d in map name:\n%s\n",
8845 temp_record->map_layer,
8846 temp_record->filename);
8847 }
8848
8849 if ( (temp_record->draw_filled < 0)
8850 || (temp_record->draw_filled > 2) )
8851 {
8852 processed = 0; // Reject this record
8853 fprintf(stderr,"\nindex_restore_from_file: draw_filled field incorrect %d in map name:\n%s\n",
8854 temp_record->draw_filled,
8855 temp_record->filename);
8856 }
8857
8858 if ( (temp_record->usgs_drg < 0)
8859 || (temp_record->usgs_drg > 2) )
8860 {
8861 processed = 0; // Reject this record
8862 fprintf(stderr,"\nindex_restore_from_file: usgs_drg field incorrect %d in map name:\n%s\n",
8863 temp_record->usgs_drg,
8864 temp_record->filename);
8865 }
8866
8867 if ( (temp_record->auto_maps < 0)
8868 || (temp_record->auto_maps > 1) )
8869 {
8870 processed = 0; // Reject this record
8871 fprintf(stderr,"\nindex_restore_from_file: auto_maps field incorrect %d in map name:\n%s\n",
8872 temp_record->auto_maps,
8873 temp_record->filename);
8874 }
8875
8876 // Check whether the filename is empty
8877 if (strlen(temp_record->filename) == 0)
8878 {
8879 processed = 0; // Reject this record
8880 }
8881
8882 // Check for control characters in the filename.
8883 // Reject any that have them.
8884 jj = (int)strlen(temp_record->filename);
8885 for (i = 0; i < jj; i++)
8886 {
8887 if (temp_record->filename[i] < 0x20)
8888 {
8889
8890 processed = 0; // Reject this record
8891 fprintf(stderr,"\nindex_restore_from_file: Found control char 0x%02x in map name:\n%s\n",
8892 temp_record->filename[i],
8893 temp_record->filename);
8894 }
8895 }
8896
8897
8898 // Mark the record as accessed at this point.
8899 // At the stage where we're writing this list off to
8900 // disk, if the record hasn't been accessed by the
8901 // re-indexing, it doesn't get written. This has
8902 // the effect of flushes deleted files from the
8903 // index quickly.
8904 temp_record->accessed = 1;
8905
8906 // Default is not-selected. Later we read in the
8907 // selected_maps.sys file and tweak some of these
8908 // fields.
8909 temp_record->selected = 0;
8910
8911 temp_record->filename[MAX_FILENAME-1] = '\0';
8912
8913 // If correct number of parameters for either old or
8914 // new format
8915 if (processed == 11 || processed == 10 || processed == 8)
8916 {
8917
8918 //fprintf(stderr,"Restored: %s\n",temp_record->filename);
8919
8920 // Insert the new record into the in-memory map
8921 // list in sorted order.
8922 // --slow for large lists
8923 // index_insert_sorted(temp_record);
8924 // -- so we just add it to the end of the list
8925 // and sort it at the end tp make sure nobody
8926 // messed us up by editting the file by hand
8927 if ( last_record == NULL ) // first record
8928 {
8929 map_index_head = temp_record;
8930 }
8931 else
8932 {
8933 last_record->next = temp_record;
8934 }
8935 last_record = temp_record;
8936
8937
8938 // Remember that we may just have attached the
8939 // record to our in-memory map list, or we may
8940 // have free'ed it in the above function call.
8941 // Set the pointer to NULL to make sure we don't
8942 // try to do anything else with the memory.
8943 temp_record = NULL;
8944 }
8945 else // sscanf didn't parse the proper number of
8946 {
8947 // items. Delete the record.
8948
8949 // Remember to free the XmString pointer if necessary.
8950
8951 free(temp_record);
8952 // fprintf(stderr,"index_restore_from_file:sscanf parsing error\n");
8953 }
8954 }
8955 }
8956 }
8957 (void)fclose(f);
8958 // now that we have read the whole file, make sure it is sorted
8959 index_sort(); // probably should check for dup records
8960
8961 if (doing_migration)
8962 {
8963 // Save in new file format if we just did a migration from
8964 // old format to new.
8965 fprintf(stderr,"Migrating from old map_index.sys format to new format.\n");
8966 index_save_to_file();
8967 }
8968 }
8969
8970
8971
8972
8973
8974 // map_indexer()
8975 //
8976 // Recurses through the map directories finding map extents
8977 // and recording them in the map index. Once the indexing is
8978 // complete, write the current index out to a file.
8979 //
8980 // It'd be nice to call index_restore_from_file() from main.c:main()
8981 // so that an earlier copy of the index is restored before the map
8982 // display is created.
8983 //
8984 // If we set the "accessed" variable in the in-memory index to 0 for
8985 // each record and then run the indexer, the save-to-file function
8986 // will delete those with a value of 0 when writing to disk. Those
8987 // maps no longer exist in the filesystem and should be deleted. We
8988 // could either wipe them from the in-memory database at that time
8989 // as well, or wipe the whole list and re-read it from disk to get
8990 // the current list.
8991 //
8992 // If parameter is 0, we'll do the smart timestamp-checking
8993 // indexing.
8994 // If 1, we'll erase the in-memory index and do full indexing.
8995 //
map_indexer(int parameter)8996 void map_indexer(int parameter)
8997 {
8998 struct stat nfile;
8999 int check_times = 1;
9000 FILE *f;
9001 map_index_record *current;
9002 map_index_record *backup_list_head = NULL;
9003 char map_index_path[MAX_VALUE];
9004
9005 get_user_base_dir(MAP_INDEX_DATA, map_index_path, sizeof(map_index_path));
9006
9007
9008 if (debug_level & 16)
9009 {
9010 fprintf(stderr,"map_indexer() start\n");
9011 }
9012
9013 fprintf(stderr,"Indexing maps...\n");
9014
9015 #ifdef HAVE_LIBSHP
9016 // get rid of stored dbfawk signatures and force reload.
9017 clear_dbfawk_sigs();
9018 #endif
9019
9020 // Find the timestamp on the index file first. Save it away so
9021 // that the timestamp for each map file can be compared to it.
9022 if (stat ( map_index_path, &nfile) != 0)
9023 {
9024
9025 // File doesn't exist yet. Create it.
9026 f = fopen( map_index_path, "w" );
9027 if (f != NULL)
9028 {
9029 (void)fclose(f);
9030 }
9031 else
9032 fprintf(stderr,"Couldn't create map index file: %s\n",
9033 map_index_path );
9034
9035 check_times = 0; // Don't check the timestamps. Do them all.
9036 }
9037 else // File exists
9038 {
9039 map_index_timestamp = (time_t)nfile.st_mtime;
9040 check_times = 1;
9041 }
9042
9043
9044 if (parameter == 1) // Full indexing instead of timestamp-check indexing
9045 {
9046
9047 // Move the in-memory index to a backup pointer
9048 backup_list_head = map_index_head;
9049 map_index_head = NULL;
9050
9051 // // Set the timestamp to 0 so that everything gets indexed
9052 // map_index_timestamp = (time_t)0l;
9053
9054 check_times = 0;
9055 }
9056
9057
9058 // Set the "accessed" field to zero for every record in the
9059 // index. Note that the list could be empty at this point.
9060 current = map_index_head;
9061 while (current != NULL)
9062 {
9063 current->accessed = 0;
9064 current = current->next;
9065 }
9066
9067
9068 if (check_times)
9069 {
9070 if (debug_level & 16)
9071 {
9072 fprintf(stderr,"map_indexer: Calling map_search\n");
9073 }
9074
9075 map_search (NULL, AUTO_MAP_DIR, NULL, NULL, (int)FALSE, INDEX_CHECK_TIMESTAMPS);
9076
9077 if (debug_level & 16)
9078 {
9079 fprintf(stderr,"map_indexer: Returned from map_search\n");
9080 }
9081 }
9082 else
9083 {
9084 if (debug_level & 16)
9085 {
9086 fprintf(stderr,"map_indexer: Calling map_search\n");
9087 }
9088
9089 map_search (NULL, AUTO_MAP_DIR, NULL, NULL, (int)FALSE, INDEX_NO_TIMESTAMPS);
9090
9091 if (debug_level & 16)
9092 {
9093 fprintf(stderr,"map_indexer: Returned from map_search\n");
9094 }
9095 }
9096
9097 if (debug_level & 16)
9098 {
9099 fprintf(stderr,"map_indexer() middle\n");
9100 }
9101
9102
9103 if (parameter == 1) // Full indexing instead of timestamp-check indexing
9104 {
9105 // Copy the Properties from the backup list to the new list,
9106 // then free the backup list.
9107 map_index_copy_properties(map_index_head, backup_list_head);
9108 }
9109
9110
9111 // Save the updated index to the file
9112 index_save_to_file();
9113
9114 fprintf(stderr,"Finished indexing maps\n");
9115
9116 if (debug_level & 16)
9117 {
9118 fprintf(stderr,"map_indexer() end\n");
9119 }
9120 }
9121
9122
9123
9124
9125
9126 /* moved these here and made them static so it will function on FREEBSD */
9127 #define MAX_ALERT 7000
9128 // If we comment this out, we link, but get a segfault at runtime.
9129 // Take out the "static" and we get a segfault when we zoom out too
9130 // far with the lakes or counties shapefile loaded. No idea why
9131 // yet. --we7u
9132 //static alert_entry alert[MAX_ALERT];
9133 static int alert_count;
9134
9135
9136
9137
9138
9139 /*******************************************************************
9140 * fill_in_new_alert_entries()
9141 *
9142 * Fills in the index and filename portions of any alert entries
9143 * that are missing them. This function should be called at the
9144 * point where we've just received a new weather alert.
9145 *
9146
9147 //WE7U
9148 // Later we should change this so that it doesn't scan the entire
9149 // message list, but is passed the important info directly from the
9150 // decode routines in db.c, and the message should NOT be added to
9151 // the message list.
9152 //WE7U
9153
9154 *
9155 * This function is designed to use ESRI Shapefile map files. The
9156 * base directory where the Shapefiles are located is passed to us
9157 * in the "dir" variable.
9158 *
9159 * map_search() fills in the filename field of the alert struct.
9160 * draw_shapefile_map() fills in the index field.
9161 *******************************************************************/
fill_in_new_alert_entries()9162 void fill_in_new_alert_entries()
9163 {
9164 // int ii;
9165 char alert_scan[MAX_FILENAME];
9166 // char *dir_ptr;
9167 struct hashtable_itr *iterator = NULL;
9168 alert_entry *temp;
9169 char dir[MAX_FILENAME];
9170
9171
9172 if (debug_level & 2)
9173 {
9174 fprintf(stderr,"fill_in_new_alert_entries start\n");
9175 }
9176
9177 xastir_snprintf(dir,
9178 sizeof(dir),
9179 "%s",
9180 ALERT_MAP_DIR);
9181
9182 alert_count = MAX_ALERT - 1;
9183
9184 // Set up our path to the wx alert maps
9185 memset(alert_scan, 0, sizeof (alert_scan)); // Zero our alert_scan string
9186 xastir_snprintf(alert_scan, // Fetch the base directory
9187 sizeof(alert_scan),
9188 "%s",
9189 dir);
9190 strncat(alert_scan, // Complete alert directory is now set up in the string
9191 "/",
9192 sizeof(alert_scan) - 1 - strlen(alert_scan));
9193 // dir_ptr = &alert_scan[strlen (alert_scan)]; // Point to end of path
9194
9195 // Iterate through the weather alerts. It looks like we wish to
9196 // just fill in the alert struct and to determine whether the
9197 // alert is within our viewport here. We don't wish to draw the
9198 // alerts at this stage, that happens in the load_alert_maps()
9199 // function below.
9200
9201 iterator = create_wx_alert_iterator();
9202 temp = get_next_wx_alert(iterator);
9203 while (iterator != NULL && temp)
9204 {
9205
9206 if (!temp->filename[0]) // Filename is
9207 {
9208 // empty, we need to fill it in.
9209
9210 // fprintf(stderr,"fill_in_new_alert_entries() Title: %s\n",temp->title);
9211
9212 // The last parameter denotes loading into
9213 // pixmap_alerts instead of pixmap or pixmap_final.
9214 // Note that just calling map_search does not get
9215 // the alert areas drawn on the screen. The
9216 // draw_map() function called by map_search just
9217 // fills in the filename field in the struct and
9218 // exits.
9219 //
9220 // The "warn" parameter (next to last) specifies whether
9221 // to dump warnings out to the console as well. If the
9222 // warning was received on local RF or locally, warn the
9223 // operator (the weather must be near).
9224 map_search (da,
9225 alert_scan,
9226 temp,
9227 &alert_count,
9228 (int)temp->flags[source],
9229 DRAW_TO_PIXMAP_ALERTS);
9230
9231 // fprintf(stderr,"fill_in_new_alert_entries() Title1:%s\n",temp->title);
9232 }
9233 temp = get_next_wx_alert(iterator);
9234 }
9235 #ifndef USING_LIBGC
9236 //fprintf(stderr,"free iterator 4\n");
9237 if (iterator)
9238 {
9239 free(iterator);
9240 }
9241 #endif // USING_LIBGC
9242
9243 if (debug_level & 2)
9244 {
9245 fprintf(stderr,"fill_in_new_alert_entries end\n");
9246 }
9247 }
9248
9249
9250
9251
9252 /*******************************************************************
9253 * load_alert_maps()
9254 *
9255 * Used to load weather alert maps, based on NWS weather alerts that
9256 * are received. Called from create_image() and refresh_image().
9257 * This function is designed to use ESRI Shapefile map files. The
9258 * base directory where the Shapefiles are located is passed to us
9259 * in the "dir" variable.
9260 *
9261 * map_search() fills in the filename field of the alert struct.
9262 * draw_shapefile_map() fills in the index field.
9263 *******************************************************************/
load_alert_maps(Widget w,char * dir)9264 void load_alert_maps (Widget w, char *dir)
9265 {
9266 // int ii;
9267 int level;
9268 unsigned char fill_color[] = { (unsigned char)0x69, // gray86
9269 (unsigned char)0x4a, // red2
9270 (unsigned char)0x63, // yellow2
9271 (unsigned char)0x66, // cyan2
9272 (unsigned char)0x61, // RoyalBlue
9273 (unsigned char)0x64, // ForestGreen
9274 (unsigned char)0x62
9275 }; // orange3
9276
9277 struct hashtable_itr *iterator = NULL;
9278 alert_entry *temp;
9279 map_draw_flags mdf;
9280
9281
9282 //fprintf(stderr,"load_alert_maps\n");
9283
9284 // TODO:
9285 // Figure out how to pass a quantity of zones off to the map drawing
9286 // routines, then we can draw them all with one pass through each
9287 // map file. Alphanumerically sort the zones to make it easier for
9288 // the map drawing functions? Note that the indexing routines fill
9289 // in both the filename and the shapefile index for each record.
9290 //
9291 // Alternative: Call map_draw for each filename listed and have the
9292 // draw_shapefile function iterate through the array looking for all
9293 // filename matches, pulling non-negative indexes out of each index
9294 // field for matches and drawing them. That should be fast and
9295 // require no sorting of the array. Downside: The alerts won't be
9296 // layered based on alert level unless we modify the above: Drawing
9297 // each file once for each alert-level in the proper layering order.
9298 // Perhaps we could keep a list of which filenames have been called,
9299 // and only call each one once per load_alert_maps() call.
9300
9301
9302 // Just for a test
9303 //draw_shapefile_map (w, dir, filenm, alert, alert_color, destination_pixmap);
9304 //draw_shapefile_map (w, dir, "c_16my01.shp", NULL, '\0', DRAW_TO_PIXMAP_ALERTS);
9305
9306
9307 // Are we drawing them in reverse order so that the important
9308 // alerts end up drawn on top of the less important alerts?
9309 // Actually, since the alert hash isn't ordered, perhaps we need to
9310 // order them by priority, then by map file, so that we can draw the
9311 // shapes from each map file in the correct order. This might cause
9312 // each map file to be drawn up to three times (once for each
9313 // priority level), but that's better than calling each map for each
9314 // zone as is done now.
9315
9316 iterator = create_wx_alert_iterator();
9317 temp = get_next_wx_alert(iterator);
9318 while (iterator != NULL && temp)
9319 {
9320
9321 HandlePendingEvents(app_context);
9322 if (interrupt_drawing_now)
9323 {
9324 #ifndef USING_LIBGC
9325 //fprintf(stderr,"free iterator 5\n");
9326 if (iterator)
9327 {
9328 free(iterator);
9329 }
9330 #endif // USING_LIBGC
9331 return;
9332 }
9333
9334 if (disable_all_maps)
9335 {
9336 #ifndef USING_LIBGC
9337 //fprintf(stderr,"free iterator 6\n");
9338 if (iterator)
9339 {
9340 free(iterator);
9341 }
9342 #endif // USING_LIBGC
9343 return;
9344 }
9345
9346 // Check whether the alert slot is filled/empty
9347 if (temp->title[0] == '\0') // Empty slot
9348 {
9349 temp = get_next_wx_alert(iterator);
9350 continue;
9351 }
9352
9353 if ( (level = alert_active(temp, ALERT_ALL) ) )
9354 {
9355 if (level >= (int)sizeof (fill_color))
9356 {
9357 level = 0;
9358 }
9359
9360 // The last parameter denotes drawing into pixmap_alert
9361 // instead of pixmap or pixmap_final.
9362
9363 if (debug_level & 16)
9364 {
9365 fprintf(stderr,"load_alert_maps() Drawing %s\n",temp->filename);
9366 fprintf(stderr,"load_alert_maps() Title4:%s\n",temp->title);
9367 }
9368
9369 // Attempt to draw alert
9370 if ( temp->index != -1 ) // Shape found in shapefile
9371 {
9372
9373 // Check whether we've ever tried to draw this alert
9374 // before. If not, attempt it and get the boundary
9375 // limits filled in.
9376 //
9377 if ( temp->bottom_boundary == 0.0
9378 && temp->top_boundary == 0.0
9379 && temp->left_boundary == 0.0
9380 && temp->right_boundary == 0.0)
9381 {
9382
9383 if (temp->alert_level != 'C')
9384 {
9385 draw_map (w, dir, temp->filename, temp,
9386 fill_color[level], DRAW_TO_PIXMAP_ALERTS, &mdf); // draw filled
9387 }
9388 }
9389
9390 if (map_visible_lat_lon(temp->bottom_boundary, // Shape visible
9391 temp->top_boundary,
9392 temp->left_boundary,
9393 temp->right_boundary) )
9394 {
9395
9396 if (temp->alert_level != 'C') // Alert not cancelled
9397 {
9398 mdf.draw_filled=1;
9399 mdf.usgs_drg=0;
9400
9401 if (debug_level & 16)
9402 {
9403 fprintf(stderr,"load_alert_maps: Calling draw_map\n");
9404 }
9405
9406 draw_map (w, dir, temp->filename, temp,
9407 fill_color[level], DRAW_TO_PIXMAP_ALERTS, &mdf); // draw filled
9408 }
9409 if (temp)
9410 {
9411 temp->flags[on_screen] = 'Y';
9412 }
9413 }
9414 else
9415 {
9416 // Not in our viewport, don't draw it!
9417 if (debug_level & 16)
9418 {
9419 fprintf(stderr,"load_alert_maps() Alert not visible\n");
9420 }
9421 //fprintf(stderr,"B:%f T:%f L:%f R:%f\n", temp->bottom_boundary, temp->top_boundary, temp->left_boundary, temp->right_boundary);
9422 if (temp)
9423 {
9424 temp->flags[on_screen] = 'N';
9425 }
9426 }
9427 }
9428 else
9429 {
9430 // Can't find this shape in the shapefile.
9431 if (debug_level & 16)
9432 {
9433 fprintf(stderr,
9434 "load_alert_maps() Shape %s, strlen=%d, not found in %s\n",
9435 temp->title,
9436 (int)strlen(temp->title),
9437 temp->filename );
9438 }
9439 }
9440 }
9441 temp = get_next_wx_alert(iterator);
9442 }
9443 #ifndef USING_LIBGC
9444 //fprintf(stderr,"free iterator 7\n");
9445 if (iterator)
9446 {
9447 free(iterator);
9448 }
9449 #endif // USING_LIBGC
9450
9451 if (debug_level & 16)
9452 {
9453 fprintf(stderr,"load_alert_maps() Done drawing all active alerts\n");
9454 }
9455
9456 if (alert_display_request())
9457 {
9458 alert_redraw_on_update = redraw_on_new_data = 2;
9459 }
9460 }
9461
9462
9463
9464
9465
9466 // Here's the head of our sorted-by-layer maps list
9467 static map_index_record *map_sorted_list_head = NULL;
9468
9469
empty_map_sorted_list(void)9470 static void empty_map_sorted_list(void)
9471 {
9472 map_index_record *current = map_sorted_list_head;
9473
9474 while (map_sorted_list_head != NULL)
9475 {
9476 current = map_sorted_list_head;
9477 map_sorted_list_head = current->next;
9478 if (current->XmStringPtr != NULL)
9479 {
9480 XmStringFree(current->XmStringPtr);
9481 }
9482 free(current);
9483 }
9484 }
9485
9486
9487
9488
9489
9490 // Insert a map into the list at the end of the maps with the same
9491 // layer number. We'll need to look up the parameters for it from
9492 // the master map_index list and then attach a new record to our new
9493 // sorted list in the proper place.
9494 //
9495 // This function should be called when we're first starting up
9496 // Xastir and anytime that selected_maps.sys is changed.
9497 //
insert_map_sorted(char * filename)9498 static void insert_map_sorted(char *filename)
9499 {
9500 map_index_record *current;
9501 map_index_record *last;
9502 map_index_record *temp_record;
9503 unsigned long bottom;
9504 unsigned long top;
9505 unsigned long left;
9506 unsigned long right;
9507 int max_zoom;
9508 int min_zoom;
9509 int map_layer;
9510 int draw_filled;
9511 int usgs_drg;
9512 int auto_maps;
9513 int done;
9514
9515
9516 if (index_retrieve(filename,
9517 &bottom,
9518 &top,
9519 &left,
9520 &right,
9521 &max_zoom,
9522 &min_zoom,
9523 &map_layer,
9524 &draw_filled,
9525 &usgs_drg,
9526 &auto_maps)) // Found a match
9527 {
9528
9529 // Allocate a new record
9530 temp_record = (map_index_record *)malloc(sizeof(map_index_record));
9531 CHECKMALLOC(temp_record);
9532
9533 // Fill in the values
9534 xastir_snprintf(temp_record->filename,MAX_FILENAME,"%s",filename);
9535 temp_record->bottom = bottom;
9536 temp_record->top = top;
9537 temp_record->left = left;
9538 temp_record->right = right;
9539 temp_record->max_zoom = max_zoom;
9540 temp_record->min_zoom = min_zoom;
9541 temp_record->map_layer = map_layer;
9542 temp_record->draw_filled = draw_filled;
9543 temp_record->usgs_drg = usgs_drg;
9544 temp_record->auto_maps = auto_maps;
9545 temp_record->selected = 1; // Always, we already know this!
9546 temp_record->accessed = 0;
9547 temp_record->next = NULL;
9548 temp_record->XmStringPtr = NULL;
9549
9550 // Now find the proper place for it and insert it in
9551 // layer-order into the list.
9552 current = map_sorted_list_head;
9553 last = map_sorted_list_head;
9554 done = 0;
9555
9556 // Possible cases:
9557 // Empty list
9558 // insert at beginning of list
9559 // insert at end of list
9560 // insert between other entries
9561
9562 if (map_sorted_list_head == NULL)
9563 {
9564 // Empty list. Insert record.
9565 map_sorted_list_head = temp_record;
9566 done++;
9567 }
9568 else if (map_layer < current->map_layer)
9569 {
9570 // Insert at beginning of list
9571 temp_record->next = current;
9572 map_sorted_list_head = temp_record;
9573 done++;
9574 }
9575 else // Need to insert between records or at end of list
9576 {
9577 while (!done && (current != NULL) )
9578 {
9579 if (map_layer >= current->map_layer) // Not to our layer yet
9580 {
9581 last = current;
9582 current = current->next; // May point to NULL now
9583 }
9584 else if (map_layer < current->map_layer)
9585 {
9586 temp_record->next = current;
9587 last->next = temp_record;
9588 done++;
9589 }
9590 }
9591 }
9592 // Handle running off the end of the list
9593 if (!done && (current == NULL) )
9594 {
9595 last->next = temp_record;
9596 }
9597 }
9598 else
9599 {
9600 // We failed to find it in the map index
9601 }
9602 }
9603
9604
9605
9606
9607
9608 /**********************************************************
9609 * load_auto_maps()
9610 *
9611 * NEW: Uses the in-memory map_index to scan through the
9612 * maps.
9613 *
9614 * OLD: Recurses through the map directories looking for
9615 * maps to load.
9616 **********************************************************/
load_auto_maps(Widget w,char * UNUSED (dir))9617 void load_auto_maps (Widget w, char * UNUSED(dir) )
9618 {
9619 map_index_record *current = map_index_head;
9620 map_draw_flags mdf;
9621
9622 HandlePendingEvents(app_context);
9623 if (interrupt_drawing_now)
9624 {
9625 return;
9626 }
9627
9628 // Skip the sorting of the maps if we don't need to do it
9629 if (re_sort_maps)
9630 {
9631
9632 //fprintf(stderr,"*** Sorting the selected maps by layer...\n");
9633
9634 // Empty the sorted list first. We'll create a new one.
9635 empty_map_sorted_list();
9636
9637 // Run through the entire map_index linked list
9638 while (current != NULL)
9639 {
9640 if (auto_maps_skip_raster
9641 && ( strstr(current->filename,".geo")
9642 || strstr(current->filename,".GEO")
9643 || strstr(current->filename,".Geo")
9644 || strstr(current->filename,".tif")
9645 || strstr(current->filename,".TIF")
9646 || strstr(current->filename,".Tif")))
9647 {
9648 // Skip this map
9649 }
9650 else // Draw this map
9651 {
9652
9653 //fprintf(stderr,"Loading: %s/%s\n",SELECTED_MAP_DIR,current->filename);
9654
9655 //WE7U
9656 insert_map_sorted(current->filename);
9657
9658 /*
9659 draw_map (w,
9660 SELECTED_MAP_DIR,
9661 current->filename,
9662 NULL,
9663 '\0',
9664 DRAW_TO_PIXMAP);
9665 */
9666 }
9667 current = current->next;
9668 }
9669
9670 // All done sorting until something is changed in the Map
9671 // Chooser.
9672 re_sort_maps = 0;
9673
9674 //fprintf(stderr,"*** DONE sorting the selected maps.\n");
9675 }
9676
9677 // We have the maps in sorted order. Run through the list and
9678 // draw them. Only include those that have the auto_maps field
9679 // set to 1.
9680 current = map_sorted_list_head;
9681 while (current != NULL)
9682 {
9683
9684 HandlePendingEvents(app_context);
9685 if (interrupt_drawing_now)
9686 {
9687 // Update to screen
9688 (void)XCopyArea(XtDisplay(da),
9689 pixmap,
9690 XtWindow(da),
9691 gc,
9692 0,
9693 0,
9694 (unsigned int)screen_width,
9695 (unsigned int)screen_height,
9696 0,
9697 0);
9698 return;
9699 }
9700
9701 if (disable_all_maps)
9702 {
9703 // Update to screen
9704 (void)XCopyArea(XtDisplay(da),
9705 pixmap,
9706 XtWindow(da),
9707 gc,
9708 0,
9709 0,
9710 (unsigned int)screen_width,
9711 (unsigned int)screen_height,
9712 0,
9713 0);
9714 return;
9715 }
9716
9717 // Debug
9718 // fprintf(stderr,"Drawing level:%05d, file:%s\n",
9719 // current->map_layer,
9720 // current->filename);
9721
9722 // Draw the maps in sorted-by-layer order
9723 if (current->auto_maps)
9724 {
9725
9726 mdf.draw_filled = current->draw_filled;
9727 mdf.usgs_drg = current->usgs_drg;
9728
9729 if (debug_level & 16)
9730 {
9731 fprintf(stderr,"load_auto_maps: Calling draw_map\n");
9732 }
9733
9734 draw_map (w,
9735 SELECTED_MAP_DIR,
9736 current->filename,
9737 NULL,
9738 '\0',
9739 DRAW_TO_PIXMAP,
9740 &mdf);
9741 }
9742
9743 current = current->next;
9744 }
9745 }
9746
9747
9748
9749
9750
9751 /*******************************************************************
9752 * load_maps()
9753 *
9754 * Loads maps, draws grid, updates the display.
9755 *
9756 * We now create a linked list of maps in layer-order and use this
9757 * list to draw the maps. This preserves the correct ordering in
9758 * all cases. The layer to draw each map is specified in the
9759 * map_index.sys file (fifth parameter). Eventually code will be
9760 * added to the Map Chooser in order to change the layer each map is
9761 * drawn at.
9762 *******************************************************************/
load_maps(Widget w)9763 void load_maps (Widget w)
9764 {
9765 FILE *f;
9766 char mapname[MAX_FILENAME];
9767 int i;
9768 char selected_dir[MAX_FILENAME];
9769 map_index_record *current;
9770 map_draw_flags mdf;
9771 char selected_map_path[MAX_VALUE];
9772
9773 get_user_base_dir(SELECTED_MAP_DATA, selected_map_path, sizeof(selected_map_path));
9774
9775 // int dummy;
9776
9777
9778 if (debug_level & 16)
9779 {
9780 fprintf(stderr,"Load maps start\n");
9781 }
9782
9783 HandlePendingEvents(app_context);
9784 if (interrupt_drawing_now)
9785 {
9786 // Update to screen
9787 (void)XCopyArea(XtDisplay(da),
9788 pixmap,
9789 XtWindow(da),
9790 gc,
9791 0,
9792 0,
9793 (unsigned int)screen_width,
9794 (unsigned int)screen_height,
9795 0,
9796 0);
9797 return;
9798 }
9799
9800 // Skip the sorting of the maps if we don't need to do it
9801 if (re_sort_maps)
9802 {
9803
9804 //fprintf(stderr,"*** Sorting the selected maps by layer...\n");
9805
9806 // Empty the sorted list first. We'll create a new one.
9807 empty_map_sorted_list();
9808
9809 // Make sure the string is empty before we start
9810 selected_dir[0] = '\0';
9811
9812 // Create empty file if it doesn't exist
9813 (void)filecreate( selected_map_path );
9814
9815 f = fopen ( selected_map_path, "r" );
9816 if (f != NULL)
9817 {
9818 if (debug_level & 16)
9819 {
9820 fprintf(stderr,"Load maps Open map file\n");
9821 }
9822
9823 while (!feof (f))
9824 {
9825
9826 // Grab one line from the file
9827 if ( fgets( mapname, MAX_FILENAME-1, f ) != NULL )
9828 {
9829
9830 // Forced termination (just in case)
9831 mapname[MAX_FILENAME-1] = '\0';
9832
9833 // Get rid of the newline at the end
9834 for (i = strlen(mapname); i > 0; i--)
9835 {
9836 if (mapname[i] == '\n')
9837 {
9838 mapname[i] = '\0';
9839 }
9840 }
9841
9842 if (debug_level & 16)
9843 {
9844 fprintf(stderr,"Found mapname: %s\n", mapname);
9845 }
9846
9847 // Test for comment
9848 if (mapname[0] != '#')
9849 {
9850
9851
9852 // Check whether it's a directory that was
9853 // selected. If so, save it in a special
9854 // variable and use that to match all the files
9855 // inside the directory. Note that with the way
9856 // we have things ordered in the list, the
9857 // directories appear before their member files.
9858 if (mapname[strlen(mapname)-1] == '/')
9859 {
9860 int len;
9861
9862 // Found a directory. Save the name.
9863 xastir_snprintf(selected_dir,
9864 sizeof(selected_dir),
9865 "%s",
9866 mapname);
9867
9868 len = strlen(mapname);
9869
9870 //fprintf(stderr,"Selected %s directory\n",selected_dir);
9871
9872 // Here we need to run through the map_index
9873 // list to find all maps that match the
9874 // currently selected directory. Attempt to
9875 // load all of those maps as well.
9876
9877 //fprintf(stderr,"Load all maps under this directory: %s\n",selected_dir);
9878
9879 // Point to the start of the map_index list
9880 current = map_index_head;
9881
9882 while (current != NULL)
9883 {
9884
9885 if (strncmp(current->filename,selected_dir,len) == 0)
9886 {
9887
9888 if (current->filename[strlen(current->filename)-1] != '/')
9889 {
9890
9891 //fprintf(stderr,"Loading: %s\n",current->filename);
9892
9893 //WE7U
9894 insert_map_sorted(current->filename);
9895
9896 /*
9897 draw_map (w,
9898 SELECTED_MAP_DIR,
9899 current->filename,
9900 NULL,
9901 '\0',
9902 DRAW_TO_PIXMAP);
9903 */
9904
9905 }
9906 }
9907 current = current->next;
9908 }
9909 }
9910 // Else must be a regular map file
9911 else
9912 {
9913 //fprintf(stderr,"%s\n",mapname);
9914 //start_timer();
9915
9916 //WE7U
9917 insert_map_sorted(mapname);
9918
9919 /*
9920 draw_map (w,
9921 SELECTED_MAP_DIR,
9922 mapname,
9923 NULL,
9924 '\0',
9925 DRAW_TO_PIXMAP);
9926 */
9927
9928 //stop_timer();
9929 //print_timer_results();
9930
9931 if (debug_level & 16)
9932 {
9933 fprintf(stderr,"Load maps -%s\n", mapname);
9934 }
9935
9936 XmUpdateDisplay (da);
9937 }
9938 }
9939 }
9940 else // We've hit EOF
9941 {
9942 break;
9943 }
9944
9945 }
9946 (void)fclose (f);
9947 statusline(" ",1); // delete status line
9948 }
9949 else
9950 {
9951 fprintf(stderr,"Couldn't open file: %s\n", selected_map_path );
9952 }
9953
9954 // All done sorting until something is changed in the Map
9955 // Chooser.
9956 re_sort_maps = 0;
9957
9958 //fprintf(stderr,"*** DONE sorting the selected maps.\n");
9959 }
9960
9961
9962 // We have the maps in sorted order. Run through the list and
9963 // draw them.
9964 current = map_sorted_list_head;
9965 while (current != NULL)
9966 {
9967
9968 HandlePendingEvents(app_context);
9969 if (interrupt_drawing_now)
9970 {
9971 statusline(" ",1); // delete status line
9972 // Update to screen
9973 (void)XCopyArea(XtDisplay(da),
9974 pixmap,
9975 XtWindow(da),
9976 gc,
9977 0,
9978 0,
9979 (unsigned int)screen_width,
9980 (unsigned int)screen_height,
9981 0,
9982 0);
9983 return;
9984 }
9985
9986 if (disable_all_maps)
9987 {
9988 // Update to screen
9989 (void)XCopyArea(XtDisplay(da),
9990 pixmap,
9991 XtWindow(da),
9992 gc,
9993 0,
9994 0,
9995 (unsigned int)screen_width,
9996 (unsigned int)screen_height,
9997 0,
9998 0);
9999 return;
10000 }
10001
10002 // Debug
10003 // fprintf(stderr,"Drawing level:%05d, file:%s\n",
10004 // current->map_layer,
10005 // current->filename);
10006
10007 // Draw the maps in sorted-by-layer order
10008 mdf.draw_filled = current->draw_filled;
10009 mdf.usgs_drg = current->usgs_drg;
10010
10011 if (debug_level & 16)
10012 {
10013 fprintf(stderr,"load_maps: Calling draw_map\n");
10014 }
10015
10016 // Map profiling, set up for 800x600 window at "Map Profile Test
10017 // Site" bookmark.
10018 //
10019 // Loading "rd011802.shp" 500 times takes
10020 // 302->256->264->269->115->116 seconds.
10021 //
10022 // 100 times on PP200 takes 192->183 seconds.
10023 //
10024 //start_timer();
10025 //fprintf(stderr,"Calling draw_map() 500 times...\n");
10026 //for (dummy = 0; dummy < 500; dummy++) {
10027 draw_map (w,
10028 SELECTED_MAP_DIR,
10029 current->filename,
10030 NULL,
10031 '\0',
10032 DRAW_TO_PIXMAP,
10033 &mdf);
10034 //}
10035 //stop_timer(); print_timer_results();
10036
10037 current = current->next;
10038 }
10039
10040 if (debug_level & 16)
10041 {
10042 fprintf(stderr,"Load maps stop\n");
10043 }
10044 }
10045
10046
10047