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  * Please see separate copyright notice attached to the
25  * shape_ring_direction() function in this file.
26  *
27  * DBFAWK TODO:
28  *  - reload .dbfawk's when they've changed (or maps are reindexed)
29  *  - scale line widths based on zoom level (see city_flag, for example)
30  *  - allow multiple font sizes (font_size=small|medium|large|huge)
31  *  - allow multiple font faces?
32  *  - allow setting of map layer for individual shapes
33  *  - transparency
34  *  - allow setting fill_style (solid, stippled, etc.)
35  *  - do more config/ *.dbfawk files!
36  *
37  */
38 #ifdef HAVE_CONFIG_H
39   #include "config.h"
40 #endif  // HAVE_CONFIG_H
41 
42 #include "snprintf.h"
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <sys/stat.h>
48 #include <ctype.h>
49 #include <sys/types.h>
50 #include <pwd.h>
51 #include <errno.h>
52 
53 // Needed for Solaris
54 #ifdef HAVE_STRINGS_H
55   #include <strings.h>
56 #endif  // HAVE_STRINGS_H
57 
58 #include <dirent.h>
59 #include <netinet/in.h>
60 #include <Xm/XmAll.h>
61 
62 #ifdef HAVE_X11_XPM_H
63   #include <X11/xpm.h>
64   #ifdef HAVE_LIBXPM // if we have both, prefer the extra library
65     #undef HAVE_XM_XPMI_H
66   #endif // HAVE_LIBXPM
67 #endif // HAVE_X11_XPM_H
68 
69 #ifdef HAVE_XM_XPMI_H
70   #include <Xm/XpmI.h>
71 #endif // HAVE_XM_XPMI_H
72 
73 #include <X11/Xlib.h>
74 
75 #include <math.h>
76 
77 #include "xastir.h"
78 #include "maps.h"
79 #include "alert.h"
80 #include "util.h"
81 #include "main.h"
82 #include "datum.h"
83 #include "draw_symbols.h"
84 #include "rotated.h"
85 #include "color.h"
86 #include "xa_config.h"
87 
88 #define CHECKMALLOC(m)  if (!m) { fprintf(stderr, "***** Malloc Failed *****\n"); exit(0); }
89 #define CHECKREALLOC(m)  if (!m) { fprintf(stderr, "***** Realloc Failed *****\n"); exit(0); }
90 
91 #ifdef HAVE_LIBSHP
92 #ifdef HAVE_LIBPCRE
93   #include "awk.h"
94   #include "dbfawk.h"
95 #endif /* HAVE_LIBPCRE */
96 #ifdef HAVE_SHAPEFIL_H
97   #include <shapefil.h>
98 #else   // HAVE_SHAPEFIL_H
99   #ifdef HAVE_LIBSHP_SHAPEFIL_H
100     #include <libshp/shapefil.h>
101   #else   // HAVE_LIBSHP_SHAPEFIL_H
102     #error HAVE_LIBSHP defined but no corresponding include defined
103   #endif // HAVE_LIBSHP_SHAPEFIL_H
104 #endif // HAVE_SHAPEFIL_H
105 
106 extern int npoints;        /* tsk tsk tsk -- globals */
107 
108 #include <rtree/index.h>
109 #include "shp_hash.h"
110 
111 // Must be last include file
112 #include "leak_detection.h"
113 
114 
115 
116 static int *RTree_hitarray=NULL;
117 int RTree_hitarray_size=0;
118 int RTree_hitarray_index=0;
119 
120 
121 //This trivial routine is used by the RTreeSearch as a callback when it finds
122 // a match.
RTreeSearchCallback(int id,void * UNUSED (arg))123 int RTreeSearchCallback(int id, void* UNUSED(arg) )
124 {
125   if (!RTree_hitarray)
126   {
127     RTree_hitarray = (int *)malloc(1000*sizeof(int));
128     RTree_hitarray_size=1000;
129   }
130   if (RTree_hitarray_size <= RTree_hitarray_index)
131   {
132     int *ptr;
133     ptr = realloc(RTree_hitarray, (RTree_hitarray_size+1000)*sizeof(int));
134     CHECKREALLOC(ptr);  // fatal error if we can't get 'em :-(
135     RTree_hitarray=ptr;
136     RTree_hitarray_size += 1000;
137     //fprintf(stderr,"Hitarray now at %d\n",RTree_hitarray_size);
138   }
139 
140 
141   RTree_hitarray[RTree_hitarray_index++]=id-1;
142   return 1;  // signal to keep searching for more matches
143 }
144 
145 
146 
147 
148 
149 /*******************************************************************
150  * create_shapefile_map()
151  *
152  * Do we have a need for storing date/ time/ speed/ course/
153  * altitude/ heard-direct for each point?  Altitude could be stored
154  * in the Z space.  If we store a station's trail as an SHPT_POINT
155  * file, then we can associate a bunch of info with each point:
156  * date/time, altitude, course, speed, status/comments, path, port,
157  * heard-direct, weather data, etc.  We could also dynamically
158  * change the number of fields based on what we have a need to
159  * store, using field-names to determine what's stored in each file.
160  *
161  * shapefile_name is the path/name of the map file we wish to create
162  * (without the extension).
163  *
164  * type = SHPT_POINT, SHPT_ARC, SHPT_POLYGON,
165  * SHPT_MULTIPOINT, etc.
166  *
167  * quantity equals the number of vertices we have.
168  *
169  * padfx/padfy/padfz are the vertices themselves, in double format.
170  *******************************************************************/
create_shapefile_map(char * dir,char * shapefile_name,int type,int quantity,double * padfx,double * padfy,double * padfz,int add_timestamp,char * shape_label)171 void create_shapefile_map(char *dir, char *shapefile_name, int type,
172                           int quantity, double *padfx, double *padfy, double *padfz,
173                           int add_timestamp, char * shape_label)
174 {
175 
176   SHPHandle my_shp_handle;
177   SHPObject *my_object;
178   DBFHandle my_dbf_handle;
179   char timedatestring[101];
180   int index;
181   int max_objects = 1;
182   char credit_string[] = "Created by Xastir, http://www.xastir.org";
183   char temp_shapefile_name[MAX_FILENAME];
184   char temp_prj_name[MAX_FILENAME];
185 
186   if (debug_level & 16)
187   {
188     fprintf(stderr,"create_shapefile_map\n");
189     fprintf(stderr,"%s %s %d %d %d\n",
190             dir,
191             shapefile_name,
192             type,
193             quantity,
194             add_timestamp);
195   }
196 
197   if (quantity == 0)
198   {
199     // No reason to make a map if we don't have any points.
200     return;
201   }
202 
203   // Get the time/datestamp
204   get_timestamp(timedatestring);
205 
206   if (add_timestamp)      // Prepend a timestamp to the filename
207   {
208     int ii;
209 
210     xastir_snprintf(temp_shapefile_name,
211                     sizeof(temp_shapefile_name),
212                     "%s%s_%s",
213                     dir,
214                     timedatestring,
215                     shapefile_name);
216 
217     // Change spaces and colons to underlines
218     for (ii = 0; ii < (int)strlen(temp_shapefile_name); ii++)
219     {
220       if (temp_shapefile_name[ii] == ' ' ||
221           temp_shapefile_name[ii] == ':')
222       {
223         temp_shapefile_name[ii] = '_';
224       }
225 
226     }
227   }
228   else    // Use the filename directly, no timestamp
229   {
230     xastir_snprintf(temp_shapefile_name,
231                     sizeof(temp_shapefile_name),
232                     "%s%s",
233                     dir,
234                     shapefile_name);
235   }
236 
237   if (debug_level & 16)
238   {
239     fprintf(stderr, "Creating file %s\n", temp_shapefile_name);
240   }
241 
242   // Create empty .shp/.shx/.dbf files
243   //
244   my_shp_handle = SHPCreate(temp_shapefile_name, type);
245   my_dbf_handle = DBFCreate(temp_shapefile_name);
246 
247   // Check whether we were able to open these handles
248   if ((my_shp_handle == NULL) || (my_dbf_handle == NULL))
249   {
250     // Probably write-protected directory
251     fprintf(stderr, "Could not create shapefile %s\n",
252             temp_shapefile_name);
253     return;
254   }
255 
256   // Write out a WKT in a .prj file to go with this shapefile.
257   strcpy(temp_prj_name, temp_shapefile_name);
258   temp_prj_name[sizeof(temp_prj_name)-1] = '\0';  // Terminate string
259   strcat(temp_prj_name, ".prj");
260   temp_prj_name[sizeof(temp_prj_name)-1] = '\0';  // Terminate string
261 
262   xastirWriteWKT(temp_prj_name);
263 
264   // Create the different fields we'll use to store the
265   // attributes:
266   //
267   // Add a credits field and set the length.  Field 0.
268   DBFAddField(my_dbf_handle, "Credits", FTString, strlen(credit_string) + 1, 0);
269   //
270   // Add a date/time field and set the length.  Field 1.
271   DBFAddField(my_dbf_handle, "DateTime", FTString, strlen(timedatestring) + 1, 0);
272 
273   // Add a label field
274   DBFAddField(my_dbf_handle, "Label", FTString, strlen(shape_label) + 1, 0);
275 
276   // Note that if were passed additional parameters that went
277   // along with the lat/long/altitude points, we could write those
278   // into the DBF file in the loop below.  We would have to change
279   // from a polygon to a point filetype though.
280 
281   // Populate the files with objects and attributes.  Perform this
282   // loop once for each object.
283   //
284   for (index = 0; index < max_objects; index++)
285   {
286 
287     // Create a temporary object from the vertices
288     my_object = SHPCreateSimpleObject( type,
289                                        quantity,
290                                        padfx,
291                                        padfy,
292                                        padfz);
293 
294     // Write out the vertices
295     SHPWriteObject( my_shp_handle,
296                     -1,
297                     my_object);
298 
299     // Destroy the temporary object
300     SHPDestroyObject(my_object);
301 
302 // Note that with the current setup the below really only get
303 // written into the file once.  Check it with dbfinfo/dbfdump.
304 
305     // Write the credits attributes
306     DBFWriteStringAttribute( my_dbf_handle,
307                              index,
308                              0,
309                              credit_string);
310 
311     // Write the time/date string
312     DBFWriteStringAttribute( my_dbf_handle,
313                              index,
314                              1,
315                              timedatestring);
316 
317     // Write the label string
318     DBFWriteStringAttribute( my_dbf_handle,
319                              index,
320                              2,
321                              shape_label);
322   }
323 
324   // Close the .shp/.shx/.dbf files
325   SHPClose(my_shp_handle);
326   DBFClose(my_dbf_handle);
327 }
328 
329 
330 
331 
332 
333 // Function which creates a Shapefile map from an APRS trail.
334 //
335 // Navigate through the linked list of TrackRow structs to pick out
336 // the lat/long and write them into arrays of floats.  We then pass
337 // those arrays to create_shapefile_map().
338 //
create_map_from_trail(char * call_sign)339 void create_map_from_trail(char *call_sign)
340 {
341   DataRow *p_station;
342 
343 
344   // Find the station in our database.  Count the number of points
345   // for that station first, then allocate some arrays to hold
346   // that quantity of points.  Stuff the lat/long into the arrays
347   // and then call create_shapefile_map().
348   //
349   if (search_station_name(&p_station, call_sign, 1))
350   {
351     int count;
352     int ii;
353     TrackRow *ptr;
354     char temp[MAX_FILENAME];
355     char temp2[MAX_FILENAME];
356     double *x;
357     double *y;
358     double *z;
359     char temp_base_dir[MAX_VALUE];
360 
361     count = 0;
362     ptr = p_station->oldest_trackpoint;
363     while (ptr != NULL)
364     {
365       count++;
366       ptr = ptr->next;
367     }
368 
369 //fprintf(stderr, "Quantity of points: %d\n", count);
370 
371     if (count == 0)
372     {
373       // No reason to make a map if we don't have any points
374       // in the track list.
375       return;
376     }
377 
378     // We know how many points are in the linked list.  Allocate
379     // arrays to hold the values.
380     x = (double *) malloc( count * sizeof(double) );
381     y = (double *) malloc( count * sizeof(double) );
382     z = (double *) malloc( count * sizeof(double) );
383     CHECKMALLOC(x);
384     CHECKMALLOC(y);
385     CHECKMALLOC(z);
386 
387     // Fill in the values.  We need to convert from Xastir
388     // coordinate system to lat/long doubles as we go.
389     ptr = p_station->oldest_trackpoint;
390     ii = 0;
391     while ((ptr != NULL) && (ii < count) )
392     {
393 
394       // Convert from Xastir coordinates to lat/long
395 
396       // Convert to string
397       convert_lon_l2s(ptr->trail_long_pos, temp, sizeof(temp), CONVERT_DEC_DEG);
398       // Convert to double and stuff into array of doubles
399       if (1 != sscanf(temp, "%lf", &x[ii]))
400       {
401         fprintf(stderr,"create_map_from_trail:sscanf parsing error\n");
402       }
403       // If longitude string contains "W", make the final
404       // result negative.
405       if (index(temp, 'W'))
406       {
407         x[ii] = x[ii] * -1.0;
408       }
409 
410       // Convert to string
411       convert_lat_l2s(ptr->trail_lat_pos, temp, sizeof(temp), CONVERT_DEC_DEG);
412       // Convert to double and stuff into array of doubles
413       if (1 != sscanf(temp, "%lf", &y[ii]))
414       {
415         fprintf(stderr,"create_map_from_trail:sscanf parsing error\n");
416       }
417       // If latitude string contains "S", make the final
418       // result negative.
419       if (index(temp, 'S'))
420       {
421         y[ii] = y[ii] * -1.0;
422       }
423 
424       z[ii] = ptr->altitude;  // Altitude (meters), undefined=-99999
425 
426       ptr = ptr->next;
427       ii++;
428     }
429 
430     // Create a Shapefile from the APRS trail.  Write it into
431     // "~/.xastir/tracklogs" and add a date/timestamp to the end.
432     //
433     xastir_snprintf(temp, sizeof(temp),
434                     "%s/",
435                     get_user_base_dir("tracklogs", temp_base_dir, sizeof(temp_base_dir)));
436 
437     // Create filename
438     xastir_snprintf(temp2, sizeof(temp2),
439                     "%s%s",
440                     call_sign,
441                     "_APRS_Trail_Red");
442 
443     create_shapefile_map(
444       temp,
445       temp2,
446       SHPT_ARC,
447       count,
448       x,
449       y,
450       z,
451       1, // Add a timestamp to the front of the filename
452       call_sign);
453 
454     // Free the storage that we malloc'ed
455     free(x);
456     free(y);
457     free(z);
458   }
459   else    // Couldn't find the station of interest
460   {
461   }
462 }
463 
464 
465 
466 
467 
468 // Code borrowed from Shapelib which determines whether a ring is CW
469 // or CCW.  Thanks to Frank Warmerdam for permitting us to use this
470 // under the GPL license!  Per e-mail of 04/29/2003 between Frank
471 // and Curt, WE7U.  Frank gave permission for us to use _any_
472 // portion of Shapelib inside the GPL'ed Xastir program.
473 //
474 // Test Ring for Clockwise/Counter-Clockwise (fill or hole ring)
475 //
476 // Return  1  for Clockwise (fill ring)
477 // Return -1  for Counter-Clockwise (hole ring)
478 // Return  0  for error/indeterminate (shouldn't get this!)
479 //
shape_ring_direction(SHPObject * psObject,int Ring)480 int shape_ring_direction ( SHPObject *psObject, int Ring )
481 {
482   int nVertStart;
483   int nVertCount;
484   int iVert;
485   float dfSum;
486   int result;
487 
488 
489   nVertStart = psObject->panPartStart[Ring];
490 
491   if( Ring == psObject->nParts-1 )
492   {
493     nVertCount = psObject->nVertices - psObject->panPartStart[Ring];
494   }
495   else
496     nVertCount = psObject->panPartStart[Ring+1]
497                  - psObject->panPartStart[Ring];
498 
499   dfSum = 0.0;
500   for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ )
501   {
502     dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1]
503              - psObject->padfY[iVert] * psObject->padfX[iVert+1];
504   }
505 
506   dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart]
507            - psObject->padfY[iVert] * psObject->padfX[nVertStart];
508 
509   if (dfSum < 0.0)
510   {
511     result = 1;
512   }
513   else if (dfSum > 0.0)
514   {
515     result = -1;
516   }
517   else
518   {
519     result = 0;
520   }
521 
522   return(result);
523 }
524 
525 
526 
527 
528 
529 /**********************************************************
530  * draw_shapefile_map()
531  *
532  * This function handles both weather-alert shapefiles (from the
533  * NOAA site) and shapefiles used as maps (from a number of
534  * sources).
535  *
536  * If destination_pixmap equals INDEX_CHECK_TIMESTAMPS or
537  * INDEX_NO_TIMESTAMPS, then we are indexing the file (finding the
538  * extents) instead of drawing it.
539  *
540  * The current implementation can draw Polygon, PolyLine, and Point
541  * Shapefiles, but only from a few sources (NOAA, Mapshots.com, and
542  * ESRI/GeographyNetwork.com).  We don't handle some of the more
543  * esoteric formats.  We now handle the "hole" drawing in polygon
544  * shapefiles, where one direction around the ring means a fill, and
545  * the other direction means a hole in the polygon.
546  *
547  * Note that we must currently hard-code the file-recognition
548  * portion and the file-drawing portion, because every new source of
549  * Shapefiles has a different format, and the fields and field
550  * definitions can all change between them.
551  *
552  * If alert is NULL, draw every shape that fits the screen.  If
553  * non-NULL, draw only the shape that matches the zone number.
554  *
555  * Here's what I get for the County_Warning_Area Shapefile:
556  *
557  *
558  * Info for shapefiles/county_warning_areas/w_24ja01.shp
559  * 4 Columns,  121 Records in file
560  *            WFO          string  (3,0)
561  *            CWA          string  (3,0)
562  *            LON           float  (18,5)
563  *            LAT           float  (18,5)
564  * Info for shapefiles/county_warning_areas/w_24ja01.shp
565  * Polygon(5), 121 Records in file
566  * File Bounds: (              0,              0)
567  *              (    179.7880249,    71.39809418)
568  *
569  * From the NOAA web pages:
570 
571  Zone Alert Maps: (polygon) (such as z_16mr01.shp)
572  ----------------
573  field name type   width,dec description
574  STATE      character 2     [ss] State abbrev (US Postal Standard)
575  ZONE       character 3     [zzz] Zone number, from WSOM C-11
576  CWA        character 3     County Warning Area, from WSOM C-47
577  NAME       character 254   Zone name, from WSOM C-11
578  STATE_ZONE character 5     [sszzz] state+("00"+zone.trim).right(3))
579  TIME_ZONE  character 2     [tt] Time Zone, 2 chars if split
580  FE_AREA    character 2     [aa] Cardinal area of state (occasional)
581  LON        numeric   10,5  Longitude of centroid [decimal degrees]
582  LAT        numeric   9,5   Latitude of centroid [decimal degrees]
583 
584 
585  NOTE:  APRS weather alerts have these sorts of codes in them:
586     AL_C001
587     AUTAUGACOUNTY
588     MS_C075
589     LAUDERDALE&NEWTONCOUNTIES
590     KY_C009
591     EDMONSON
592     MS_CLARKE
593     MS_C113
594     MN_Z076
595     PIKECOUNTY
596     ATTALACOUNTY
597     ST.LUCIECOUNTY
598     CW_AGLD
599 
600  The strings in the shapefiles are mixed-case, and it appears that the NAME field would
601  have the county name, in case state_zone-number format was not used.  We can use the
602  STATE_ZONE filed for a match unless it is a non-standard form, in which case we'll need
603  to look through the NAME field, and perhaps chop off the "SS_" state portion first.
604 
605 
606  County Warning Areas: (polygon)
607  ---------------------
608  field name type   width,dec description
609  WFO        character 3     WFO Identifier (name of CWA)
610  CWA        character 3     CWA Identifier (same as WFO)
611  LON        numeric   10,5  Longitude of centroid [decimal degrees]
612  LAT        numeric   9,5   Latitude of centroid [decimal degrees]
613 
614  Coastal and Offshore Marine Areas: (polygon)
615  ----------------------------------
616  field name type   width,dec description
617  ID         character 6     Marine Zone Identifier
618  WFO        character 3     Assigned WFO (Office Identifier)
619  NAME       character 250   Name of Marine Zone
620  LON        numeric   10,5  Longitude of Centroid [decimal degrees]
621  LAT        numeric   9,5   Latitude of Centroid [decimal degrees]
622  WFO_AREA   character 200   "Official Area of Responsibility", from WSOM D51/D52
623 
624  Road Maps: (polyline)
625  ----------
626  field name type     width,dec  description
627  STFIPS     numeric     2,0     State FIPS Code
628  CTFIPS     numeric     3,0     County FIPS Code
629  MILES      numeric     6,2     length [mi]
630  KILOMETERS numeric     6,2     length [km]
631  TOLL       numeric     1,0
632  SURFACE    numeric     1,0     Surface type
633  LANES      numeric     2,0     Number of lanes
634  FEAT_CLASS numeric     2,0
635  CLASS      character   30
636  SIGN1      character   6       Primary Sign Route
637  SIGN2      character   6       Secondary Sign Route
638  SIGN3      character   6       Alternate Sign Route
639  DESCRIPT   character   35      Name of road (sparse)
640  SPEEDLIM   numeric     16,0
641  SECONDS    numeric     16,2
642 
643 Lakes (lk17de98.shp):
644 ---------------------
645 field name  type     width,dec  description
646 NAME        string      (40,0)
647 FEATURE     string      (40,0)
648 LON         float       (10,5)
649 LAT         float       (9,5)
650 
651 
652 USGS Quads Overlay (24kgrid.shp) from
653 http://data.geocomm.com/quadindex/
654 ----------------------------------
655 field name  type     width,dec  Example
656 NAME        string     (30,0)   Lummi Bay OE W
657 STATE       string      (2,0)   WA
658 LAT         string      (6,0)   48.750
659 LON         string      (8,0)   -122.750
660 MRC         string      (8,0)   48122-G7
661 
662 
663 // Need to figure out which type of alert it is, select the corresponding shapefile,
664 // then store the shapefile AND the alert_tag in the alert hash .filename list?
665 // and draw the map.  Add an item to alert hash to keep track?
666 
667 // The last parameter denotes loading into pixmap_alerts instead of pixmap or pixmap_final
668 // Here's the old APRS-type map call:
669 //map_search (w, alert_scan, alert, &alert_count,(int)(alert_status[i + 2] == DATA_VIA_TNC || alert_status[i + 2] == DATA_VIA_LOCAL), DRAW_TO_PIXMAP_ALERTS);
670 
671 // Check the zone name(s) to see which Shapefile(s) to use.
672 
673             switch (zone[4]) {
674                 case ('C'): // County File (c_16my01.shp)
675                     break;
676 ***             case ('A'): // County Warning File (w_24ja01.shp)
677                     break;
678                 case ('Z'): // Zone File (z_16mr01.shp, z_16my01.shp, mz24ja01.shp, oz09de99.shp)
679                     break;
680                 case ('F'): // Fire weather (fz_ddmmyy.shp)
681                     break;
682 ***             case ('A'): // Canadian Area (a_mmddyy.shp)
683                     break;
684                 case ('R'): // Canadian Region (r_mmddyy.shp)
685                     break;
686             }
687 
688 
689  **********************************************************/
690 
691 static dbfawk_sig_info *Dbf_sigs = NULL;
692 static awk_symtab *Symtbl = NULL;
693 /* default dbfawk rule when no better signature match is found */
694 static awk_rule dbfawk_default_rules[] =
695 {
696   {
697     0,
698     BEGIN,
699     NULL,
700     NULL,
701     0,
702     0,
703     "dbfinfo=\"\"; key=\"\"; lanes=1; color=8; fill_color=13; fill_stipple=0; name=\"\"; filled=0; fill_style=0; pattern=0; display_level=65536; label_level=0",
704     0,
705     0
706   },
707 };
708 #define dbfawk_default_nrules (sizeof(dbfawk_default_rules)/sizeof(dbfawk_default_rules[0]))
709 static dbfawk_sig_info *dbfawk_default_sig = NULL;
710 
draw_shapefile_map(Widget w,char * dir,char * filenm,alert_entry * alert,u_char alert_color,int destination_pixmap,map_draw_flags * mdf)711 void draw_shapefile_map (Widget w,
712                          char *dir,
713                          char *filenm,
714                          alert_entry * alert,
715                          u_char alert_color,
716                          int destination_pixmap,
717                          map_draw_flags *mdf)
718 {
719 
720   DBFHandle       hDBF;
721   SHPObject       *object;
722   static XPoint   points[MAX_MAP_POINTS];
723   char            file[MAX_FILENAME];  /* Complete path/name of image file */
724   char            short_filenm[MAX_FILENAME];
725   int             i, fieldcount, recordcount, structure, ring;
726   SHPHandle       hSHP;
727   int             nShapeType, nEntities;
728   double          adfBndsMin[4], adfBndsMax[4];
729   char            *sType;
730   unsigned long   my_lat, my_long;
731   long            x,y;
732   int             ok, index;
733   int             polygon_hole_flag;
734   int             *polygon_hole_storage;
735   GC              gc_temp = NULL;
736   XGCValues       gc_temp_values;
737   Region          region[3];
738   int             temp_region1;
739   int             temp_region2;
740   int             temp_region3;
741   int             gps_flag = 0;
742   char            gps_label[100];
743   int             gps_color = 0x0c;
744   int             quad_overlay_flag = 0;
745   int             weather_alert_flag = 0;
746   char            *filename;  // filename itself w/o directory
747   int             found_shape = -1;
748   int             ok_to_draw = 0;
749   int             high_water_mark_i = 0;
750   int             high_water_mark_index = 0;
751   char            quad_label[100];
752   char            status_text[MAX_FILENAME];
753   /* these have to be static since I recycle Symtbl between calls */
754   static char     dbfsig[1024],dbffields[1024],name[64],key[64],sym[4];
755   static int      color,lanes,filled,pattern,display_level,label_level;
756   static int      fill_style,fill_color;
757   static int      fill_stipple;
758   //static int layer;
759   dbfawk_sig_info *sig_info = NULL;
760   dbfawk_field_info *fld_info = NULL;
761 
762 
763   int draw_filled_orig;
764   int draw_filled;
765   static int label_color = 8; /* set by dbfawk.  Otherwise it's black. */
766   static int font_size = FONT_DEFAULT; // set by dbfawk, else this default
767 
768   typedef struct _label_string
769   {
770     char   label[50];
771     int    found;
772     struct _label_string *next;
773   } label_string;
774 
775   // Define hash table for label pointers
776   label_string *label_hash[256];
777   // And the index into it
778   uint8_t hash_index = 0;
779 
780   label_string *ptr2 = NULL;
781 
782   struct Rect viewportRect;
783   double rXmin, rYmin, rXmax,rYmax;
784   shpinfo *si;
785   int nhits;
786 
787   // pull this out of the map_draw_flags
788   draw_filled = mdf->draw_filled;
789 
790   // Initialize the hash table label pointers
791   for (i = 0; i < 256; i++)
792   {
793     label_hash[i] = NULL;
794   }
795 
796 
797   // We're going to change draw_filled a bunch if we've got Auto turned
798   // on, but we have to check --- save this!
799   draw_filled_orig=draw_filled;
800 
801   // Re-initialize these static variables every time through here.
802   // Otherwise, if a dbfawk file forgets to set one, we'd use what the
803   // last map used.  Sometimes that's ugly.
804   color=8;
805   lanes=1;
806   filled=0;
807   fill_style=0;
808   fill_color=13;
809   fill_stipple=0;
810   pattern=0;
811   display_level=8192;
812   label_level=0;
813   label_color=8;
814   font_size=FONT_DEFAULT;
815 
816   if (Dbf_sigs == NULL)
817   {
818     Dbf_sigs = dbfawk_load_sigs(get_data_base_dir("config"),".dbfawk");
819   }
820 
821   if (debug_level & 16)
822     fprintf(stderr,"DBFAWK signatures %sfound in %s.\n",
823             (Dbf_sigs)?" ":"NOT ",get_data_base_dir("config"));
824 
825   if (dbfawk_default_sig == NULL)
826   {
827     /* set up default dbfawk when no sig matches */
828     // This one is ok to leave allocated, as it gets malloc'ed
829     // once during each runtime and then gets left alone.  We
830     // don't need to free it.
831     dbfawk_default_sig = calloc(1,sizeof(dbfawk_sig_info));
832     CHECKMALLOC(dbfawk_default_sig);
833 
834     // Calls awk_new_program which allocates memory.  Again, we
835     // don't need to free this one, as it gets allocated only
836     // once per runtime.
837     dbfawk_default_sig->prog = awk_load_program_array(dbfawk_default_rules,dbfawk_default_nrules);
838   }
839 
840   //fprintf(stderr,"*** Alert color: %d ***\n",alert_color);
841 
842   // We don't draw the shapes if alert_color == -1
843   if (alert_color != 0xff)
844   {
845     ok_to_draw++;
846   }
847 
848 
849   xastir_snprintf(file, sizeof(file), "%s/%s", dir, filenm);
850 
851   // Create a shorter filename for display (one that fits the
852   // status line more closely).  Subtract the length of the
853   // "Indexing " and/or "Loading " strings as well.
854   if (strlen(filenm) > (41 - 9))
855   {
856     int avail = 41 - 11;
857     int new_len = strlen(filenm) - avail;
858 
859     xastir_snprintf(short_filenm,
860                     sizeof(short_filenm),
861                     "..%s",
862                     &filenm[new_len]);
863   }
864   else
865   {
866     xastir_snprintf(short_filenm,
867                     sizeof(short_filenm),
868                     "%s",
869                     filenm);
870   }
871 
872   //fprintf(stderr,"draw_shapefile_map:start:%s\n",file);
873 
874   filename = filenm;
875   i = strlen(filenm);
876   while ( (i >= 0) && (filenm[i] != '/') )
877   {
878     filename = &filenm[i--];
879   }
880   //fprintf(stderr,"draw_shapefile_map:filename:%s\ttitle:%s\n",filename,alert->title);
881 
882   if (alert)
883   {
884     weather_alert_flag++;
885   }
886 
887   // Check for ~/.xastir/tracklogs directory.  We set up the
888   // labels and colors differently for these types of files.
889 //    if (strstr(filenm,".xastir/tracklogs")) { // We're in the ~/.xastir/tracklogs directory
890   if (strstr(filenm,"GPS"))   // We're in the maps/GPS directory
891   {
892     gps_flag++;
893   }
894 
895   // Open the .dbf file for reading.  This has the textual
896   // data (attributes) associated with each shape.
897   hDBF = DBFOpen( file, "rb" );
898   if ( hDBF == NULL )
899   {
900     if (debug_level & 16)
901     {
902       fprintf(stderr,"draw_shapefile_map: DBFOpen(%s,\"rb\") failed.\n", file );
903     }
904 
905     return;
906   }
907   if (debug_level & 16)
908   {
909     fprintf(stderr,"\n---------------------------------------------\nInfo for %s\n",filenm);
910   }
911 
912   *dbfsig = '\0';
913   fieldcount = dbfawk_sig(hDBF,dbfsig,sizeof(dbfsig));
914   if (fieldcount == 0)
915   {
916     DBFClose( hDBF );   // Clean up open file descriptors
917     return;     // Should have at least one field
918 
919   }
920   recordcount = DBFGetRecordCount(hDBF);
921   if (recordcount == 0)
922   {
923     DBFClose( hDBF );   // Clean up open file descriptors
924     return;     // Should have at least one record
925   }
926   if (debug_level & 16)
927   {
928     fprintf(stderr,"%d Columns,  %d Records in file\n", fieldcount, recordcount);
929     fprintf(stderr,"DBF signature: %s\n",dbfsig);
930   }
931   if (Dbf_sigs)     /* see if we have a .dbfawk file that matches */
932   {
933     sig_info = dbfawk_find_sig(Dbf_sigs,dbfsig,file);
934     if (sig_info)
935     {
936       gps_flag = 0;  // trump gps_flag-- use dbfawk
937     }
938     if (!sig_info)
939     {
940       fprintf(stderr,"No DBFAWK signature for %s!  Using default.\n",filenm);
941       sig_info = dbfawk_default_sig;
942     }
943     if (sig_info)           /* we've got a .dbfawk, so set up symtbl */
944     {
945 
946       if (!Symtbl)
947       {
948         Symtbl = awk_new_symtab();
949         awk_declare_sym(Symtbl,"dbffields",STRING,dbffields,sizeof(dbffields));
950         awk_declare_sym(Symtbl,"color",INT,&color,sizeof(color));
951         awk_declare_sym(Symtbl,"lanes",INT,&lanes,sizeof(lanes));
952         //awk_declare_sym(Symtbl,"layer",INT,&layer,sizeof(layer));
953         awk_declare_sym(Symtbl,"name",STRING,name,sizeof(name));
954         awk_declare_sym(Symtbl,"key",STRING,key,sizeof(key));
955         awk_declare_sym(Symtbl,"symbol",STRING,sym,sizeof(sym));
956         awk_declare_sym(Symtbl,"filled",INT,&filled,sizeof(filled));
957         awk_declare_sym(Symtbl,"fill_style",INT,&fill_style,sizeof(fill_style));
958         awk_declare_sym(Symtbl,"fill_color",INT,&fill_color,sizeof(fill_color));
959         awk_declare_sym(Symtbl,"fill_stipple",INT,&fill_stipple,sizeof(fill_stipple));
960         awk_declare_sym(Symtbl,"pattern",INT,&pattern,sizeof(pattern));
961         awk_declare_sym(Symtbl,"display_level",INT,&display_level,sizeof(display_level));
962         awk_declare_sym(Symtbl,"label_level",INT,&label_level,sizeof(label_level));
963         awk_declare_sym(Symtbl,"label_color",INT,&label_color,sizeof(label_color));
964         awk_declare_sym(Symtbl,"font_size",INT,&font_size,sizeof(font_size));
965       }
966       if (awk_compile_program(Symtbl,sig_info->prog) < 0)
967       {
968         fprintf(stderr,"Unable to compile .dbfawk program\n");
969 
970         if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
971         {
972           dbfawk_free_sigs(sig_info);
973         }
974         return;
975       }
976       awk_exec_begin(sig_info->prog); /* execute a BEGIN rule if any */
977 
978       /* find out which dbf fields we care to read */
979       fld_info = dbfawk_field_list(hDBF, dbffields);
980 
981     }
982     else                    /* should never be reached anymore! */
983     {
984       fprintf(stderr,"No DBFAWK signature for %s and no default!\n",filenm);
985       //exit(1);  // Debug
986       return;
987     }
988   }
989   /*
990    * DBFAWK: all this WX junk following to set up the search fields and
991    * parameters is now done by the dbfawk file which sets the "key"
992    * variable to the appropriate search key for each record which is
993    * compared to the alert->title[].
994    */
995 
996   // Search for specific record if we're doing alerts
997   if (weather_alert_flag && (alert->index == -1) )
998   {
999     int done = 0;
1000 
1001     // Step through all records
1002     for( i = 0; i < recordcount && !done; i++ )
1003     {
1004       int keylen;
1005 
1006       if (sig_info)
1007       {
1008         char modified_title[50];
1009 
1010         dbfawk_parse_record(sig_info->prog,hDBF,fld_info,i);
1011 
1012         keylen = strlen(key);
1013         if (debug_level & 16)
1014         {
1015           static char old_key[4]; // Used to limit number of output lines in debug mode
1016 
1017           if (strncmp(old_key, key, 4))
1018           {
1019             fprintf(stderr,"dbfawk alert parse: record %d key=%s\n",
1020                     i,key);
1021             memcpy(old_key, key, sizeof(old_key));
1022           }
1023         }
1024 
1025         xastir_snprintf(modified_title, sizeof(modified_title), "%s", alert->title);
1026 
1027         // Tweak for RED_FLAG alerts:  If RED_FLAG alert
1028         // we've changed the 'Z' to an 'F' in our
1029         // alert->title already.  Change the 'F' back to a
1030         // 'Z' temporarily (modified_title) for our
1031         // compares.
1032         //
1033         if (modified_title[3] == 'F' && strncmp(alert->filename, "fz", 2) == 0)
1034         {
1035           modified_title[3] = 'Z';
1036         }
1037 
1038         // If match using keylen number of chars, try the
1039         // same match but using titlelen number of chars
1040         if (strncmp(modified_title,key,keylen) == 0)
1041         {
1042           int titlelen;
1043 
1044           titlelen = strlen(modified_title);
1045 
1046           // Try the same match with titlelen number of
1047           // chars
1048           if (strncmp(modified_title,key,titlelen) == 0)
1049           {
1050 
1051             found_shape = i;
1052             done++;
1053             if (debug_level & 16)
1054             {
1055               fprintf(stderr,"dbfawk alert found it: %d \n",i);
1056               fprintf(stderr,"Title %s, key %s\n",modified_title,key);
1057 
1058             }
1059           }
1060           else
1061           {
1062             // Found a match using keylen number of
1063             // characters, but it's not a match using
1064             // titlelen number of characters.
1065             if (debug_level & 16)
1066             {
1067               fprintf(stderr,
1068                       "dbfawk alert: match w/keylen, not w/titlelen: %s=%d %s=%d\n",
1069                       key,
1070                       keylen,
1071                       modified_title,
1072                       titlelen);
1073               fprintf(stderr,"Title %s, key %s\n",modified_title,key);
1074             }
1075           }
1076         }
1077       }
1078     }
1079     alert->index = found_shape; // Fill it in 'cuz we just found it
1080   } /* if (weather_alert_flag && alert_index == -1)... */
1081   else if (weather_alert_flag)
1082   {
1083     // We've been here before and we already know the index into the
1084     // file to fetch this particular shape.
1085     found_shape = alert->index;
1086     if (debug_level & 16)
1087     {
1088       fprintf(stderr,"wx_alert: found_shape = %d\n",found_shape);
1089     }
1090   }
1091 
1092   //fprintf(stderr,"Found shape: %d\n", found_shape);
1093 
1094   if (debug_level & 16)
1095   {
1096     fprintf(stderr,"Calling SHPOpen()\n");
1097   }
1098 
1099   // Open the .shx/.shp files for reading.
1100   // These are the index and the vertice files.
1101   hSHP = SHPOpen( file, "rb" );
1102   if( hSHP == NULL )
1103   {
1104     fprintf(stderr,"draw_shapefile_map: SHPOpen(%s,\"rb\") failed.\n", file );
1105     DBFClose( hDBF );   // Clean up open file descriptors
1106 
1107     dbfawk_free_info(fld_info);
1108     if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1109     {
1110       dbfawk_free_sigs(sig_info);
1111     }
1112 
1113     return;
1114   }
1115 
1116 
1117   // Get the extents of the map file
1118   SHPGetInfo( hSHP, &nEntities, &nShapeType, adfBndsMin, adfBndsMax );
1119 
1120 
1121   // Check whether we're indexing or drawing the map
1122   if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
1123        || (destination_pixmap == INDEX_NO_TIMESTAMPS) )
1124   {
1125 
1126     xastir_snprintf(status_text,
1127                     sizeof(status_text),
1128                     langcode ("BBARSTA039"),
1129                     short_filenm);
1130     statusline(status_text,0);       // Indexing ...
1131 
1132     // We're indexing only.  Save the extents in the index.
1133     index_update_ll(filenm,    // Filename only
1134                     adfBndsMin[1],  // Bottom
1135                     adfBndsMax[1],  // Top
1136                     adfBndsMin[0],  // Left
1137                     adfBndsMax[0],  // Right
1138                     1000);          // Default Map Level
1139 
1140     DBFClose( hDBF );   // Clean up open file descriptors
1141     SHPClose( hSHP );
1142 
1143     dbfawk_free_info(fld_info);
1144     if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1145     {
1146       dbfawk_free_sigs(sig_info);
1147     }
1148 
1149     return; // Done indexing this file
1150   }
1151   else
1152   {
1153     xastir_snprintf(status_text,
1154                     sizeof(status_text),
1155                     langcode ("BBARSTA028"),
1156                     short_filenm);
1157     statusline(status_text,0);       // Loading ...
1158   }
1159 
1160   // We put this section AFTER the code that determines whether we're merely
1161   // indexing, so we don't bother to generate rtrees unless we're really
1162   // drawing the file.
1163   si=NULL;
1164 
1165   // Don't bother even looking at the hash if this shapefile is completely
1166   // contained in the current viewport.  We'll have to read every shape
1167   // in it anyway, and all we'd be doing is extra work searching the
1168   // RTree
1169   if (!map_inside_viewport_lat_lon(adfBndsMin[1],
1170                                    adfBndsMax[1],
1171                                    adfBndsMin[0],
1172                                    adfBndsMax[0]))
1173   {
1174     si = get_shp_from_hash(file);
1175     if (!si)
1176     {
1177       // we don't have what we need, so generate the index and make
1178       // the hashtable entry
1179       add_shp_to_hash(file,hSHP); // this will index all the shapes in
1180       // an RTree and save the root in a
1181       // shpinfo structure
1182       si=get_shp_from_hash(file); // now get that structure
1183       if (!si)
1184       {
1185         fprintf(stderr,
1186                 "Panic!  added %s, lost it already!\n",file);
1187         exit(1);
1188       }
1189     }
1190   }
1191   // we need this for the rtree search
1192   get_viewport_lat_lon(&rXmin, &rYmin, &rXmax, &rYmax);
1193   viewportRect.boundary[0] = (RectReal) rXmin;
1194   viewportRect.boundary[1] = (RectReal) rYmin;
1195   viewportRect.boundary[2] = (RectReal) rXmax;
1196   viewportRect.boundary[3] = (RectReal) rYmax;
1197 
1198   switch ( nShapeType )
1199   {
1200     case SHPT_POINT:
1201       sType = "Point";
1202       break;
1203 
1204     case SHPT_POINTZ:
1205       sType = "3D Point";
1206       break;
1207 
1208     case SHPT_ARC:
1209       sType = "Polyline";
1210       break;
1211 
1212     case SHPT_ARCZ:
1213       sType = "3D Polyline";
1214       break;
1215 
1216     case SHPT_POLYGON:
1217       sType = "Polygon";
1218       break;
1219 
1220     case SHPT_POLYGONZ:
1221       sType = "3D Polygon";
1222       break;
1223 
1224     case SHPT_MULTIPOINT:
1225       fprintf(stderr,"Multi-Point Shapefile format not implemented: %s\n",file);
1226       sType = "MultiPoint";
1227       DBFClose( hDBF );   // Clean up open file descriptors
1228       SHPClose( hSHP );
1229 
1230       dbfawk_free_info(fld_info);
1231       if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1232       {
1233         dbfawk_free_sigs(sig_info);
1234       }
1235 
1236       return; // Multipoint type.  Not implemented yet.
1237       break;
1238 
1239     default:
1240       DBFClose( hDBF );   // Clean up open file descriptors
1241       SHPClose( hSHP );
1242 
1243       dbfawk_free_info(fld_info);
1244       if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1245       {
1246         dbfawk_free_sigs(sig_info);
1247       }
1248 
1249       return; // Unknown type.  Don't know how to process it.
1250       break;
1251   }
1252 
1253   if (debug_level & 16)
1254   {
1255     fprintf(stderr,"%s(%d), %d Records in file\n",sType,nShapeType,nEntities);
1256   }
1257 
1258   if (debug_level & 16)
1259     fprintf(stderr,"File Bounds: (%15.10g,%15.10g)\n\t(%15.10g,%15.10g)\n",
1260             adfBndsMin[0], adfBndsMin[1], adfBndsMax[0], adfBndsMax[1] );
1261 
1262   // Check the bounding box for this shapefile.  If none of the
1263   // file is within our viewport, we can skip the entire file.
1264 
1265   if (debug_level & 16)
1266   {
1267     fprintf(stderr,"Calling map_visible_lat_lon on the entire shapefile\n");
1268   }
1269 
1270   if (! map_visible_lat_lon(  adfBndsMin[1],  // Bottom
1271                               adfBndsMax[1],  // Top
1272                               adfBndsMin[0],  // Left
1273                               adfBndsMax[0]) )    // Right
1274   {
1275     if (debug_level & 16)
1276     {
1277       fprintf(stderr,"No shapes within viewport.  Skipping file...\n");
1278     }
1279 
1280     DBFClose( hDBF );   // Clean up open file descriptors
1281     SHPClose( hSHP );
1282 
1283     dbfawk_free_info(fld_info);
1284     if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1285     {
1286       dbfawk_free_sigs(sig_info);
1287     }
1288 
1289     return;     // The file contains no shapes in our viewport
1290   }
1291 
1292 
1293   // Set a default line width for all maps.  This will most likely
1294   // be modified for particular maps in later code.
1295   (void)XSetLineAttributes(XtDisplay(w), gc, 0, LineSolid, CapButt,JoinMiter);
1296 
1297 
1298   // NOTE: Setting the color here and in the "else" may not stick if we do more
1299   //       complex drawing further down like a SteelBlue lake with a black boundary,
1300   //       or if we have labels turned on which resets our color to black.
1301   if (weather_alert_flag)     /* XXX */
1302   {
1303     char xbm_path[MAX_FILENAME];
1304     unsigned int _w, _h;
1305     int _xh, _yh;
1306     int ret_val;
1307     FILE *alert_fp = NULL;
1308     char xbm_filename[MAX_FILENAME];
1309 
1310 
1311     // This GC is used for weather alerts (writing to the
1312     // pixmap: pixmap_alerts) and _was_ used for beam_heading
1313     // rays, but no longer is.
1314     (void)XSetForeground (XtDisplay (w), gc_tint, colors[(int)alert_color]);
1315 
1316     // N7TAP: No more tinting as that would change the color of the alert, losing that information.
1317     (void)XSetFunction(XtDisplay(w), gc_tint, GXcopy);
1318     /*
1319     Options are:
1320         GXclear         0                       (Don't use)
1321         GXand           src AND dst             (Darker colors, black can result from overlap)
1322         GXandReverse    src AND (NOT dst)       (Darker colors)
1323         GXcopy          src                     (Don't use)
1324         GXandInverted   (NOT src) AND dst       (Pretty colors)
1325         GXnoop          dst                     (Don't use)
1326         GXxor           src XOR dst             (Don't use, overlapping areas cancel each other out)
1327         GXor            src OR dst              (More pastel colors, too bright?)
1328         GXnor           (NOT src) AND (NOT dst) (Darker colors, very readable)
1329         GXequiv         (NOT src) XOR dst       (Bright, very readable)
1330         GXinvert        (NOT dst)               (Don't use)
1331         GXorReverse     src OR (NOT dst)        (Bright, not as readable as others)
1332         GXcopyInverted  (NOT src)               (Don't use)
1333         GXorInverted    (NOT src) OR dst        (Bright, not very readable)
1334         GXnand          (NOT src) OR (NOT dst)  (Bright, not very readable)
1335         GXset           1                       (Don't use)
1336     */
1337 
1338 
1339     // Note that we can define more alert files for other countries.  They just need to match
1340     // the alert text that comes along in the NWS packet.
1341     // Current alert files we have defined:
1342     //      winter_storm.xbm *
1343     //      snow.xbm
1344     //      winter_weather.xbm *
1345     //      flood.xbm
1346     //      torndo.xbm *
1347     //      red_flag.xbm
1348     //      wind.xbm
1349     //      alert.xbm (Used if no match to another filename)
1350 
1351     // Alert texts we receive:
1352     //      FLOOD
1353     //      SNOW
1354     //      TORNDO
1355     //      WIND
1356     //      WINTER_STORM
1357     //      WINTER_WEATHER
1358     //      RED_FLAG
1359     //      SVRTSM (no file defined for this yet)
1360     //      Many others.
1361 
1362     // Attempt to open the alert filename:  <alert_tag>.xbm (lower-case alert text)
1363     // to detect whether we have a matching filename for our alert text.
1364     xastir_snprintf(xbm_filename, sizeof(xbm_filename), "%s", alert->alert_tag);
1365 
1366     // Convert the filename to lower-case
1367     to_lower(xbm_filename);
1368 
1369     // Construct the complete path/filename
1370     strcpy(xbm_path, SYMBOLS_DIR);
1371     xbm_path[sizeof(xbm_path)-1] = '\0';  // Terminate string
1372     strcat(xbm_path, "/");
1373     xbm_path[sizeof(xbm_path)-1] = '\0';  // Terminate string
1374     strcat(xbm_path, xbm_filename);
1375     xbm_path[sizeof(xbm_path)-1] = '\0';  // Terminate string
1376     strcat(xbm_path, ".xbm");
1377     xbm_path[sizeof(xbm_path)-1] = '\0';  // Terminate string
1378 
1379     // Try opening the file
1380     alert_fp = fopen(xbm_path, "rb");
1381     if (alert_fp == NULL)
1382     {
1383       // Failed to find a matching file:  Instead use the "alert.xbm" file
1384       xastir_snprintf(xbm_path, sizeof(xbm_path), "%s/%s", SYMBOLS_DIR, "alert.xbm");
1385     }
1386     else
1387     {
1388       // Success:  Close the file pointer
1389       fclose(alert_fp);
1390     }
1391 
1392     /* XXX - need to add SVRTSM */
1393 
1394 
1395     (void)XSetLineAttributes(XtDisplay(w), gc_tint, 0, LineSolid, CapButt,JoinMiter);
1396     XFreePixmap(XtDisplay(w), pixmap_wx_stipple);
1397     ret_val = XReadBitmapFile(XtDisplay(w),
1398                               DefaultRootWindow(XtDisplay(w)),
1399                               xbm_path,
1400                               &_w,
1401                               &_h,
1402                               &pixmap_wx_stipple,
1403                               &_xh,
1404                               &_yh);
1405 
1406     if (ret_val != 0)
1407     {
1408       fprintf(stderr,"XReadBitmapFile() failed: Bitmap not found? %s\n",xbm_path);
1409 
1410       // We shouldn't exit on this one, as it's not so severe
1411       // that we should kill Xastir.  I've seen this happen
1412       // after very long runtimes though, so perhaps there's a
1413       // problem somewhere in the X11 server and/or it's
1414       // caching?
1415       //
1416       //exit(1);
1417     }
1418     else
1419     {
1420       // We successfully loaded the bitmap, so set the stipple
1421       // properly to use it.  Skip this part if we were
1422       // unsuccessful at loading the bitmap.
1423       (void)XSetStipple(XtDisplay(w), gc_tint, pixmap_wx_stipple);
1424     }
1425 
1426   } /* ...end if (weather_alert_flag) */
1427   else   /* !weather_alert_flag */
1428   {
1429 // Are these actually used anymore by the code?  Colors get set later
1430 // when we know more about what we're dealing with.
1431   }
1432 
1433   // Now that we have the file open, we can read out the structures.
1434   // We can handle Point, PolyLine and Polygon shapefiles at the moment.
1435 
1436 
1437 
1438   HandlePendingEvents(app_context);
1439   if (interrupt_drawing_now)
1440   {
1441     DBFClose( hDBF );   // Clean up open file descriptors
1442     SHPClose( hSHP );
1443     // Update to screen
1444     (void)XCopyArea(XtDisplay(da),
1445                     pixmap,
1446                     XtWindow(da),
1447                     gc,
1448                     0,
1449                     0,
1450                     (unsigned int)screen_width,
1451                     (unsigned int)screen_height,
1452                     0,
1453                     0);
1454 
1455     dbfawk_free_info(fld_info);
1456     if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1457     {
1458       dbfawk_free_sigs(sig_info);
1459     }
1460 
1461     return;
1462   }
1463 
1464   // Now instead of looping over all the shapes, search for the ones that
1465   // are in our viewport and only loop over those
1466   //fprintf(stderr,"Deciding how to process this file...\n");
1467   if (weather_alert_flag)     // We're drawing _one_ weather alert shape
1468   {
1469     //fprintf(stderr," weather alert flag set...\n");
1470     if (found_shape != -1)      // Found the record
1471     {
1472       //fprintf(stderr,"  found_shape set...\n");
1473       // just in case we haven't drawn any real maps yet...
1474       if (!RTree_hitarray)
1475       {
1476         //fprintf(stderr,"   mallocing hitarray...\n");
1477         RTree_hitarray = (int *)malloc(sizeof(int)*1000);
1478         RTree_hitarray_size=1000;
1479       }
1480       CHECKMALLOC(RTree_hitarray);
1481       RTree_hitarray[0]=found_shape;
1482       //fprintf(stderr," %s contains alert\n",file);
1483       nhits=1;
1484     }
1485     else    // Didn't find the record
1486     {
1487       //fprintf(stderr,"   found_shape is -1...\n");
1488       nhits=0;
1489     }
1490   }
1491   else    // Draw an entire Shapefile map
1492   {
1493     //fprintf(stderr,"   weather_alert_flag not set...\n");
1494     if (si)
1495     {
1496       //fprintf(stderr,"   si is 0x%lx...\n",(unsigned long int) si);
1497       RTree_hitarray_index=0;
1498       // the callback will be executed every time the search finds a
1499       // shape whose bounding box overlaps the viewport.
1500       nhits = Xastir_RTreeSearch(si->root, &viewportRect,
1501                                  (void *)RTreeSearchCallback, 0);
1502       //fprintf(stderr,"Found %d hits in %s\n",nhits,file);
1503     }
1504     else
1505     {
1506       //fprintf(stderr,"   si not set ...\n");
1507       // we read the entire shapefile
1508       nhits=nEntities;
1509       // fprintf(stderr," %s entirely in view, with %d shapes\n",file,nhits);
1510     }
1511   }
1512   //fprintf(stderr," Done with decision, nhits is %d\n",nhits);
1513 
1514   // only iterate over the hits found by RTreeSearch, not all of them
1515   for (RTree_hitarray_index=0; RTree_hitarray_index<nhits;
1516        RTree_hitarray_index++)
1517   {
1518     int skip_it = 0;
1519     int skip_label = 0;
1520 
1521 
1522     // Call HandlePendingEvents() every XX objects so that we
1523     // can interrupt map drawing if necessary.  Keep this at a
1524     // power of two for speed.  Bigger numbers speed up map
1525     // drawing at the expense of being able to interrupt map
1526     // drawing quickly.  HandlePendingEvents() drops to about
1527     // 0.5% CPU at 64 (according to gprof).  That appears to be
1528     // the break-even point where CPU is minimal.
1529     //
1530     if ( (RTree_hitarray_index % 64) == 0 )
1531     {
1532 
1533       HandlePendingEvents(app_context);
1534       if (interrupt_drawing_now)
1535       {
1536         DBFClose( hDBF );   // Clean up open file descriptors
1537         SHPClose( hSHP );
1538         // Update to screen
1539         (void)XCopyArea(XtDisplay(da),
1540                         pixmap,
1541                         XtWindow(da),
1542                         gc,
1543                         0,
1544                         0,
1545                         (unsigned int)screen_width,
1546                         (unsigned int)screen_height,
1547                         0,
1548                         0);
1549         dbfawk_free_info(fld_info);
1550         if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
1551         {
1552           dbfawk_free_sigs(sig_info);
1553         }
1554         return;
1555       }
1556     }
1557 
1558 
1559     if (si)
1560     {
1561       structure=RTree_hitarray[RTree_hitarray_index];
1562     }
1563     else if ((weather_alert_flag && found_shape!=-1))
1564     {
1565       structure = RTree_hitarray[0];
1566     }
1567     else
1568     {
1569       structure = RTree_hitarray_index;
1570     }
1571 
1572     // Have had segfaults before at the SHPReadObject() call
1573     // when the Shapefile was corrupted.
1574     //fprintf(stderr,"Before SHPReadObject:%d\n",structure);
1575 
1576     object = SHPReadObject( hSHP, structure );  // Note that each structure can have multiple rings
1577 
1578     //fprintf(stderr,"After SHPReadObject\n");
1579 
1580     if (object == NULL)
1581     {
1582       continue;  // Skip this iteration, go on to the next
1583     }
1584 
1585     // Fill in the boundary variables in the alert record.  We use
1586     // this info in load_alert_maps() to determine which alerts are
1587     // within our view, without having to open up the shapefiles to
1588     // get this info (faster).
1589     if (weather_alert_flag)
1590     {
1591       alert->top_boundary    = object->dfYMax;
1592       alert->left_boundary   = object->dfXMin;
1593       alert->bottom_boundary = object->dfYMin;
1594       alert->right_boundary  = object->dfXMax;
1595     }
1596 
1597     // Here we check the bounding box for this shape against our
1598     // current viewport.  If we can't see it, don't draw it.
1599 
1600 //        if (debug_level & 16)
1601 //            fprintf(stderr,"Calling map_visible_lat_lon on a shape\n");
1602 
1603     if ( map_visible_lat_lon( object->dfYMin,   // Bottom
1604                               object->dfYMax,   // Top
1605                               object->dfXMin,   // Left
1606                               object->dfXMax) )     // Right
1607     {
1608 
1609       const char *temp;
1610       int jj;
1611       int x0 = 0; // Used for computing label rotation
1612       int x1 = 0;
1613       int y0 = 0;
1614       int y1 = 0;
1615 
1616 
1617       if (debug_level & 16)
1618       {
1619         fprintf(stderr,"Shape %d is visible, drawing it.", structure);
1620         fprintf(stderr,"  Parts in shape: %d\n", object->nParts );    // Number of parts in this structure
1621       }
1622 
1623       if (debug_level & 16)
1624       {
1625         // Print the field contents
1626         for (jj = 0; jj < fieldcount; jj++)
1627         {
1628           if (fieldcount >= (jj + 1) )
1629           {
1630             temp = DBFReadStringAttribute( hDBF, structure, jj );
1631             if (temp != NULL)
1632             {
1633               fprintf(stderr,"%s, ", temp);
1634             }
1635           }
1636         }
1637         fprintf(stderr,"\n");
1638         fprintf(stderr,"Done with field contents\n");
1639       }
1640       if (sig_info)
1641       {
1642         dbfawk_parse_record(sig_info->prog,hDBF,fld_info,structure);
1643         if (debug_level & 16)
1644         {
1645           fprintf(stderr,"dbfawk parse of structure %d: ",structure);
1646           fprintf(stderr,"color=%d ",color);
1647           fprintf(stderr,"lanes=%d ",lanes);
1648           fprintf(stderr,"name=%s ",name);
1649           fprintf(stderr,"key=%s ",key);
1650           fprintf(stderr,"symbol=%s ",sym);
1651           fprintf(stderr,"filled=%d ",filled);
1652           fprintf(stderr,"fill_style=%d ",fill_style);
1653           fprintf(stderr,"fill_color=%d ",fill_color);
1654           fprintf(stderr,"fill_stipple=%d ",fill_stipple);
1655           fprintf(stderr,"pattern=%d ",pattern);
1656           fprintf(stderr,"display_level=%d ",display_level);
1657           fprintf(stderr,"label_level=%d ",label_level);
1658           fprintf(stderr,"label_color=%d\n",label_color);
1659           // fprintf(stderr,"layer=%d\n",layer);
1660         }
1661         /* set attributes */
1662         (void)XSetForeground(XtDisplay(w), gc, colors[color]);
1663 
1664 
1665         // Let the user decide whether to make the map
1666         // filled or unfilled via the Map Properties dialog.
1667         // This allows things like the NOAA counties map to
1668         // be used as a base map (filled) or as a vector
1669         // overlay map at the user's discretion.  The
1670         // choices available are:
1671         // 0: Global No-Fill.  Don't fill any polygons.
1672         // 1: Global Fill.  Fill all polygons.
1673         // 2: Auto.  dbfawk file, if present, takes over control.
1674         //
1675         if (debug_level & 16)
1676         {
1677           fprintf(stderr," draw_filled is %d\n",draw_filled_orig);
1678         }
1679         switch (draw_filled_orig)
1680         {
1681 
1682           case 0: // Global No-Fill (Vector)
1683             // Do nothing, user has chosen Global
1684             // No_Fill for this map.  The draw_filled
1685             // variable takes precedence.
1686             break;
1687 
1688           case 1: // Global Fill
1689             // Do nothing, user has chosen Global
1690             // Fill for this map.  The draw_filled
1691             // variable takes precedence.
1692             break;
1693 
1694           case 2: // Auto
1695           default:
1696             // User has chosen Auto Fill for this map,
1697             // so the Dbfawk file controls the fill
1698             // property.  If no dbfawk file, "filled"
1699             // will take on the default setting listed
1700             // in dbfawk_default_rules[] above.
1701             draw_filled = filled;
1702             break;
1703         }
1704 
1705 
1706         if (weather_alert_flag)   /* XXX will this fix WX alerts? */
1707         {
1708           fill_style = FillStippled;
1709         }
1710 
1711         skip_it = (map_color_levels && (scale_y > display_level));
1712         skip_label = (map_color_levels && (scale_y > label_level));
1713 
1714       }
1715 
1716       /* This case statement is a mess.  Lots of common code could be
1717          used before the case but currently isn't */
1718 
1719       switch ( nShapeType )
1720       {
1721 
1722 
1723         case SHPT_POINT:
1724         case SHPT_POINTZ:
1725           // We hit this case once for each point shape in
1726           // the file, iff that shape is within our
1727           // viewport.
1728 
1729 
1730           if (debug_level & 16)
1731           {
1732             fprintf(stderr,"Found Point Shapefile\n");
1733           }
1734 
1735           // Read each point, place a label there, and an optional symbol
1736           //object->padfX
1737           //object->padfY
1738           //object->padfZ
1739 
1740 //                    if (    mapshots_labels_flag
1741 //                            && map_labels
1742 //                            && (fieldcount >= 3) ) {
1743 
1744           if (!skip_it)      // Need a bracket so we can define
1745           {
1746             // some local variables.
1747             const char *temp = NULL;
1748             int ok = 1;
1749             int temp_ok;
1750 
1751             if (map_labels)
1752             {
1753               temp = name;
1754             }
1755             // Convert point to Xastir coordinates
1756             temp_ok = convert_to_xastir_coordinates(&my_long,
1757                                                     &my_lat,
1758                                                     (float)object->padfX[0],
1759                                                     (float)object->padfY[0]);
1760             //fprintf(stderr,"%ld %ld\n", my_long, my_lat);
1761 
1762             if (!temp_ok)
1763             {
1764               fprintf(stderr,"draw_shapefile_map1: Problem converting from lat/lon\n");
1765               ok = 0;
1766               x = 0;
1767               y = 0;
1768             }
1769             else
1770             {
1771               // Convert to screen coordinates.  Careful
1772               // here!  The format conversions you'll need
1773               // if you try to compress this into two
1774               // lines will get you into trouble.
1775               x = my_long - NW_corner_longitude;
1776               y = my_lat - NW_corner_latitude;
1777               x = x / scale_x;
1778               y = y / scale_y;
1779             }
1780 
1781             if (ok == 1)
1782             {
1783               char symbol_table = '/';
1784               char symbol_id = '.'; /* small x */
1785               char symbol_over = ' ';
1786 
1787               // Fine-tuned the location here so that
1788               // the middle of the 'X' would be at the
1789               // proper pixel.
1790               if (*sym)
1791               {
1792                 symbol(w,0,sym[0],sym[1],sym[2],pixmap,1,x-10,y-10,' ');
1793               }
1794               else
1795               {
1796                 symbol(w, 0, symbol_table, symbol_id, symbol_over, pixmap, 1, x-10, y-10, ' ');
1797               }
1798 
1799               // Fine-tuned this string so that it is
1800               // to the right of the 'X' and aligned
1801               // nicely.
1802               if (map_labels && !skip_label)
1803               {
1804 // Labeling of points done here
1805                 draw_nice_string(w, pixmap, 0, x+10, y+5, (char*)temp, 0xf, 0x10, strlen(temp));
1806                 //(void)draw_label_text ( w, x, y, strlen(temp), colors[label_color], (char *)temp);
1807                 //(void)draw_rotated_label_text (w, 90, x+10, y, strlen(temp), colors[label_color], (char *)temp);
1808               }
1809             }
1810           }
1811           break;
1812 
1813 
1814 
1815         case SHPT_ARC:
1816         case SHPT_ARCZ:
1817           // We hit this case once for each polyline shape
1818           // in the file, iff at least part of that shape
1819           // is within our viewport.
1820 
1821 
1822           if (debug_level & 16)
1823           {
1824             fprintf(stderr,"Found Polylines\n");
1825           }
1826 
1827 // Draw the PolyLines themselves:
1828 
1829           // Default in case we forget to set the line
1830           // width later:
1831           (void)XSetLineAttributes (XtDisplay (w), gc, 0, LineSolid, CapButt,JoinMiter);
1832 
1833 
1834           if (!skip_it)
1835           {
1836             (void)XSetForeground(XtDisplay(w), gc, colors[color]);
1837             (void)XSetLineAttributes(XtDisplay (w), gc,
1838                                      (lanes)?lanes:1,
1839                                      pattern,
1840                                      CapButt,JoinMiter);
1841           }
1842 
1843 //WE7U
1844 // I'd like to be able to change the color of each GPS track for
1845 // each team in the field.  It'll help to differentiate the tracks
1846 // where they happen to cross.
1847           /* XXX - WITH_DBFAWK should handle this case too.  Need to add a color
1848              attribute to the generated dbf file */
1849           if (gps_flag)
1850           {
1851             int jj;
1852             int done = 0;
1853 
1854 
1855             // Fill in the label we'll use later
1856             xastir_snprintf(gps_label,
1857                             sizeof(gps_label), "%s",
1858                             filename);
1859 
1860             // Knock off the "_Color.shp" portion of the
1861             // label.  Find the last underline character
1862             // and change it to an end-of-string.
1863             jj = strlen(gps_label);
1864             while ( !done && (jj > 0) )
1865             {
1866               if (gps_label[jj] == '_')
1867               {
1868                 gps_label[jj] = '\0';   // Terminate it here
1869                 done++;
1870               }
1871               jj--;
1872             }
1873 
1874 
1875             // Check for a color in the filename: i.e.
1876             // "Team2_Track_Red.shp"
1877             if (strstr(filenm,"_Red.shp"))
1878             {
1879               gps_color = 0x0c; // Red
1880             }
1881             else if (strstr(filenm,"_Green.shp"))
1882             {
1883 //                            gps_color = 0x64; // ForestGreen
1884               gps_color = 0x23; // Area Green Hi
1885             }
1886             else if (strstr(filenm,"_Black.shp"))
1887             {
1888               gps_color = 0x08; // black
1889             }
1890             else if (strstr(filenm,"_White.shp"))
1891             {
1892               gps_color = 0x0f; // white
1893             }
1894             else if (strstr(filenm,"_Orange.shp"))
1895             {
1896 //                            gps_color = 0x06; // orange
1897 //                            gps_color = 0x19; // orange2
1898 //                            gps_color = 0x41; // DarkOrange3 (good medium orange)
1899               gps_color = 0x62; // orange3 (brighter)
1900             }
1901             else if (strstr(filenm,"_Blue.shp"))
1902             {
1903               gps_color = 0x03; // cyan
1904             }
1905             else if (strstr(filenm,"_Yellow.shp"))
1906             {
1907               gps_color = 0x0e; // yellow
1908             }
1909             else if (strstr(filenm,"_Purple.shp"))
1910             {
1911               gps_color = 0x0b; // mediumorchid
1912             }
1913             else    // Default color
1914             {
1915               gps_color = 0x0c; // Red
1916             }
1917 
1918             // Set the color for the arc's
1919             (void)XSetForeground(XtDisplay(w), gc, colors[gps_color]);
1920 
1921             // Make the track nice and wide: Easy to
1922             // see.
1923 //                        (void)XSetLineAttributes (XtDisplay (w), gc, 3, LineSolid, CapButt,JoinMiter);
1924             (void)XSetLineAttributes (XtDisplay (w), gc, 3, LineOnOffDash, CapButt,JoinMiter);
1925 //                        (void)XSetLineAttributes (XtDisplay (w), gc, 3, LineDoubleDash, CapButt,JoinMiter);
1926 
1927           }   // End of gps flag portion
1928 
1929 
1930           index = 0;  // Index into our own points array.
1931           // Tells how many points we've
1932           // collected so far.
1933 
1934 
1935           if (ok_to_draw && !skip_it)
1936           {
1937 
1938             // Read the vertices for each vector now
1939 
1940             for (ring = 0; ring < object->nVertices; ring++ )
1941             {
1942               int temp_ok;
1943 
1944               ok = 1;
1945 
1946               //fprintf(stderr,"\t%d:%g %g\t", ring, object->padfX[ring], object->padfY[ring] );
1947               // Convert to Xastir coordinates
1948               temp_ok = convert_to_xastir_coordinates(&my_long,
1949                                                       &my_lat,
1950                                                       (float)object->padfX[ring],
1951                                                       (float)object->padfY[ring]);
1952               //fprintf(stderr,"%ld %ld\n", my_long, my_lat);
1953 
1954               if (!temp_ok)
1955               {
1956                 fprintf(stderr,"draw_shapefile_map2: Problem converting from lat/lon\n");
1957                 ok = 0;
1958                 x = 0;
1959                 y = 0;
1960               }
1961               else
1962               {
1963                 // Convert to screen coordinates.  Careful
1964                 // here!  The format conversions you'll need
1965                 // if you try to compress this into two
1966                 // lines will get you into trouble.
1967                 x = my_long - NW_corner_longitude;
1968                 y = my_lat - NW_corner_latitude;
1969                 x = x / scale_x;
1970                 y = y / scale_y;
1971 
1972 
1973                 // Save the endpoints of the first line
1974                 // segment for later use in label rotation
1975                 if (ring == 0)
1976                 {
1977                   // Save the first set of screen coordinates
1978                   x0 = (int)x;
1979                   y0 = (int)y;
1980                 }
1981                 else if (ring == 1)
1982                 {
1983                   // Save the second set of screen coordinates
1984                   x1 = (int)x;
1985                   y1 = (int)y;
1986                 }
1987               }
1988 
1989               if (ok == 1)
1990               {
1991                 // XDrawLines uses 16-bit unsigned
1992                 // integers (shorts).  Make sure we
1993                 // stay within the limits.
1994                 points[index].x = l16(x);
1995                 points[index].y = l16(y);
1996                 //fprintf(stderr,"%d %d\t", points[index].x, points[index].y);
1997                 index++;
1998               }
1999               if (index > high_water_mark_index)
2000               {
2001                 high_water_mark_index = index;
2002               }
2003 
2004               if (index >= MAX_MAP_POINTS)
2005               {
2006                 index = MAX_MAP_POINTS - 1;
2007                 fprintf(stderr,"Trying to overrun the points array: SHPT_ARC, index=%d\n",index);
2008               }
2009             }   // End of "for" loop for polyline points
2010           }
2011 
2012 
2013           if (ok_to_draw && !skip_it)
2014           {
2015             (void)XDrawLines(XtDisplay(w),
2016                              pixmap,
2017                              gc,
2018                              points,
2019                              l16(index),
2020                              CoordModeOrigin);
2021           }
2022 
2023 
2024 // Figure out and draw the labels for PolyLines.  Note that we later
2025 // determine whether we want to draw the label at all.  Move all
2026 // code possible below that decision point to keep everything fast.
2027 // Don't do unnecessary calculations if we're not going to draw the
2028 // label.
2029 
2030           temp = (gps_flag)?gps_label:name;
2031           if ( (temp != NULL)
2032                && (strlen(temp) != 0)
2033                && map_labels
2034                && !skip_it
2035                && !skip_label )
2036           {
2037             int temp_ok;
2038 
2039             ok = 1;
2040 
2041             // Convert to Xastir coordinates
2042             temp_ok = convert_to_xastir_coordinates(&my_long,
2043                                                     &my_lat,
2044                                                     (float)object->padfX[0],
2045                                                     (float)object->padfY[0]);
2046             //fprintf(stderr,"%ld %ld\n", my_long, my_lat);
2047 
2048             if (!temp_ok)
2049             {
2050               fprintf(stderr,"draw_shapefile_map3: Problem converting from lat/lon\n");
2051               ok = 0;
2052               x = 0;
2053               y = 0;
2054             }
2055             else
2056             {
2057               // Convert to screen coordinates.  Careful
2058               // here!  The format conversions you'll need
2059               // if you try to compress this into two
2060               // lines will get you into trouble.
2061               x = my_long - NW_corner_longitude;
2062               y = my_lat - NW_corner_latitude;
2063               x = l16(x / scale_x);
2064               y = l16(y / scale_y);
2065             }
2066 
2067             if (ok == 1 && ok_to_draw)
2068             {
2069 
2070               int new_label = 1;
2071               int mod_number;
2072 
2073               // Set up the mod_number, which is used
2074               // below to determine how many of each
2075               // identical label are skipped at each
2076               // zoom level.
2077 
2078 // The goal here is to have one complete label visible on the screen
2079 // for each road.  We end up skipping labels based on zoom level,
2080 // which, if the road doesn't have very many segments, may end up
2081 // drawing one label almost entirely off-screen.  :-(
2082 
2083 // If we could check the first line segment to see if the label
2084 // would be drawn off-screen, perhaps we could start drawing at
2085 // segment #2?  We'd have to check whether there is a segment #2.
2086 // Another possibility would be to shift the label on-screen.  Would
2087 // this work for twisty/turny roads though?  I suppose, 'cuz they'd
2088 // end up with more line segments and we could just draw at segment
2089 // #2 in that case instead of shifting.
2090 
2091               if      (scale_y == 1)
2092               {
2093                 mod_number = 1;
2094               }
2095               else if (scale_y <= 2)
2096               {
2097                 mod_number = 1;
2098               }
2099               else if (scale_y <= 4)
2100               {
2101                 mod_number = 2;
2102               }
2103               else if (scale_y <= 8)
2104               {
2105                 mod_number = 4;
2106               }
2107               else if (scale_y <= 16)
2108               {
2109                 mod_number = 8;
2110               }
2111               else if (scale_y <= 32)
2112               {
2113                 mod_number = 16;
2114               }
2115               else
2116               {
2117                 mod_number = (int)(scale_y);
2118               }
2119 
2120               // Check whether we've written out this string
2121               // already:  Look for a match in our linked list
2122 
2123 // The problem with this method is that we might get strings
2124 // "written" at the extreme top or right edge of the display, which
2125 // means the strings wouldn't be visible, but Xastir thinks that it
2126 // wrote the string out visibly.  To partially counteract this I've
2127 // set it up to write only some of the identical strings.  This
2128 // still doesn't help in the cases where a street only comes in from
2129 // the top or right and doesn't have an intersection with another
2130 // street (and therefore another label) within the view.
2131 
2132               // Hash index is just the first
2133               // character.  Tried using lower 6 bits
2134               // of first two chars and lower 7 bits
2135               // of first two chars but the result was
2136               // slower than just using the first
2137               // character.
2138               hash_index = (uint8_t)(temp[0]);
2139 
2140               ptr2 = label_hash[hash_index];
2141               while (ptr2 != NULL)     // Step through the list
2142               {
2143                 // Check 2nd character (fast!)
2144                 if ( (uint8_t)(ptr2->label[1]) == (uint8_t)(temp[1]) )
2145                 {
2146                   if (strcasecmp(ptr2->label,temp) == 0)      // Found a match
2147                   {
2148                     //fprintf(stderr,"Found a match!\t%s\n",temp);
2149                     new_label = 0;
2150                     ptr2->found = ptr2->found + 1;  // Increment the "found" quantity
2151 
2152 // We change this "mod" number based on zoom level, so that long
2153 // strings don't overwrite each other, and so that we don't get too
2154 // many or too few labels drawn.  This will cause us to skip
2155 // intersections (the tiger files appear to have a label at each
2156 // intersection).  Between rural and urban areas, this method might
2157 // not work well.  Urban areas have few intersections, so we'll get
2158 // fewer labels drawn.
2159 // A better method might be to check the screen location for each
2160 // one and only write the strings if they are far enough apart, and
2161 // only count a string as written if the start of it is onscreen and
2162 // the angle is correct for it to be written on the screen.
2163 
2164                     // Draw a number of labels
2165                     // appropriate for the zoom
2166                     // level.
2167 // Labeling: Skip label logic
2168                     if ( ((ptr2->found - 1) % mod_number) != 0)
2169                     {
2170                       skip_label++;
2171                     }
2172                     ptr2 = NULL; // End the loop
2173                   }
2174                   else
2175                   {
2176                     ptr2 = ptr2->next;
2177                   }
2178                 }
2179                 else
2180                 {
2181                   ptr2 = ptr2->next;
2182                 }
2183               }
2184 
2185               if (!skip_label)    // Draw the string
2186               {
2187 
2188                 // Compute the label rotation angle
2189                 float diff_X = (int)x1 - x0;
2190                 float diff_Y = (int)y1 - y0;
2191                 float angle = 0.0;  // Angle for the beginning of this polyline
2192 
2193                 if (diff_X == 0.0)    // Avoid divide by zero errors
2194                 {
2195                   diff_X = 0.0000001;
2196                 }
2197                 angle = atan( diff_X / diff_Y );    // Compute in radians
2198                 // Convert to degrees
2199                 angle = angle / (2.0 * M_PI );
2200                 angle = angle * 360.0;
2201 
2202                 // Change to fit our rotate label function's idea of angle
2203                 angle = 360.0 - angle;
2204 
2205                 //fprintf(stderr,"Y: %f\tX: %f\tAngle: %f ==> ",diff_Y,diff_X,angle);
2206 
2207                 if ( angle > 90.0 )
2208                 {
2209                   angle += 180.0;
2210                 }
2211                 if ( angle >= 360.0 )
2212                 {
2213                   angle -= 360.0;
2214                 }
2215 
2216                 //fprintf(stderr,"%f\t%s\n",angle,temp);
2217 
2218 // Labeling of polylines done here
2219 
2220 //                              (void)draw_label_text ( w, x, y, strlen(temp), colors[label_color], (char *)temp);
2221                 if (gps_flag)
2222                 {
2223                   (void)draw_rotated_label_text (w,
2224                                                  //(int)angle,
2225                                                  -90,    // Horizontal, easiest to read
2226                                                  x,
2227                                                  y,
2228                                                  strlen(temp),
2229                                                  colors[gps_color],
2230                                                  (char *)temp,
2231                                                  font_size);
2232                 }
2233                 else
2234                 {
2235                   (void)draw_rotated_label_text(w,
2236                                                 (int)angle,
2237                                                 x,
2238                                                 y,
2239                                                 strlen(temp),
2240                                                 colors[label_color],
2241                                                 (char *)temp,
2242                                                 font_size);
2243                 }
2244               }
2245 
2246               if (new_label)
2247               {
2248 
2249                 // Create a new record for this string
2250                 // and add it to the head of the list.
2251                 // Make sure to "free" this linked
2252                 // list.
2253                 //fprintf(stderr,"Creating a new record: %s\n",temp);
2254                 ptr2 = (label_string *)malloc(sizeof(label_string));
2255                 CHECKMALLOC(ptr2);
2256 
2257                 memcpy(ptr2->label, temp, sizeof(ptr2->label));
2258                 ptr2->label[sizeof(ptr2->label)-1] = '\0';  // Terminate string
2259                 ptr2->found = 1;
2260 
2261                 // We use first character of string
2262                 // as our hash index.
2263                 hash_index = temp[0];
2264 
2265                 ptr2->next = label_hash[hash_index];
2266                 label_hash[hash_index] = ptr2;
2267                 //if (label_hash[hash_index]->next == NULL)
2268                 //    fprintf(stderr,"only one record\n");
2269               }
2270             }
2271           }
2272           break;
2273 
2274 
2275 
2276         case SHPT_POLYGON:
2277         case SHPT_POLYGONZ:
2278 
2279           if (debug_level & 16)
2280           {
2281             fprintf(stderr,"Found Polygons\n");
2282           }
2283 
2284           // User requested filled polygons with stippling.
2285           // Set the stipple now.  need to do here, because if
2286           // done earlier the labels get stippled, too.
2287           (void)XSetFillStyle(XtDisplay(w), gc, fill_style);
2288 
2289           if (draw_filled != 0 && fill_style == FillStippled)
2290           {
2291 
2292 
2293             switch (fill_stipple)
2294             {
2295               case 0:
2296                 (void)XSetStipple(XtDisplay(w), gc,
2297                                   pixmap_13pct_stipple);
2298                 break;
2299               case 1:
2300                 (void)XSetStipple(XtDisplay(w), gc,
2301                                   pixmap_25pct_stipple);
2302                 break;
2303               default:
2304                 (void)XSetStipple(XtDisplay(w), gc,
2305                                   pixmap_25pct_stipple);
2306                 break;
2307             }
2308           }
2309 
2310           // Each polygon can be made up of multiple
2311           // rings, and each ring has multiple points that
2312           // define it.  We hit this case once for each
2313           // polygon shape in the file, iff at least part
2314           // of that shape is within our viewport.
2315 
2316 // We now handle the "hole" drawing in polygon shapefiles, where
2317 // clockwise direction around the ring means a fill, and CCW means a
2318 // hole in the polygon.
2319 //
2320 // Possible implementations:
2321 //
2322 // 1) Snag an algorithm for a polygon "fill" function from
2323 // somewhere, but add a piece that will check for being inside a
2324 // "hole" polygon and just not draw while traversing it (change the
2325 // pen color to transparent over the holes?).
2326 // SUMMARY: How to do this?
2327 //
2328 // 2) Draw to another layer, then copy only the filled pixels to
2329 // their final destination pixmap.
2330 // SUMMARY: Draw polygons once, then a copy operation.
2331 //
2332 // 3) Separate area:  Draw polygon.  Copy from other map layer into
2333 // holes, then copy the result back.
2334 // SUMMARY:  How to determine outline?
2335 //
2336 // 4) Use clip-masks to prevent drawing over the hole areas:  Draw
2337 // to another 1-bit pixmap or region.  This area can be the size of
2338 // the max shape extents.  Filled = 1.  Holes = 0.  Use that pixmap
2339 // as a clip-mask to draw the polygons again onto the final pixmap.
2340 // SUMMARY: We end up drawing the shape twice!
2341 //
2342 // MODIFICATION:  Draw a filled rectangle onto the map pixmap using
2343 // the clip-mask to control where it actually gets drawn.
2344 // SUMMARY: Only end up drawing the shape once.
2345 //
2346 // 5) Inverted clip-mask:  Draw just the holes to a separate pixmap:
2347 // Create a pixmap filled with 1's (XFillRectangle & GXset).  Draw
2348 // the holes and use GXinvert to draw zero's to the mask where the
2349 // holes go.  Use this as a clip-mask to draw the filled areas of
2350 // the polygon to the map pixmap.
2351 // SUMMARY: Faster than methods 1-4?
2352 //
2353 // 6) Use Regions to do the same method as #5 but with more ease.
2354 // Create a polygon Region, then create a Region for each hole and
2355 // subtract the hole from the polygon Region.  Once we have a
2356 // complete polygon + holes, use that as the clip-mask for drawing
2357 // the real polygon.  Use XSetRegion() on the GC to set this up.  We
2358 // might have to do offsets as well so that the region maps properly
2359 // to our map pixmap when we draw the final polygon to it.
2360 // SUMMARY:  Should be faster than methods 1-5.
2361 //
2362 // 7) Do method 6 but instead of drawing a polygon region, draw a
2363 // rectangle region first, then knock holes in it.  Use that region
2364 // as the clip-mask for the XFillPolygon() later by calling
2365 // XSetRegion() on the GC.  We don't really need a polygon region
2366 // for a clip-mask.  A rectangle with holes in it will work just as
2367 // well and should be faster overall.  We might have to do offsets
2368 // as well so that the region maps properly to our map pixmap when
2369 // we draw the final polygon to it.
2370 // SUMMARY:  This might be the fastest method of the ones listed, if
2371 // drawing a rectangle region is faster than drawing a polygon
2372 // region.  We draw the polygon once here instead of twice, and each
2373 // hole only once.  The only added drawing time would be the
2374 // creation of the rectangle region, which should be fairly fast,
2375 // and the subtracting of the hole regions from it.
2376 //
2377 //
2378 // Shapefiles also allow identical points to be next to each other
2379 // in the vertice list.  We should look for that and get rid of
2380 // duplicate vertices.
2381 
2382 
2383 //if (object->nParts > 1)
2384 //fprintf(stderr,"Number of parts: %d\n", object->nParts);
2385 
2386 
2387 // Unfortunately, for Polygon shapes we must make one pass through
2388 // the entire set of rings to see if we have any "hole" rings (as
2389 // opposed to "fill" rings).  If we have any "hole" rings, we must
2390 // handle the drawing of the Shape quite differently.
2391 //
2392 // Read the vertices for each ring in the Shape.  Test whether we
2393 // have a hole ring.  If so, save the ring index away for the next
2394 // step when we actually draw the shape.
2395 
2396           polygon_hole_flag = 0;
2397 
2398           // Allocate storage for a flag for each ring in
2399           // this Shape.
2400           // !!Remember to free this storage later!!
2401           polygon_hole_storage = (int *)malloc(object->nParts*sizeof(int));
2402           CHECKMALLOC(polygon_hole_storage);
2403 
2404 // Run through the entire shape (all rings of it) once.  Create an
2405 // array of flags that specify whether each ring is a fill or a
2406 // hole.  If any holes found, set the global polygon_hole_flag as
2407 // well.
2408 
2409           for (ring = 0; ring < object->nParts; ring++ )
2410           {
2411 
2412             // Testing for fill or hole ring.  This will
2413             // determine how we ultimately draw the
2414             // entire shape.
2415             //
2416             switch ( shape_ring_direction( object, ring) )
2417             {
2418               case  0:    // Error in trying to compute whether fill or hole
2419                 fprintf(stderr,"Error in computing fill/hole ring\n");
2420               /* Falls through. */
2421               case  1:    // It's a fill ring
2422                 // Do nothing for these two cases
2423                 // except clear the flag in our
2424                 // storage
2425                 polygon_hole_storage[ring] = 0;
2426                 break;
2427               case -1:    // It's a hole ring
2428                 // Add it to our list of hole rings
2429                 // here and set a flag.  That way we
2430                 // won't have to run through
2431                 // SHPRingDir_2d again in the next
2432                 // loop.
2433                 polygon_hole_flag++;
2434                 polygon_hole_storage[ring] = 1;
2435 //                                fprintf(stderr, "Ring %d/%d is a polygon hole\n",
2436 //                                    ring,
2437 //                                    object->nParts);
2438                 break;
2439             }
2440           }
2441 // We're done with the initial run through the vertices of all
2442 // rings.  We now know which rings are fills and which are holes and
2443 // have recorded the data.
2444 
2445 // Speedup:  Only loop through the vertices once, by determining
2446 // hole/fill polygons as we go.
2447 
2448 
2449 //WE7U3
2450           // Speedup:  If not draw_filled, then don't go
2451           // through the math and region code.  Set the
2452           // flag to zero so that we won't do all the math
2453           // and the regions.
2454           if (!map_color_fill || !draw_filled)
2455           {
2456             polygon_hole_flag = 0;
2457           }
2458 
2459           if (polygon_hole_flag)
2460           {
2461             XRectangle rectangle;
2462             long width, height;
2463             double top_ll, left_ll, bottom_ll, right_ll;
2464             int temp_ok;
2465 
2466 
2467 //                        fprintf(stderr, "%s:Found %d hole rings in shape %d\n",
2468 //                            file,
2469 //                            polygon_hole_flag,
2470 //                            structure);
2471 
2472 //WE7U3
2473 ////////////////////////////////////////////////////////////////////////
2474 
2475 // Now that we know which are fill/hole rings, worry about drawing
2476 // each ring of the Shape:
2477 //
2478 // 1) Create a filled rectangle region, probably the size of the
2479 // Shape extents, and at the same screen coordinates as the entire
2480 // shape would normally be drawn.
2481 //
2482 // 2) Create a region for each hole ring and subtract these new
2483 // regions one at a time from the rectangle region created above.
2484 //
2485 // 3) When the "swiss-cheese" rectangle region is complete, draw
2486 // only the filled polygons onto the map pixmap using the
2487 // swiss-cheese rectangle region as the clip-mask.  Use a temporary
2488 // GC for this operation, as I can't find a way to remove a
2489 // clip-mask from a GC.  We may have to use offsets to make this
2490 // work properly.
2491 
2492 
2493             // Create three regions and rotate between
2494             // them, due to the XSubtractRegion()
2495             // needing three parameters.  If we later
2496             // find that two of the parameters can be
2497             // repeated, we can simplify our code.
2498             // We'll rotate through them mod 3.
2499 
2500             temp_region1 = 0;
2501 
2502             // Create empty region
2503             region[temp_region1] = XCreateRegion();
2504 
2505             // Draw a rectangular clip-mask inside the
2506             // Region.  Use the same extents as the full
2507             // Shape.
2508 
2509             // Set up the real sizes from the Shape
2510             // extents.
2511             top_ll    = object->dfYMax;
2512             left_ll   = object->dfXMin;
2513             bottom_ll = object->dfYMin;
2514             right_ll  = object->dfXMax;
2515 
2516             // Convert point to Xastir coordinates:
2517             temp_ok = convert_to_xastir_coordinates(&my_long,
2518                                                     &my_lat,
2519                                                     left_ll,
2520                                                     top_ll);
2521             //fprintf(stderr,"%ld %ld\n", my_long, my_lat);
2522 
2523             if (!temp_ok)
2524             {
2525               fprintf(stderr,"draw_shapefile_map4: Problem converting from lat/lon\n");
2526               ok = 0;
2527               x = 0;
2528               y = 0;
2529             }
2530             else
2531             {
2532               // Convert to screen coordinates.  Careful
2533               // here!  The format conversions you'll need
2534               // if you try to compress this into two
2535               // lines will get you into trouble.
2536               x = my_long - NW_corner_longitude;
2537               y = my_lat - NW_corner_latitude;
2538               x = x / scale_x;
2539               y = y / scale_y;
2540 
2541 
2542               // Here we check for really wacko points that will cause problems
2543               // with the X drawing routines, and fix them.
2544               if (x > 1700l)
2545               {
2546                 x = 1700l;
2547               }
2548               if (x <    0l)
2549               {
2550                 x =    0l;
2551               }
2552               if (y > 1700l)
2553               {
2554                 y = 1700l;
2555               }
2556               if (y <    0l)
2557               {
2558                 y =    0l;
2559               }
2560             }
2561 
2562             // Convert points to Xastir coordinates
2563             temp_ok = convert_to_xastir_coordinates(&my_long,
2564                                                     &my_lat,
2565                                                     right_ll,
2566                                                     bottom_ll);
2567             //fprintf(stderr,"%ld %ld\n", my_long, my_lat);
2568 
2569             if (!temp_ok)
2570             {
2571               fprintf(stderr,"draw_shapefile_map5: Problem converting from lat/lon\n");
2572               ok = 0;
2573               width = 0;
2574               height = 0;
2575             }
2576             else
2577             {
2578               // Convert to screen coordinates.  Careful
2579               // here!  The format conversions you'll need
2580               // if you try to compress this into two
2581               // lines will get you into trouble.
2582               width = my_long - NW_corner_longitude;
2583               height = my_lat - NW_corner_latitude;
2584               width = width / scale_x;
2585               height = height / scale_y;
2586 
2587               // Here we check for really wacko points that will cause problems
2588               // with the X drawing routines, and fix them.
2589               if (width  > 1700l)
2590               {
2591                 width  = 1700l;
2592               }
2593               if (width  <    1l)
2594               {
2595                 width  =    1l;
2596               }
2597               if (height > 1700l)
2598               {
2599                 height = 1700l;
2600               }
2601               if (height <    1l)
2602               {
2603                 height =    1l;
2604               }
2605             }
2606 
2607 //TODO
2608 // We can run into trouble here because we only have 16-bit values
2609 // to work with.  If we're zoomed in and the Shape is large in
2610 // comparison to the screen, we'll easily exceed these numbers.
2611 // Perhaps we'll need to work with something other than screen
2612 // coordinates?  Perhaps truncating the values will be adequate.
2613 
2614             rectangle.x      = (short) x;
2615             rectangle.y      = (short) y;
2616             rectangle.width  = (unsigned short) width;
2617             rectangle.height = (unsigned short) height;
2618 
2619 //fprintf(stderr,"*** Rectangle: %d,%d %dx%d\n", rectangle.x, rectangle.y, rectangle.width, rectangle.height);
2620 
2621             // Create the initial region containing a
2622             // filled rectangle.
2623             XUnionRectWithRegion(&rectangle,
2624                                  region[temp_region1],
2625                                  region[temp_region1]);
2626 
2627             // Create a region for each set of hole
2628             // vertices (CCW rotation of the vertices)
2629             // and subtract each from the rectangle
2630             // region.
2631             for (ring = 0; ring < object->nParts; ring++ )
2632             {
2633               int endpoint;
2634               int on_screen;
2635 
2636 
2637               if (polygon_hole_storage[ring] == 1)
2638               {
2639                 // It's a hole polygon.  Cut the
2640                 // hole out of our rectangle region.
2641                 int num_vertices = 0;
2642 //                              int nVertStart;
2643 
2644 
2645 //                              nVertStart = object->panPartStart[ring];
2646 
2647                 if( ring == object->nParts-1 )
2648                   num_vertices = object->nVertices
2649                                  - object->panPartStart[ring];
2650                 else
2651                   num_vertices = object->panPartStart[ring+1]
2652                                  - object->panPartStart[ring];
2653 
2654 //TODO
2655 // Snag the vertices and put them into the "points" array,
2656 // converting to screen coordinates as we go, then subtracting the
2657 // starting point, so that the regions remain small?
2658 //
2659 // We could either subtract the starting point of each shape from
2660 // each point, or take the hit on region size and just use the full
2661 // screen size (or whatever part of it the shape required plus the
2662 // area from the starting point to 0,0).
2663 
2664 
2665                 if ( (ring+1) < object->nParts)
2666                 {
2667                   endpoint = object->panPartStart[ring+1];
2668                 }
2669                 //else endpoint = object->nVertices;
2670                 else
2671                 {
2672                   endpoint = object->panPartStart[0] + object->nVertices;
2673                 }
2674                 //fprintf(stderr,"Endpoint %d\n", endpoint);
2675                 //fprintf(stderr,"Vertices: %d\n", endpoint - object->panPartStart[ring]);
2676 
2677                 i = 0;  // i = Number of points to draw for one ring
2678                 on_screen = 0;
2679 
2680                 // index = ptr into the shapefile's array of points
2681                 for (index = object->panPartStart[ring]; index < endpoint; )
2682                 {
2683                   int temp_ok;
2684 
2685 
2686                   // Get vertice and convert to Xastir coordinates
2687                   temp_ok = convert_to_xastir_coordinates(&my_long,
2688                                                           &my_lat,
2689                                                           (float)object->padfX[index],
2690                                                           (float)object->padfY[index]);
2691 
2692                   //fprintf(stderr,"%lu %lu\t", my_long, my_lat);
2693 
2694                   if (!temp_ok)
2695                   {
2696                     fprintf(stderr,"draw_shapefile_map6: Problem converting from lat/lon\n");
2697                     ok = 0;
2698                     x = 0;
2699                     y = 0;
2700                   }
2701                   else
2702                   {
2703                     // Convert to screen coordinates.  Careful
2704                     // here!  The format conversions you'll need
2705                     // if you try to compress this into two
2706                     // lines will get you into trouble.
2707                     x = my_long - NW_corner_longitude;
2708                     y = my_lat - NW_corner_latitude;
2709                     x = x / scale_x;
2710                     y = y / scale_y;
2711 
2712 
2713                     //fprintf(stderr,"%ld %ld\t\t", x, y);
2714 
2715                     // Here we check for really
2716                     // wacko points that will
2717                     // cause problems with the X
2718                     // drawing routines, and fix
2719                     // them.  Increment
2720                     // on_screen if any of the
2721                     // points might be on
2722                     // screen.
2723                     if (x >  1700l)
2724                     {
2725                       x =  1700l;
2726                     }
2727                     else if (x < 0l)
2728                     {
2729                       x = 0l;
2730                     }
2731                     else
2732                     {
2733                       on_screen++;
2734                     }
2735 
2736                     if (y >  1700l)
2737                     {
2738                       y =  1700l;
2739                     }
2740                     else if (y < 0l)
2741                     {
2742                       y = 0l;
2743                     }
2744                     else
2745                     {
2746                       on_screen++;
2747                     }
2748 
2749                     points[i].x = l16(x);
2750                     points[i].y = l16(y);
2751 
2752                     if (on_screen)
2753                     {
2754 //    fprintf(stderr,"%d x:%d y:%d\n",
2755 //        i,
2756 //        points[i].x,
2757 //        points[i].y);
2758                     }
2759                     i++;
2760                   }
2761                   index++;
2762 
2763                   if (index > high_water_mark_index)
2764                   {
2765                     high_water_mark_index = index;
2766                   }
2767 
2768                   if (index > endpoint)
2769                   {
2770                     index = endpoint;
2771                     fprintf(stderr,"Trying to run past the end of shapefile array: index=%d\n",index);
2772                   }
2773                 }   // End of converting vertices for a ring
2774 
2775 
2776                 // Create and subtract the region
2777                 // only if it might be on screen.
2778                 if (on_screen)
2779                 {
2780                   temp_region2 = (temp_region1 + 1) % 3;
2781                   temp_region3 = (temp_region1 + 2) % 3;
2782 
2783                   // Create empty regions.
2784                   region[temp_region2] = XCreateRegion();
2785                   region[temp_region3] = XCreateRegion();
2786 
2787                   // Draw the hole polygon
2788                   if (num_vertices >= 3)
2789                   {
2790                     XDestroyRegion(region[temp_region2]); // Release the old
2791                     region[temp_region2] = XPolygonRegion(points,
2792                                                           num_vertices,
2793                                                           WindingRule);
2794                   }
2795                   else
2796                   {
2797                     fprintf(stderr,
2798                             "draw_shapefile_map:XPolygonRegion with too few vertices:%d\n",
2799                             num_vertices);
2800                   }
2801 
2802                   // Subtract region2 from region1 and
2803                   // put the result into region3.
2804 //fprintf(stderr, "Subtracting region\n");
2805                   XSubtractRegion(region[temp_region1],
2806                                   region[temp_region2],
2807                                   region[temp_region3]);
2808 
2809                   // Get rid of the two regions we no
2810                   // longer need
2811                   XDestroyRegion(region[temp_region1]);
2812                   XDestroyRegion(region[temp_region2]);
2813 
2814                   // Indicate the final result region for
2815                   // the next iteration or the exit of the
2816                   // loop.
2817                   temp_region1 = temp_region3;
2818                 }
2819               }
2820             }
2821 
2822             // region[temp_region1] now contains a
2823             // clip-mask of the original polygon with
2824             // holes cut out of it (swiss-cheese
2825             // rectangle).
2826 
2827             // Create temporary GC.  It looks like we
2828             // don't need this to create the regions,
2829             // but we'll need it when we draw the filled
2830             // polygons onto the map pixmap using the
2831             // final region as a clip-mask.
2832 
2833 // Offsets?
2834 // XOffsetRegion
2835 
2836 //                        gc_temp_values.function = GXcopy;
2837             gc_temp = XCreateGC(XtDisplay(w),
2838                                 XtWindow(w),
2839                                 0,
2840                                 &gc_temp_values);
2841             // now copy the fill style and stipple from gc.
2842             XCopyGC(XtDisplay(w),
2843                     gc,
2844                     (GCFillStyle|GCStipple),
2845                     gc_temp);
2846 
2847             // Set the clip-mask into the GC.  This GC
2848             // is now ruined for other purposes, so
2849             // destroy it when we're done drawing this
2850             // one shape.
2851             XSetRegion(XtDisplay(w), gc_temp, region[temp_region1]);
2852             XDestroyRegion(region[temp_region1]);
2853           }
2854 //WE7U3
2855 ////////////////////////////////////////////////////////////////////////
2856 
2857 
2858           // Read the vertices for each ring in this Shape
2859           for (ring = 0; ring < object->nParts; ring++ )
2860           {
2861             int endpoint;
2862             //fprintf(stderr,"Ring: %d\t\t", ring);
2863 
2864             if ( (ring+1) < object->nParts)
2865             {
2866               endpoint = object->panPartStart[ring+1];
2867             }
2868             //else endpoint = object->nVertices;
2869             else
2870             {
2871               endpoint = object->panPartStart[0] + object->nVertices;
2872             }
2873 
2874             //fprintf(stderr,"Endpoint %d\n", endpoint);
2875             //fprintf(stderr,"Vertices: %d\n", endpoint - object->panPartStart[ring]);
2876 
2877             i = 0;  // i = Number of points to draw for one ring
2878             // index = ptr into the shapefile's array of points
2879             for (index = object->panPartStart[ring]; index < endpoint; )
2880             {
2881               int temp_ok;
2882 
2883               ok = 1;
2884 
2885               //fprintf(stderr,"\t%d:%g %g\t", index, object->padfX[index], object->padfY[index] );
2886 
2887               // Get vertice and convert to Xastir coordinates
2888               temp_ok = convert_to_xastir_coordinates(&my_long,
2889                                                       &my_lat,
2890                                                       (float)object->padfX[index],
2891                                                       (float)object->padfY[index]);
2892 
2893               //fprintf(stderr,"%lu %lu\t", my_long, my_lat);
2894 
2895               if (!temp_ok)
2896               {
2897                 fprintf(stderr,"draw_shapefile_map7: Problem converting from lat/lon\n");
2898                 ok = 0;
2899                 x = 0;
2900                 y = 0;
2901                 index++;
2902               }
2903               else
2904               {
2905                 // Convert to screen coordinates.  Careful
2906                 // here!  The format conversions you'll need
2907                 // if you try to compress this into two
2908                 // lines will get you into trouble.
2909                 x = my_long - NW_corner_longitude;
2910                 y = my_lat - NW_corner_latitude;
2911                 x = x / scale_x;
2912                 y = y / scale_y;
2913 
2914 
2915                 //fprintf(stderr,"%ld %ld\t\t", x, y);
2916 
2917                 // Here we check for really wacko points that will cause problems
2918                 // with the X drawing routines, and fix them.
2919                 points[i].x = l16(x);
2920                 points[i].y = l16(y);
2921                 i++;    // Number of points to draw
2922 
2923                 if (i > high_water_mark_i)
2924                 {
2925                   high_water_mark_i = i;
2926                 }
2927 
2928 
2929                 if (i >= MAX_MAP_POINTS)
2930                 {
2931                   i = MAX_MAP_POINTS - 1;
2932                   fprintf(stderr,"Trying to run past the end of our internal points array: i=%d\n",i);
2933                 }
2934 
2935                 //fprintf(stderr,"%d %d\t", points[i].x, points[i].y);
2936 
2937                 index++;
2938 
2939                 if (index > high_water_mark_index)
2940                 {
2941                   high_water_mark_index = index;
2942                 }
2943 
2944                 if (index > endpoint)
2945                 {
2946                   index = endpoint;
2947                   fprintf(stderr,"Trying to run past the end of shapefile array: index=%d\n",index);
2948                 }
2949               }
2950             }
2951 
2952             if ( (i >= 3)
2953                  && (ok_to_draw && !skip_it)
2954                  && ( !draw_filled || !map_color_fill || (draw_filled && polygon_hole_storage[ring] == 0) ) )
2955             {
2956               // We have a polygon to draw!
2957 //WE7U3
2958               if ((!draw_filled || !map_color_fill) && polygon_hole_storage[ring] == 1)
2959               {
2960                 // We have a hole drawn as unfilled.
2961                 // Draw as a black dashed line.
2962                 (void)XSetForeground(XtDisplay(w), gc, colors[color]);
2963                 (void)XSetLineAttributes (XtDisplay (w),
2964                                           gc,
2965                                           0,
2966                                           LineOnOffDash,
2967                                           CapButt,
2968                                           JoinMiter);
2969                 (void)XDrawLines(XtDisplay(w),
2970                                  pixmap,
2971                                  gc,
2972                                  points,
2973                                  l16(i),
2974                                  CoordModeOrigin);
2975                 (void)XSetLineAttributes (XtDisplay (w),
2976                                           gc,
2977                                           0,
2978                                           LineSolid,
2979                                           CapButt,
2980                                           JoinMiter);
2981               }
2982               else if (quad_overlay_flag)
2983               {
2984                 (void)XDrawLines(XtDisplay(w),
2985                                  pixmap,
2986                                  gc,
2987                                  points,
2988                                  l16(i),
2989                                  CoordModeOrigin);
2990               }
2991               /* old glacier, lake and river code was identical
2992                  with the exception of what color to use! */
2993               else if (!weather_alert_flag)
2994               {
2995                 /* color is already set by dbfawk(?) */
2996                 /* And so are lanes and pattern.  Let's
2997                    use what was specified. */
2998                 (void)XSetLineAttributes(XtDisplay(w),
2999                                          gc,
3000                                          (lanes)?lanes:1,
3001                                          pattern,
3002                                          CapButt,
3003                                          JoinMiter);
3004                 (void)XSetForeground(XtDisplay(w), gc, colors[color]);
3005                 if (map_color_fill && draw_filled)
3006                 {
3007                   if (polygon_hole_flag)
3008                   {
3009                     (void)XSetForeground(XtDisplay(w), gc_temp, colors[fill_color]);
3010                     if (i >= 3)
3011                     {
3012                       (void)XFillPolygon(XtDisplay(w),
3013                                          pixmap,
3014                                          gc_temp,
3015                                          points,
3016                                          i,
3017                                          Nonconvex,
3018                                          CoordModeOrigin);
3019                     }
3020                     else
3021                     {
3022                       fprintf(stderr,
3023                               "draw_shapefile_map:Too few points:%d, Skipping XFillPolygon()",
3024                               npoints);
3025                     }
3026                   }
3027                   else   /* no holes in this polygon */
3028                   {
3029                     if (i >= 3)
3030                     {
3031                       /* draw the filled polygon */
3032                       (void)XSetForeground(XtDisplay(w), gc, colors[fill_color]);
3033                       (void)XFillPolygon(XtDisplay(w),
3034                                          pixmap,
3035                                          gc,
3036                                          points,
3037                                          i,
3038                                          Nonconvex,
3039                                          CoordModeOrigin);
3040                     }
3041                     else
3042                     {
3043                       fprintf(stderr,
3044                               "draw_shapefile_map:Too few points:%d, Skipping XFillPolygon()",
3045                               npoints);
3046                     }
3047                   }
3048                 }
3049                 /* draw the polygon border */
3050                 (void)XSetForeground(XtDisplay(w), gc, colors[color]);
3051                 (void)XSetFillStyle(XtDisplay(w), gc, FillSolid);
3052                 (void)XDrawLines(XtDisplay(w),
3053                                  pixmap,
3054                                  gc,
3055                                  points,
3056                                  l16(i),
3057                                  CoordModeOrigin);
3058               }
3059               else if (weather_alert_flag)
3060               {
3061                 (void)XSetFillStyle(XtDisplay(w), gc_tint, FillStippled);
3062 // We skip the hole/fill thing for these?
3063 
3064                 if (i >= 3)
3065                 {
3066                   (void)XFillPolygon(XtDisplay(w),
3067                                      pixmap_alerts,
3068                                      gc_tint,
3069                                      points,
3070                                      i,
3071                                      Nonconvex,
3072                                      CoordModeOrigin);
3073                 }
3074                 else
3075                 {
3076                   fprintf(stderr,
3077                           "draw_shapefile_map:Too few points:%d, Skipping XFillPolygon()",
3078                           npoints);
3079                 }
3080 
3081                 (void)XSetFillStyle(XtDisplay(w), gc_tint, FillSolid);
3082                 (void)XDrawLines(XtDisplay(w),
3083                                  pixmap_alerts,
3084                                  gc_tint,
3085                                  points,
3086                                  l16(i),
3087                                  CoordModeOrigin);
3088               }
3089               else if (map_color_fill && draw_filled)    // Land masses?
3090               {
3091                 if (polygon_hole_flag)
3092                 {
3093                   (void)XSetForeground(XtDisplay(w), gc_temp, colors[fill_color]);
3094                   if (i >= 3)
3095                   {
3096                     (void)XFillPolygon(XtDisplay(w),
3097                                        pixmap,
3098                                        gc_temp,
3099                                        points,
3100                                        i,
3101                                        Nonconvex,
3102                                        CoordModeOrigin);
3103                   }
3104                   else
3105                   {
3106                     fprintf(stderr,
3107                             "draw_shapefile_map:Too few points:%d, Skipping XFillPolygon()",
3108                             npoints);
3109                   }
3110                 }
3111                 else   /* no polygon hole */
3112                 {
3113                   (void)XSetForeground(XtDisplay(w), gc, colors[fill_color]);
3114                   if (i >= 3)
3115                   {
3116                     (void)XFillPolygon(XtDisplay (w),
3117                                        pixmap,
3118                                        gc,
3119                                        points,
3120                                        i,
3121                                        Nonconvex,
3122                                        CoordModeOrigin);
3123                   }
3124                   else
3125                   {
3126                     fprintf(stderr,
3127                             "draw_shapefile_map:Too few points:%d, Skipping XFillPolygon()",
3128                             npoints);
3129                   }
3130                 }
3131 
3132                 (void)XSetForeground(XtDisplay(w), gc, colors[color]); // border color
3133 
3134                 // Draw a thicker border for city boundaries
3135                 (void)XSetForeground(XtDisplay(w), gc, colors[color]); // border
3136                 (void)XSetFillStyle(XtDisplay(w), gc, FillSolid);
3137 
3138                 (void)XDrawLines(XtDisplay(w),
3139                                  pixmap,
3140                                  gc,
3141                                  points,
3142                                  l16(i),
3143                                  CoordModeOrigin);
3144               }
3145               else    // Use whatever color is defined by this point.
3146               {
3147                 (void)XSetLineAttributes(XtDisplay(w), gc, 0, LineSolid, CapButt,JoinMiter);
3148                 (void)XSetFillStyle(XtDisplay(w), gc, FillSolid);
3149                 (void)XDrawLines(XtDisplay(w),
3150                                  pixmap,
3151                                  gc,
3152                                  points,
3153                                  l16(i),
3154                                  CoordModeOrigin);
3155               }
3156             }
3157           }
3158           // Free the storage that we allocated to hold
3159           // the "hole" flags for the shape.
3160           free(polygon_hole_storage);
3161 
3162           if (polygon_hole_flag)
3163           {
3164             //Free the temporary GC that we may have used to
3165             //draw polygons using the clip-mask:
3166             XFreeGC(XtDisplay(w), gc_temp);
3167           }
3168 
3169 
3170 ////////////////////////////////////////////////////////////////////////////////////////////////////
3171 // Done with drawing shapes, now draw labels
3172 ////////////////////////////////////////////////////////////////////////////////////////////////////
3173 
3174           temp = name;
3175           // Set fill style back to defaults, or labels will get
3176           // stippled along with polygons!
3177           XSetFillStyle(XtDisplay(w), gc, FillSolid);
3178 
3179           /* XXX - figure out how to set this from dbfawk: */
3180           if (quad_overlay_flag)
3181           {
3182             if (fieldcount >= 5)
3183             {
3184               // Use just the last two characters of
3185               // the quad index.  "44072-A3" converts
3186               // to "A3"
3187               temp = DBFReadStringAttribute( hDBF, structure, 4 );
3188               xastir_snprintf(quad_label,
3189                               sizeof(quad_label),
3190                               "%s ",
3191                               &temp[strlen(temp) - 2]);
3192 
3193               // Append the name of the quad
3194               temp = DBFReadStringAttribute( hDBF, structure, 0 );
3195               strncat(quad_label,
3196                       temp,
3197                       sizeof(quad_label) - 1 - strlen(quad_label));
3198             }
3199             else
3200             {
3201               quad_label[0] = '\0';
3202             }
3203           }
3204 
3205           if ( (temp != NULL)
3206                && (strlen(temp) != 0)
3207                && map_labels
3208                && !skip_label )
3209           {
3210             int temp_ok;
3211 
3212             if (debug_level & 16)
3213             {
3214               fprintf(stderr,"I think I should display labels\n");
3215             }
3216             ok = 1;
3217 
3218             // Convert to Xastir coordinates:
3219             // If quad overlay shapefile, need to
3220             // snag the label coordinates from the DBF
3221             // file instead.  Note that the coordinates
3222             // are for the bottom right corner of the
3223             // quad, so we need to shift it left by 7.5'
3224             // to make the label appear inside the quad
3225             // (attached to the bottom left corner in
3226             // this case).
3227             /* XXX - figure out how to do WITH_DBFAWK: */
3228             if (quad_overlay_flag)
3229             {
3230               const char *dbf_temp;
3231               float lat_f;
3232               float lon_f;
3233 
3234               if (fieldcount >= 4)
3235               {
3236                 dbf_temp = DBFReadStringAttribute( hDBF, structure, 2 );
3237                 if (1 != sscanf(dbf_temp, "%f", &lat_f))
3238                 {
3239                   fprintf(stderr,"draw_shapefile_map:sscanf parsing error\n");
3240                 }
3241                 dbf_temp = DBFReadStringAttribute( hDBF, structure, 3 );
3242                 if (1 != sscanf(dbf_temp, "%f", &lon_f))
3243                 {
3244                   fprintf(stderr,"draw_shapefile_map:sscanf parsing error\n");
3245                 }
3246                 lon_f = lon_f - 0.125;
3247               }
3248               else
3249               {
3250                 lat_f = 0.0;
3251                 lon_f = 0.0;
3252               }
3253 
3254               //fprintf(stderr,"Lat: %f, Lon: %f\t, Quad: %s\n", lat_f, lon_f, quad_label);
3255 
3256               temp_ok = convert_to_xastir_coordinates(&my_long,
3257                                                       &my_lat,
3258                                                       (float)lon_f,
3259                                                       (float)lat_f);
3260             }
3261             else    // Not quad overlay, use vertices
3262             {
3263               // XXX - todo: center large polygon labels in the center (e.g. county boundary)
3264               /* center label in polygon bounding box */
3265               temp_ok = convert_to_xastir_coordinates(&my_long,
3266                                                       &my_lat,
3267                                                       ((float)(object->dfXMax + object->dfXMin))/2.0,
3268                                                       ((float)(object->dfYMax + object->dfYMin))/2.0);
3269 #ifdef notdef
3270               temp_ok = convert_to_xastir_coordinates(&my_long,
3271                                                       &my_lat,
3272                                                       (float)object->padfX[0],
3273                                                       (float)object->padfY[0]);
3274 #endif  // notdef
3275             }
3276             //fprintf(stderr,"%ld %ld\n", my_long, my_lat);
3277 
3278             if (!temp_ok)
3279             {
3280               fprintf(stderr,"draw_shapefile_map8: Problem converting from lat/lon\n");
3281               ok = 0;
3282               x = 0;
3283               y = 0;
3284             }
3285             else
3286             {
3287               // Convert to screen coordinates.  Careful
3288               // here!  The format conversions you'll need
3289               // if you try to compress this into two
3290               // lines will get you into trouble.
3291               x = my_long - NW_corner_longitude;
3292               y = my_lat - NW_corner_latitude;
3293               x = x / scale_x;
3294               y = y / scale_y;
3295 
3296 // Labeling of polygons done here
3297 
3298               if (ok == 1 && ok_to_draw)
3299               {
3300                 if (quad_overlay_flag)
3301                 {
3302                   draw_nice_string(w,
3303                                    pixmap,
3304                                    0,
3305                                    x+2,
3306                                    y-1,
3307                                    (char*)quad_label,
3308                                    0xf,
3309                                    0x10,
3310                                    strlen(quad_label));
3311                 }
3312                 else
3313                 {
3314 #ifdef notdef
3315                   (void)draw_label_text ( w,
3316                                           x,
3317                                           y,
3318                                           strlen(temp),
3319                                           colors[label_color],
3320                                           (char *)temp);
3321 #endif  // notdef
3322                   if (debug_level & 16)
3323                   {
3324                     fprintf(stderr,
3325                             "  displaying label %s with color %x\n",
3326                             temp,label_color);
3327                   }
3328                   (void)draw_centered_label_text(w,
3329                                                  -90,
3330                                                  x,
3331                                                  y,
3332                                                  strlen(temp),
3333                                                  colors[label_color],
3334                                                  (char *)temp,
3335                                                  font_size);
3336                 }
3337               }
3338             }
3339           }
3340           break;
3341 
3342         case SHPT_MULTIPOINT:
3343         case SHPT_MULTIPOINTZ:
3344           // Not implemented.
3345           fprintf(stderr,"Shapefile Multi-Point format files aren't supported!\n");
3346           break;
3347 
3348         default:
3349           // Not implemented.
3350           fprintf(stderr,"Shapefile format not supported: Subformat unknown (default clause of switch)!\n");
3351           break;
3352 
3353       }   // End of switch
3354     }
3355     SHPDestroyObject( object ); // Done with this structure
3356   }
3357 
3358 
3359   // Free our hash of label strings, if any.  Each hash entry may
3360   // have a linked list attached below it.
3361   for (i = 0; i < 256; i++)
3362   {
3363     ptr2 = label_hash[i];
3364     while (ptr2 != NULL)
3365     {
3366       label_hash[i] = ptr2->next;
3367       //fprintf(stderr,"free: %s\n",ptr2->label);
3368       free(ptr2);
3369       ptr2 = label_hash[i];
3370     }
3371   }
3372 
3373   dbfawk_free_info(fld_info);
3374   if (sig_info != NULL && sig_info != dbfawk_default_sig  && (sig_info->sig == NULL))
3375   {
3376     dbfawk_free_sigs(sig_info);
3377   }
3378 
3379   DBFClose( hDBF );
3380   SHPClose( hSHP );
3381 
3382 //    XmUpdateDisplay (XtParent (da));
3383 
3384   if (debug_level & 16)
3385   {
3386     fprintf(stderr,"High-Mark Index:%d,\tHigh-Mark i:%d\n",
3387             high_water_mark_index,
3388             high_water_mark_i);
3389   }
3390 
3391   // Set fill style back to defaults
3392   XSetFillStyle(XtDisplay(w), gc, FillSolid);
3393 }
3394 // End of draw_shapefile_map()
3395 
3396 
3397 
3398 
3399 
3400 
3401 // This function will delete any pre-loaded dbfawk sigs and clear Dbf_sigs
3402 // This will trigger a  reload the first time a shapfile is redisplayed
clear_dbfawk_sigs(void)3403 void clear_dbfawk_sigs(void)
3404 {
3405   //    fprintf(stderr,"Clearing signatures.\n");
3406   if (Dbf_sigs )
3407   {
3408     dbfawk_free_sigs(Dbf_sigs);
3409     Dbf_sigs = NULL;
3410   }
3411 }
3412 
3413 
3414 
3415 #endif  // HAVE_LIBSHP
3416 
3417 
3418