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 
25 #ifdef HAVE_CONFIG_H
26   #include "config.h"
27 #endif  // HAVE_CONFIG_H
28 
29 #include "snprintf.h"
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <pwd.h>
38 #include <errno.h>
39 
40 // Needed for Solaris
41 #ifdef HAVE_STRINGS_H
42   #include <strings.h>
43 #endif  // HAVE_STRINGS_H
44 
45 #include <dirent.h>
46 #include <netinet/in.h>
47 #include <Xm/XmAll.h>
48 
49 #ifdef HAVE_X11_XPM_H
50   #include <X11/xpm.h>
51   #ifdef HAVE_LIBXPM // if we have both, prefer the extra library
52     #undef HAVE_XM_XPMI_H
53   #endif // HAVE_LIBXPM
54 #endif // HAVE_X11_XPM_H
55 
56 #ifdef HAVE_XM_XPMI_H
57   #include <Xm/XpmI.h>
58 #endif // HAVE_XM_XPMI_H
59 
60 #include <X11/Xlib.h>
61 
62 #include <math.h>
63 
64 #include "xastir.h"
65 #include "maps.h"
66 #include "alert.h"
67 #include "fetch_remote.h"
68 #include "util.h"
69 #include "main.h"
70 #include "datum.h"
71 #include "draw_symbols.h"
72 #include "rotated.h"
73 #include "color.h"
74 #include "xa_config.h"
75 
76 #include "map_cache.h"
77 
78 #define CHECKMALLOC(m)  if (!m) { fprintf(stderr, "***** Malloc Failed *****\n"); exit(0); }
79 
80 #ifdef HAVE_MAGICK
81   #if TIME_WITH_SYS_TIME
82     #include <sys/time.h>
83     #include <time.h>
84   #else   // TIME_WITH_SYS_TIME
85     #if HAVE_SYS_TIME_H
86       #include <sys/time.h>
87     #else  // HAVE_SYS_TIME_H
88       #include <time.h>
89     #endif // HAVE_SYS_TIME_H
90   #endif  // TIME_WITH_SYS_TIME
91   #undef RETSIGTYPE
92   // TVR: "stupid ImageMagick"
93   // The problem is that magick/api.h includes Magick's config.h file, and that
94   // pulls in all the same autoconf-generated defines that we use.
95   // plays those games below, but I don't think in the end that they actually
96   // make usable macros with our own data in them.
97   // Fortunately, we don't need them, so I'll just undef the ones that are
98   // causing problems today.  See main.c for fixes that preserve our values.
99   #undef PACKAGE
100   #undef VERSION
101   /* JMT - stupid ImageMagick */
102   #define XASTIR_PACKAGE_BUGREPORT PACKAGE_BUGREPORT
103   #undef PACKAGE_BUGREPORT
104   #define XASTIR_PACKAGE_NAME PACKAGE_NAME
105   #undef PACKAGE_NAME
106   #define XASTIR_PACKAGE_STRING PACKAGE_STRING
107   #undef PACKAGE_STRING
108   #define XASTIR_PACKAGE_TARNAME PACKAGE_TARNAME
109   #undef PACKAGE_TARNAME
110   #define XASTIR_PACKAGE_VERSION PACKAGE_VERSION
111   #undef PACKAGE_VERSION
112   #ifdef HAVE_MAGICK
113     #ifdef HAVE_MAGICKCORE_MAGICKCORE_H
114       #include <MagickCore/MagickCore.h>
115     #else
116       #ifdef HAVE_MAGICK_API_H
117         #include <magick/api.h>
118       #endif // HAVE_MAGICK_API_H
119     #endif //HAVE_MAGICKCORE_MAGICKCORE_H
120   #endif //HAVE_MAGICK
121   #undef PACKAGE_BUGREPORT
122   #define PACKAGE_BUGREPORT XASTIR_PACKAGE_BUGREPORT
123   #undef XASTIR_PACKAGE_BUGREPORT
124   #undef PACKAGE_NAME
125   #define PACKAGE_NAME XASTIR_PACKAGE_NAME
126   #undef XASTIR_PACKAGE_NAME
127   #undef PACKAGE_STRING
128   #define PACKAGE_STRING XASTIR_PACKAGE_STRING
129   #undef XASTIR_PACKAGE_STRING
130   #undef PACKAGE_TARNAME
131   #define PACKAGE_TARNAME XASTIR_PACKAGE_TARNAME
132   #undef XASTIR_PACKAGE_TARNAME
133   #undef PACKAGE_VERSION
134   #define PACKAGE_VERSION XASTIR_PACKAGE_VERSION
135   #undef XASTIR_PACKAGE_VERSION
136 #endif // HAVE_MAGICK
137 
138 // Must be last include file
139 #include "leak_detection.h"
140 
141 
142 
143 /**********************************************************
144  * draw_WMS_map()
145  **********************************************************/
146 #ifdef HAVE_MAGICK
draw_WMS_map(Widget w,char * filenm,int destination_pixmap,char * URL,transparent_color_record * c_trans_color_head,int nocache)147 void draw_WMS_map (Widget w,
148                    char *filenm,
149                    int destination_pixmap,
150                    char *URL,
151                    transparent_color_record *c_trans_color_head,
152                    int nocache)    // If non-zero, don't use cached version
153 {
154 
155 
156   char file[MAX_FILENAME];        // Complete path/name of image file
157   char short_filenm[MAX_FILENAME];
158   FILE *f;                        // Filehandle of image file
159   char fileimg[MAX_FILENAME];     // Ascii name of image file, read from GEO file
160   char WMStmp[MAX_FILENAME*2];  // Used for putting together the WMS map query
161   int width, height;
162   tiepoint tp[2];                 // Calibration points for map, read in from .geo file
163   long map_c_T, map_c_L;          // map delta NW edge coordinates, DNN: these should be signed
164   long tp_c_dx, tp_c_dy;          // tiepoint coordinate differences
165   unsigned long c_x_min,  c_y_min;// top left coordinates of map inside screen
166 //  unsigned long c_y_max;          // bottom right coordinates of map inside screen
167   double c_x;                     // Xastir coordinates 1/100 sec, 0 = 180�W
168   double c_y;                     // Xastir coordinates 1/100 sec, 0 =  90�N
169 
170   long map_y_0;                   // map pixel pointer prior to TM adjustment
171   long map_x, map_y;              // map pixel pointers, DNN: this was a float, chg to long
172   long map_x_min, map_x_max;      // map boundaries for in screen part of map
173   long map_y_min, map_y_max;      //
174 //  long map_x_ctr;                 // half map width in pixel
175 //  long map_y_ctr;                 // half map height in pixel
176   int map_seen = 0;
177   int map_act;
178 //  int map_done;
179 
180 //  long map_c_yc;                  // map center, vert coordinate
181 //  long map_c_xc;                  // map center, hor  coordinate
182   double map_c_dx, map_c_dy;      // map coordinates increment (pixel width)
183   double c_dx;                    // adjusted map pixel width
184 
185   long scr_x,  scr_y;             // screen pixel plot positions
186   long scr_xp, scr_yp;            // previous screen plot positions
187   int  scr_dx, scr_dy;            // increments in screen plot positions
188 //  long scr_x_mc;                  // map center in screen units
189 
190 //  long scr_c_xr;
191 
192 //  long scale_xa;                  // adjusted for topo maps
193 //  double scale_x_nm;              // nm per Xastir coordinate unit
194 //  long scale_x0;                  // at widest map area
195 
196   char local_filename[MAX_FILENAME];
197   ExceptionInfo exception;
198   Image *image;
199   ImageInfo *image_info;
200   PixelPacket *pixel_pack;
201   PixelPacket temp_pack;
202   IndexPacket *index_pack;
203   int l;
204   XColor my_colors[256];
205   int trans_skip = 0;             // skip transparent pixel
206   double left, right, top, bottom, map_width, map_height;
207   double lat_center  = 0;
208   double long_center = 0;
209 
210   char map_it[MAX_FILENAME];
211   char tmpstr[100];
212   int geo_image_width;        // Image width  from GEO file
213   int geo_image_height;       // Image height from GEO file
214 
215   time_t query_start_time, query_end_time;
216 
217 #ifdef USE_MAP_CACHE
218   int map_cache_return;
219   char * cache_file_id;
220 #endif  // USE_MAP_CACHE
221 
222   char temp_file_path[MAX_VALUE];
223 
224   if (debug_level & 512)
225   {
226     if (nocache)
227     {
228       fprintf(stderr,"draw_WMS_map: NOCACHE selected\n");
229     }
230     else
231     {
232       fprintf(stderr,"draw_WMS_map: CACHING if enabled\n");
233     }
234   }
235 
236   // Create a shorter filename for display (one that fits the
237   // status line more closely).  Subtract the length of the
238   // "Indexing " and/or "Loading " strings as well.
239   if (strlen(filenm) > (41 - 9))
240   {
241     int avail = 41 - 11;
242     int new_len = strlen(filenm) - avail;
243 
244     xastir_snprintf(short_filenm,
245                     sizeof(short_filenm),
246                     "..%s",
247                     &filenm[new_len]);
248   }
249   else
250   {
251     xastir_snprintf(short_filenm,
252                     sizeof(short_filenm),
253                     "%s",
254                     filenm);
255   }
256 
257   xastir_snprintf(map_it,
258                   sizeof(map_it),
259                   langcode ("BBARSTA028"),
260                   short_filenm);
261   statusline(map_it,0);       // Loading ...
262 
263 
264 
265   // Check whether we're indexing or drawing the map
266   if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
267        || (destination_pixmap == INDEX_NO_TIMESTAMPS) )
268   {
269 
270     // We're indexing only.  Save the extents in the index.
271     // Force the extents to the edges of the earth for the
272     // index file.
273     index_update_xastir(filenm, // Filename only
274                         64800000l,      // Bottom
275                         0l,             // Top
276                         0l,             // Left
277                         129600000l,     // Right
278                         0);             // Default Map Level
279 
280     // Update statusline
281     xastir_snprintf(map_it,
282                     sizeof(map_it),
283                     langcode ("BBARSTA039"),
284                     short_filenm);
285     statusline(map_it,0);       // Loading/Indexing ...
286 
287     return; // Done indexing this file
288   }
289 
290 
291   // Tiepoint for upper left screen corner
292   //
293   tp[0].img_x = 0;                // Pixel Coordinates
294   tp[0].img_y = 0;                // Pixel Coordinates
295   tp[0].x_long = NW_corner_longitude;   // Xastir Coordinates
296   tp[0].y_lat  = NW_corner_latitude;    // Xastir Coordinates
297 
298 
299   // Tiepoint for lower right screen corner
300   //
301   // Here we must use scale_x for both directions because we have
302   // square pixels returned by the WMS server.
303 
304 
305 // Really what we want to do here is to change our bounding box for
306 // our request to fit square pixels, using scale_x for both
307 // dimensions, and to change our tiepoints to match.  WMS servers
308 // currently feed us back square pixels but the spec says that the
309 // servers should be capable of sending back rectangular pixels, so
310 // the images we get back may change if we don't request square
311 // pixels each time.
312 //
313 // TODO:  Change our imagesize, bounding rectangle requested, and
314 // tiepoints to fit square pixels and to use scale_x for both
315 // dimensions.
316 //
317 // Actually, looking at the changes that were made, it looks like we
318 // _are_ using square pixels and requesting a bounding box based on
319 // scale_x for both dimensions, so we might be good to go as-is.
320 
321 
322   //
323   tp[1].img_x =  screen_width - 1; // Pixel Coordinates
324   tp[1].img_y = screen_height - 1; // Pixel Coordinates
325   tp[1].x_long = SE_corner_longitude; // Xastir Coordinates
326 
327 // Modified to use same scale (scale_x) for both dimensions, square
328 // pixels.  Don't use SE_corner_latitude here as it uses scale_y!
329 //    tp[1].y_lat  =  NW_corner_latitude + ((screen_height) * scale_y); // Xastir Coordinates
330   tp[1].y_lat  =  NW_corner_latitude + ((screen_height) * scale_x); // Xastir Coordinates
331 
332 
333   // Again, use scale_x for both directions due to the square
334   // pixels returned from the WMS server.
335   //
336   left = (double)((NW_corner_longitude - 64800000l )/360000.0);   // Lat/long Coordinates
337   top = (double)(-((NW_corner_latitude - 32400000l )/360000.0));  // Lat/long Coordinates
338   right = (double)((SE_corner_longitude - 64800000l)/360000.0);//Lat/long Coordinates
339 
340 // Modified to use same scale (scale_x) for both dimensions, square
341 // pixels.  Don't use SE_corner_latitude here as it uses scale_y!
342 //    bottom = (double)(-(((NW_corner_latitude + ((screen_height) * scale_y) ) - 32400000l)/360000.0));//Lat/long Coordinates
343   bottom = (double)(-(((NW_corner_latitude + ((screen_height) * scale_x) ) - 32400000l)/360000.0));//Lat/long Coordinates
344 
345 
346   map_width = right - left;   // Lat/long Coordinates
347   map_height = top - bottom;  // Lat/long Coordinates
348 
349   geo_image_width  = screen_width;
350   geo_image_height = screen_height;
351 
352   long_center = (left + right)/2.0l;
353   lat_center  = (top + bottom)/2.0l;
354 
355 
356 //  Example query for a WMS map server....
357 //  xastir_snprintf(fileimg, sizeof(fileimg),
358 //  "\'http://mesonet.tamu.edu/cgi-bin/p-warn?SERVICE=WMS&VERSION=1.1.1&REQUEST=getmap&layers=radar&BBOX=-129.000,52.500,-111.000,42.500&HEIGHT=1000&WIDTH=1800&FORMAT=image/png\'");
359 
360 
361 //    xastir_snprintf(WMStmp, sizeof(WMStmp),
362 //        "http://mesonet.tamu.edu/cgi-bin/p-warn?SERVICE=WMS&VERSION=1.1.1&REQUEST=getmap");
363 
364 
365   xastir_snprintf(WMStmp, sizeof(WMStmp), "%s", URL);
366   strncat(WMStmp, "&REQUEST=getmap", sizeof(WMStmp) - 1 - strlen(WMStmp));
367   strncat(WMStmp, "&EXCEPTIONS=INIMAGE", sizeof(WMStmp) - 1 - strlen(WMStmp));
368 
369 // This specifies a bounding box based on square pixels.
370   xastir_snprintf(tmpstr, sizeof(tmpstr),
371                   "&BBOX=%8.5f,%7.5f,%8.5f,%7.5f",
372                   left,   // Lower left
373                   bottom, // Lower left
374                   right,  // Upper right
375                   top);   // Upper right
376   strncat (WMStmp, tmpstr, sizeof(WMStmp) - 1 - strlen(WMStmp));
377 
378   xastir_snprintf(tmpstr, sizeof(tmpstr), "&HEIGHT=%d", geo_image_height);
379   strncat (WMStmp, tmpstr, sizeof(WMStmp) - 1 - strlen(WMStmp));
380 
381   xastir_snprintf(tmpstr, sizeof(tmpstr), "&WIDTH=%d", geo_image_width);
382   strncat (WMStmp, tmpstr, sizeof(WMStmp) - 1 - strlen(WMStmp));
383 
384 
385 // These should be specified in the .geo file instead of hard-coded:
386 //
387 //    strncat(WMStmp, "&VERSION=1.0.0", sizeof(WMStmp) - 1 - strlen(WMStmp));
388 //    strncat(WMStmp, "&FORMAT=image/png", sizeof(WMStmp) - 1 - strlen(WMStmp));
389 //    strncat(WMStmp, "&TRANSPARENT=TRUE", sizeof(WMStmp) - 1 - strlen(WMStmp));
390 //    strncat(WMStmp, "&BGCOLOR=0xffffff", sizeof(WMStmp) - 1 - strlen(WMStmp));
391 //    strncat(WMStmp, "&BGCOLOR=0x000000", sizeof(WMStmp) - 1 - strlen(WMStmp));
392 //    strncat(WMStmp, "&CRS=CRS:84", sizeof(WMStmp) - 1 - strlen(WMStmp));
393 
394   memcpy(fileimg, WMStmp, sizeof(fileimg));
395   fileimg[sizeof(fileimg)-1] = '\0';  // Terminate string
396 
397   if (debug_level & 512)
398   {
399     fprintf(stderr,"left side is %f\n", left);
400     fprintf(stderr,"right side is %f\n", right);
401     fprintf(stderr,"top  is %f\n", top);
402     fprintf(stderr,"bottom is %f\n", bottom);
403     fprintf(stderr,"lat center is %f\n", lat_center);
404     fprintf(stderr,"long center is %f\n", long_center);
405     fprintf(stderr,"screen width is %li\n", screen_width);
406     fprintf(stderr,"screen height is %li\n", screen_height);
407     fprintf(stderr,"map width is %f\n", map_width);
408     fprintf(stderr,"map height is %f\n", map_height);
409     fprintf(stderr,"fileimg is %s\n", fileimg);
410     fprintf(stderr,"ftp or http file: %s\n", fileimg);
411   }
412 
413   if (debug_level & 512)
414   {
415     query_start_time=time(&query_start_time);
416   }
417 
418 
419 #ifdef USE_MAP_CACHE
420 
421   if (nocache || map_cache_fetch_disable)
422   {
423 
424     // Delete old copy from the cache
425     if (map_cache_fetch_disable && fileimg[0] != '\0')
426     {
427       if (map_cache_del(fileimg))
428       {
429         if (debug_level & 512)
430         {
431           fprintf(stderr,"Couldn't delete old map from cache\n");
432         }
433       }
434     }
435 
436     // Simulate a cache miss
437     map_cache_return = 1;
438   }
439   else
440   {
441     // Else look for the file in the cache
442     map_cache_return = map_cache_get(fileimg,local_filename);
443   }
444 
445   if (debug_level & 512)
446   {
447     fprintf(stderr,"map_cache_return: %d\n", map_cache_return);
448   }
449 
450 
451   // Don't use cached version if "nocache" is non-zero
452   //
453   if (nocache || map_cache_return != 0 )
454   {
455     // Caching has not been requested or cached file not found.
456     // We must snag the remote file via libcurl or wget.
457 
458     if (nocache)
459     {
460       xastir_snprintf(local_filename,
461                       sizeof(local_filename),
462                       "%s/map.%s",
463                       get_user_base_dir("tmp", temp_file_path, sizeof(temp_file_path)),
464                       "png");
465     }
466     else
467     {
468       cache_file_id = map_cache_fileid();
469       xastir_snprintf(local_filename,
470                       sizeof(local_filename),
471                       "%s/map_%s.%s",
472                       get_user_base_dir("map_cache", temp_file_path, sizeof(temp_file_path)),
473                       cache_file_id,
474                       "png");
475       free(cache_file_id);
476     }
477 
478 #else   // USE_MAP_CACHE
479 
480   xastir_snprintf(local_filename,
481                   sizeof(local_filename),
482                   "%s/map.%s",
483                   get_user_base_dir("tmp", temp_file_path, sizeof(temp_file_path)),
484                   "png");
485 
486 #endif  // USE_MAP_CACHE
487 
488 
489     // Erase any previously existing local file by the same name.
490     // This avoids the problem of having an old map image here and
491     // the code trying to display it when the download fails.
492 
493     unlink( local_filename );
494 
495     HandlePendingEvents(app_context);
496     if (interrupt_drawing_now)
497     {
498       // Update to screen
499       (void)XCopyArea(XtDisplay(da),
500                       pixmap,
501                       XtWindow(da),
502                       gc,
503                       0,
504                       0,
505                       (unsigned int)screen_width,
506                       (unsigned int)screen_height,
507                       0,
508                       0);
509       return;
510     }
511 
512     if (fetch_remote_file(fileimg, local_filename))
513     {
514       // Had trouble getting the file.  Abort.
515       return;
516     }
517 
518     // For debugging the MagickError/MagickWarning segfaults.
519     //system("cat /dev/null >/var/tmp/xastir_hacker_map.png");
520 
521 
522 #ifdef USE_MAP_CACHE
523 
524     // Cache this map only if nocache is zero
525     if (!nocache)
526     {
527       map_cache_put(fileimg,local_filename);
528     }
529 
530   } // end if is cached  DHBROWN
531 #endif // USE_MAP_CACHE
532 
533 
534   if (debug_level & 512)
535   {
536     fprintf (stderr, "Fetch or query took %d seconds\n",
537              (int) (time(&query_end_time) - query_start_time));
538   }
539 
540   // Set permissions on the file so that any user can overwrite it.
541   chmod(local_filename, 0666);
542 
543   // Tell ImageMagick where to find it
544   xastir_snprintf(file,
545                   sizeof(file),
546                   "%s",
547                   local_filename);
548 
549   GetExceptionInfo(&exception);
550 
551   image_info=CloneImageInfo((ImageInfo *) NULL);
552 
553   xastir_snprintf(image_info->filename,
554                   sizeof(image_info->filename),
555                   "%s",
556                   file);
557 
558   if (debug_level & 512)
559   {
560     fprintf(stderr,"Copied %s into image info.\n", file);
561     fprintf(stderr,"image_info got: %s\n", image_info->filename);
562     fprintf(stderr,"Entered ImageMagick code.\n");
563     fprintf(stderr,"Attempting to open: %s\n", image_info->filename);
564   }
565 
566   // We do a test read first to see if the file exists, so we
567   // don't kill Xastir in the ReadImage routine.
568   f = fopen (image_info->filename, "r");
569   if (f == NULL)
570   {
571     if (debug_level & 512)
572     {
573       fprintf(stderr,"File could not be read\n");
574     }
575 
576 #ifdef USE_MAP_CACHE
577     // clear from cache if bad
578     if (map_cache_del(fileimg))
579     {
580       if (debug_level & 512)
581       {
582         fprintf(stderr,"Couldn't delete map from cache\n");
583       }
584     }
585 #endif
586 
587     if (image_info)
588     {
589       DestroyImageInfo(image_info);
590     }
591     DestroyExceptionInfo(&exception);
592     return;
593   }
594   (void)fclose (f);
595 
596 
597   image = ReadImage(image_info, &exception);
598 
599   if (image == (Image *) NULL)
600   {
601     MagickWarning(exception.severity, exception.reason, exception.description);
602     //fprintf(stderr,"MagickWarning\n");
603 
604 #ifdef USE_MAP_CACHE
605     // clear from cache if bad
606     if (map_cache_del(fileimg))
607     {
608       if (debug_level & 512)
609       {
610         fprintf(stderr,"Couldn't delete map from cache\n");
611       }
612     }
613 #endif
614 
615     if (image_info)
616     {
617       DestroyImageInfo(image_info);
618     }
619     DestroyExceptionInfo(&exception);
620     return;
621   }
622 
623 
624   if (debug_level & 512)
625   {
626     fprintf(stderr,"Color depth is %i \n", (int)image->depth);
627   }
628 
629   /*
630       if (image->colorspace != RGBColorspace) {
631           fprintf(stderr,"TBD: I don't think we can deal with colorspace != RGB");
632           if (image)
633               DestroyImage(image);
634           if (image_info)
635               DestroyImageInfo(image_info);
636           DestroyExceptionInfo(&exception);
637           return;
638       }
639   */
640 
641   width = image->columns;
642   height = image->rows;
643 
644   //  Code to mute the image so it's not as bright.
645   /*    if (raster_map_intensity < 1.0) {
646           char tempstr[30];
647 
648           if (debug_level & 512)
649               fprintf(stderr,"level=%s\n", tempstr);
650 
651           xastir_snprintf(tempstr,
652               sizeof(tempstr),
653               "%d, 100, 100",
654               (int)(raster_map_intensity * 100.0));
655 
656           ModulateImage(image, tempstr);
657       }
658   */
659 
660 
661   // If were are drawing to a low bpp display (typically < 8bpp)
662   // try to reduce the number of colors in an image.
663   // This may take some time, so it would be best to do ahead of
664   // time if it is a static image.
665 #if (MagickLibVersion < 0x0540)
666   if (visual_type == NOT_TRUE_NOR_DIRECT && GetNumberColors(image, NULL) > 128)
667   {
668 #else   // MagickLib >= 540
669   if (visual_type == NOT_TRUE_NOR_DIRECT && GetNumberColors(image, NULL, &exception) > 128)
670   {
671 #endif  // MagickLib Version
672 
673     if (image->storage_class == PseudoClass)
674     {
675 #if (MagickLibVersion < 0x0549)
676       CompressColormap(image); // Remove duplicate colors
677 #else // MagickLib >= 0x0549
678       CompressImageColormap(image); // Remove duplicate colors
679 #endif  // MagickLibVersion < 0x0549
680     }
681 
682     // Quantize down to 128 will go here...
683   }
684 
685 
686 #if defined(HAVE_GRAPHICSMAGICK) || (MagickLibVersion < 0x0669)
687   pixel_pack = GetImagePixels(image, 0, 0, image->columns, image->rows);
688 #else
689   pixel_pack = GetAuthenticPixels(image, 0, 0, image->columns, image->rows, &exception);
690 #endif
691   if (!pixel_pack)
692   {
693     fprintf(stderr,"pixel_pack == NULL!!!");
694     if (image)
695     {
696       DestroyImage(image);
697     }
698     if (image_info)
699     {
700       DestroyImageInfo(image_info);
701     }
702     DestroyExceptionInfo(&exception);
703     return;
704   }
705 
706 
707 #if defined(HAVE_GRAPHICSMAGICK)
708   #if (MagickLibVersion < 0x201702)
709     index_pack = GetIndexes(image);
710   #else
711     index_pack = AccessMutableIndexes(image);
712   #endif
713 #else
714   #if (MagickLibVersion < 0x0669)
715     index_pack = GetIndexes(image);
716   #else
717     index_pack = GetAuthenticIndexQueue(image);
718   #endif
719 #endif
720   if (image->storage_class == PseudoClass && !index_pack)
721   {
722     fprintf(stderr,"PseudoClass && index_pack == NULL!!!");
723     if (image)
724     {
725       DestroyImage(image);
726     }
727     if (image_info)
728     {
729       DestroyImageInfo(image_info);
730     }
731     DestroyExceptionInfo(&exception);
732     return;
733   }
734 
735 
736   if (image->storage_class == PseudoClass && image->colors <= 256)
737   {
738     for (l = 0; l < (int)image->colors; l++)
739     {
740       // Need to check how to do this for ANY image, as ImageMagick can read in all sorts
741       // of image files
742       temp_pack = image->colormap[l];
743       if (debug_level & 512)
744         fprintf(stderr,"Colormap color is %i  %i  %i \n",
745                 (int)temp_pack.red, (int)temp_pack.green, (int)temp_pack.blue);
746 
747       // Here's a tricky bit:  PixelPacket entries are defined as Quantum's.  Quantum
748       // is defined in /usr/include/magick/image.h as either an unsigned short or an
749       // unsigned char, depending on what "configure" decided when ImageMagick was installed.
750       // We can determine which by looking at MaxRGB or QuantumDepth.
751       //
752       if (QuantumDepth == 16)     // Defined in /usr/include/magick/image.h
753       {
754         if (debug_level & 512)
755         {
756           fprintf(stderr,"Color quantum is [0..65535]\n");
757         }
758         my_colors[l].red   = temp_pack.red * raster_map_intensity;
759         my_colors[l].green = temp_pack.green * raster_map_intensity;
760         my_colors[l].blue  = temp_pack.blue * raster_map_intensity;
761       }
762       else    // QuantumDepth = 8
763       {
764         if (debug_level & 512)
765         {
766           fprintf(stderr,"Color quantum is [0..255]\n");
767         }
768         my_colors[l].red   = (temp_pack.red * 256) * raster_map_intensity;
769         my_colors[l].green = (temp_pack.green * 256) * raster_map_intensity;
770         my_colors[l].blue  = (temp_pack.blue * 256) * raster_map_intensity;
771       }
772 
773       // Get the color allocated on < 8bpp displays. pixel color is written to my_colors.pixel
774       if (visual_type == NOT_TRUE_NOR_DIRECT)
775       {
776 //                XFreeColors(XtDisplay(w), cmap, &(my_colors[l].pixel),1,0);
777         XAllocColor(XtDisplay(w), cmap, &my_colors[l]);
778       }
779       else
780       {
781         pack_pixel_bits(my_colors[l].red, my_colors[l].green, my_colors[l].blue,
782                         &my_colors[l].pixel);
783       }
784 
785       if (debug_level & 512)
786         fprintf(stderr,"Color allocated is %li  %i  %i  %i \n", my_colors[l].pixel,
787                 my_colors[l].red, my_colors[l].blue, my_colors[l].green);
788     }
789   }
790 
791 
792 
793   /*
794   * Here are the corners of our viewport, using the Xastir
795   * coordinate system.  Notice that Y is upside down:
796   *
797   *   left edge of view = NW_corner_longitude
798   *  right edge of view = SE_corner_longitude
799   *    top edge of view =  NW_corner_latitude
800   * bottom edge of view =  SE_corner_latitude
801   *
802   * The corners of our map will soon be (after translating the
803   * tiepoints to the corners if they're not already there):
804   *
805   *   left edge of map = tp[0].x_long   in Xastir format
806   *  right edge of map = tp[1].x_long
807   *    top edge of map = tp[0].y_lat
808   * bottom edge of map = tp[1].y_lat
809   *
810   */
811   map_c_L = tp[0].x_long - NW_corner_longitude;     // map left coordinate
812   map_c_T = tp[0].y_lat  - NW_corner_latitude;      // map top  coordinate
813 
814   tp_c_dx = (long)(tp[1].x_long - tp[0].x_long);//  Width between tiepoints
815   tp_c_dy = (long)(tp[1].y_lat  - tp[0].y_lat); // Height between tiepoints
816 
817 
818   // Check for tiepoints being in wrong relation to one another
819   if (tp_c_dx < 0)
820   {
821     tp_c_dx = -tp_c_dx;  // New  width between tiepoints
822   }
823   if (tp_c_dy < 0)
824   {
825     tp_c_dy = -tp_c_dy;  // New height between tiepoints
826   }
827 
828   // Calculate step size per pixel
829   map_c_dx = ((double) tp_c_dx / abs(tp[1].img_x - tp[0].img_x));
830   map_c_dy = ((double) tp_c_dy / abs(tp[1].img_y - tp[0].img_y));
831 
832   // Scaled screen step size for use with XFillRectangle below
833   scr_dx = (int) (map_c_dx / scale_x) + 1;
834   scr_dy = (int) (map_c_dy / scale_y) + 1;
835 
836   // calculate top left map corner from tiepoints
837   if (tp[0].img_x != 0)
838   {
839     tp[0].x_long -= (tp[0].img_x * map_c_dx);   // map left edge longitude
840     map_c_L = tp[0].x_long - NW_corner_longitude;     // delta ??
841     tp[0].img_x = 0;
842     if (debug_level & 512)
843     {
844       fprintf(stderr,"Translated tiepoint_0 x: %d\t%lu\n", tp[0].img_x, tp[0].x_long);
845     }
846   }
847   if (tp[0].img_y != 0)
848   {
849     tp[0].y_lat -= (tp[0].img_y * map_c_dy);    // map top edge latitude
850     map_c_T = tp[0].y_lat - NW_corner_latitude;
851     tp[0].img_y = 0;
852     if (debug_level & 512)
853     {
854       fprintf(stderr,"Translated tiepoint_0 y: %d\t%lu\n", tp[0].img_y, tp[0].y_lat);
855     }
856   }
857 
858   // calculate bottom right map corner from tiepoints
859   // map size is geo_image_width / geo_image_height
860   if (tp[1].img_x != (geo_image_width - 1) )
861   {
862     tp[1].img_x = geo_image_width - 1;
863     tp[1].x_long = tp[0].x_long + (tp[1].img_x * map_c_dx); // right
864     if (debug_level & 512)
865     {
866       fprintf(stderr,"Translated tiepoint_1 x: %d\t%lu\n", tp[1].img_x, tp[1].x_long);
867     }
868   }
869   if (tp[1].img_y != (geo_image_height - 1) )
870   {
871     tp[1].img_y = geo_image_height - 1;
872     tp[1].y_lat = tp[0].y_lat + (tp[1].img_y * map_c_dy);   // bottom
873     if (debug_level & 512)
874     {
875       fprintf(stderr,"Translated tiepoint_1 y: %d\t%lu\n", tp[1].img_y, tp[1].y_lat);
876     }
877   }
878 
879   if (debug_level & 512)
880   {
881     fprintf(stderr,"X tiepoint width: %ld\n", tp_c_dx);
882     fprintf(stderr,"Y tiepoint width: %ld\n", tp_c_dy);
883     fprintf(stderr,"Loading imagemap: %s\n", file);
884     fprintf(stderr,"\nImage: %s\n", file);
885     fprintf(stderr,"Image size %d %d\n", geo_image_width, geo_image_height);
886     fprintf(stderr,"XX: %ld YY:%ld Sx %f %d Sy %f %d\n",
887             map_c_L, map_c_T, map_c_dx,(int) (map_c_dx / scale_x), map_c_dy, (int) (map_c_dy / scale_y));
888     fprintf(stderr,"Image size %d %d\n", width, height);
889 #if (MagickLibVersion < 0x0540)
890     fprintf(stderr,"Unique colors = %d\n", GetNumberColors(image, NULL));
891 #else // MagickLib < 540
892     fprintf(stderr,"Unique colors = %ld\n", GetNumberColors(image, NULL, &exception));
893 #endif // MagickLib < 540
894     fprintf(stderr,"XX: %ld YY:%ld Sx %f %d Sy %f %d\n", map_c_L, map_c_T,
895             map_c_dx,(int) (map_c_dx / scale_x), map_c_dy, (int) (map_c_dy / scale_y));
896     fprintf(stderr,"image matte is %i\n", image->matte);
897   } // debug_level & 512
898 
899   // draw the image from the file out to the map screen
900 
901   // Get the border values for the X and Y for loops used
902   // for the XFillRectangle call later.
903 
904 //  map_c_yc = (tp[0].y_lat + tp[1].y_lat) / 2;     // vert center of map as reference
905 //  map_y_ctr = (long)(height / 2 +0.499);
906 //  scale_x0 = get_x_scale(0,map_c_yc,scale_y);     // reference scaling at vert map center
907 
908 //  map_c_xc  = (tp[0].x_long + tp[1].x_long) / 2;  // hor center of map as reference
909 //  map_x_ctr = (long)(width  / 2 +0.499);
910 //  scr_x_mc  = (map_c_xc - NW_corner_longitude) / scale_x; // screen coordinates of map center
911 
912   // calculate map pixel range in y direction that falls into screen area
913 //  c_y_max = 0ul;
914   map_y_min = map_y_max = 0l;
915   for (map_y_0 = 0, c_y = tp[0].y_lat; map_y_0 < (long)height; map_y_0++, c_y += map_c_dy)
916   {
917     scr_y = (c_y - NW_corner_latitude) / scale_y;   // current screen position
918     if (scr_y > 0)
919     {
920       if (scr_y < screen_height)
921       {
922         map_y_max = map_y_0;          // update last map pixel in y
923 //              c_y_max = (unsigned long)c_y;// bottom map inside screen coordinate
924       }
925       else
926       {
927         break;  // done, reached bottom screen border
928       }
929     }
930     else                                // pixel is above screen
931     {
932       map_y_min = map_y_0;              // update first map pixel in y
933     }
934   }
935   c_y_min = (unsigned long)(tp[0].y_lat + map_y_min * map_c_dy);   // top map inside screen coordinate
936 
937   map_x_min = map_x_max = 0l;
938   for (map_x = 0, c_x = tp[0].x_long; map_x < (long)width; map_x++, c_x += map_c_dx)
939   {
940     scr_x = (c_x - NW_corner_longitude)/ scale_x;  // current screen position
941     if (scr_x > 0)
942     {
943       if (scr_x < screen_width)
944       {
945         map_x_max = map_x;  // update last map pixel in x
946       }
947       else
948       {
949         break;  // done, reached right screen border
950       }
951     }
952     else                                // pixel is left from screen
953     {
954       map_x_min = map_x;              // update first map pixel in x
955     }
956   }
957   c_x_min = (unsigned long)(tp[0].x_long + map_x_min * map_c_dx);   // left map inside screen coordinate
958 
959   scr_yp = -1;
960 //  scr_c_xr = SE_corner_longitude;
961   c_dx = map_c_dx;                            // map pixel width
962 //  scale_xa = scale_x0;                        // the compiler likes it ;-)
963 
964 //  map_done = 0;
965   map_act  = 0;
966   map_seen = 0;
967   scr_y = screen_height - 1;
968 
969 
970   // loop over map pixel rows
971   for (map_y_0 = map_y_min, c_y = (double)c_y_min; (map_y_0 <= map_y_max); map_y_0++, c_y += map_c_dy)
972   {
973 
974     HandlePendingEvents(app_context);
975     if (interrupt_drawing_now)
976     {
977       if (image)
978       {
979         DestroyImage(image);
980       }
981       if (image_info)
982       {
983         DestroyImageInfo(image_info);
984       }
985       // Update to screen
986       (void)XCopyArea(XtDisplay(da),
987                       pixmap,
988                       XtWindow(da),
989                       gc,
990                       0,
991                       0,
992                       (unsigned int)screen_width,
993                       (unsigned int)screen_height,
994                       0,
995                       0);
996       return;
997     }
998 
999     scr_y = (c_y - NW_corner_latitude) / scale_y;
1000     if (scr_y != scr_yp)                    // don't do a row twice
1001     {
1002       scr_yp = scr_y;                     // remember as previous y
1003       scr_xp = -1;
1004       // loop over map pixel columns
1005       map_act = 0;
1006 //          scale_x_nm = calc_dscale_x(0,(long)c_y) / 1852.0;  // nm per Xastir coordinate
1007       for (map_x = map_x_min, c_x = (double)c_x_min; map_x <= map_x_max; map_x++, c_x += c_dx)
1008       {
1009         scr_x = (c_x - NW_corner_longitude) / scale_x;
1010         if (scr_x != scr_xp)        // don't do a pixel twice
1011         {
1012           scr_xp = scr_x;         // remember as previous x
1013           map_y = map_y_0;
1014 
1015           if (map_y >= 0 && map_y <= tp[1].img_y)   // check map boundaries in y direction
1016           {
1017             map_seen = 1;
1018             map_act = 1;    // detects blank screen rows (end of map)
1019 
1020             // now copy a pixel from the map image to the screen
1021             l = map_x + map_y * image->columns;
1022             trans_skip = 1; // possibly transparent
1023             if (image->storage_class == PseudoClass)
1024             {
1025               if ( c_trans_color_head &&
1026                    check_trans(my_colors[(int)index_pack[l]],c_trans_color_head))
1027               {
1028                 trans_skip = 1; // skip it
1029               }
1030               else
1031               {
1032                 XSetForeground(XtDisplay(w), gc, my_colors[(int)index_pack[l]].pixel);
1033                 trans_skip = 0; // draw it
1034               }
1035             }
1036             else
1037             {
1038               // It is not safe to assume that the red/green/blue
1039               // elements of pixel_pack of type Quantum are the
1040               // same as the red/green/blue of an XColor!
1041               if (QuantumDepth==16)
1042               {
1043                 my_colors[0].red=pixel_pack[l].red;
1044                 my_colors[0].green=pixel_pack[l].green;
1045                 my_colors[0].blue=pixel_pack[l].blue;
1046               }
1047               else   // QuantumDepth=8
1048               {
1049                 // shift the bits of the 8-bit quantity so that
1050                 // they become the high bigs of my_colors.*
1051                 my_colors[0].red=pixel_pack[l].red*256;
1052                 my_colors[0].green=pixel_pack[l].green*256;
1053                 my_colors[0].blue=pixel_pack[l].blue*256;
1054               }
1055               // NOW my_colors has the right r,g,b range for
1056               // pack_pixel_bits
1057               pack_pixel_bits(my_colors[0].red * raster_map_intensity,
1058                               my_colors[0].green * raster_map_intensity,
1059                               my_colors[0].blue * raster_map_intensity,
1060                               &my_colors[0].pixel);
1061               if ( c_trans_color_head &&
1062                    check_trans(my_colors[0],c_trans_color_head))
1063               {
1064                 trans_skip = 1; // skip it
1065               }
1066               else
1067               {
1068                 XSetForeground(XtDisplay(w), gc, my_colors[0].pixel);
1069                 trans_skip = 0; // draw it
1070               }
1071             }
1072 
1073             // Skip drawing if a transparent pixel
1074             if (!trans_skip)
1075             {
1076               (void)XFillRectangle (XtDisplay (w),pixmap,gc,scr_x,scr_y,scr_dx,scr_dy);
1077             }
1078 
1079           } // check map boundaries in y direction
1080         }
1081       } // loop over map pixel columns
1082       if (map_seen && !map_act)
1083       {
1084 //              map_done = 1;
1085       }
1086     }
1087   } // loop over map pixel rows
1088 
1089   if (image)
1090   {
1091     DestroyImage(image);
1092   }
1093   if (image_info)
1094   {
1095     DestroyImageInfo(image_info);
1096   }
1097   DestroyExceptionInfo(&exception);
1098 }
1099 #endif //HAVE_MAGICK
1100