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