1 /*
2  *
3  *
4  * Copyright (C) 2000-2019 The Xastir Group
5  *
6  * This file was contributed by Jerry Dunmire, KA6HLD.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * Look at the README for more information on the program.
23  *
24  * This file derived from map_tiger.c which has the following copyrights:
25  *    Copyright (C) 1999,2000  Frank Giannandrea
26  *    Copyright (C) 2000-2019 The Xastir Group
27  */
28 
29 
30 #ifdef HAVE_CONFIG_H
31   #include "config.h"
32 #endif  // HAVE_CONFIG_H
33 
34 #include "snprintf.h"
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <sys/stat.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <pwd.h>
43 #include <errno.h>
44 
45 // Needed for Solaris
46 #ifdef HAVE_STRINGS_H
47   #include <strings.h>
48 #endif  // HAVE_STRINGS_H
49 
50 #include <dirent.h>
51 #include <netinet/in.h>
52 #include <Xm/XmAll.h>
53 
54 #ifdef HAVE_X11_XPM_H
55   #include <X11/xpm.h>
56   #ifdef HAVE_LIBXPM // if we have both, prefer the extra library
57     #undef HAVE_XM_XPMI_H
58   #endif // HAVE_LIBXPM
59 #endif // HAVE_X11_XPM_H
60 
61 #ifdef HAVE_XM_XPMI_H
62   #include <Xm/XpmI.h>
63 #endif // HAVE_XM_XPMI_H
64 
65 #include <X11/Xlib.h>
66 
67 #include <math.h>
68 
69 #include "xastir.h"
70 #include "maps.h"
71 #include "fetch_remote.h"
72 #include "util.h"
73 #include "main.h"
74 #include "color.h"
75 #include "xa_config.h"
76 
77 #include "map_cache.h"
78 
79 #include "tile_mgmnt.h"
80 #include "dlm.h"
81 #include "map_OSM.h"
82 
83 #define CHECKMALLOC(m)  if (!m) { fprintf(stderr, "***** Malloc Failed *****\n"); exit(0); }
84 
85 #ifdef HAVE_MAGICK
86   #if TIME_WITH_SYS_TIME
87     #include <sys/time.h>
88     #include <time.h>
89   #else   // TIME_WITH_SYS_TIME
90     #if HAVE_SYS_TIME_H
91       #include <sys/time.h>
92     #else  // HAVE_SYS_TIME_H
93       #include <time.h>
94     #endif // HAVE_SYS_TIME_H
95   #endif  // TIME_WITH_SYS_TIME
96   #undef RETSIGTYPE
97   // TVR: "stupid ImageMagick"
98   // The problem is that magick/api.h includes Magick's config.h file, and that
99   // pulls in all the same autoconf-generated defines that we use.
100   // plays those games below, but I don't think in the end that they actually
101   // make usable macros with our own data in them.
102   // Fortunately, we don't need them, so I'll just undef the ones that are
103   // causing problems today.  See main.c for fixes that preserve our values.
104   #undef PACKAGE
105   #undef VERSION
106   /* JMT - stupid ImageMagick */
107   #define XASTIR_PACKAGE_BUGREPORT PACKAGE_BUGREPORT
108   #undef PACKAGE_BUGREPORT
109   #define XASTIR_PACKAGE_NAME PACKAGE_NAME
110   #undef PACKAGE_NAME
111   #define XASTIR_PACKAGE_STRING PACKAGE_STRING
112   #undef PACKAGE_STRING
113   #define XASTIR_PACKAGE_TARNAME PACKAGE_TARNAME
114   #undef PACKAGE_TARNAME
115   #define XASTIR_PACKAGE_VERSION PACKAGE_VERSION
116   #undef PACKAGE_VERSION
117   #ifdef HAVE_MAGICK
118     #ifdef HAVE_MAGICKCORE_MAGICKCORE_H
119       #include <MagickCore/MagickCore.h>
120     #else
121       #ifdef HAVE_MAGICK_API_H
122         #include <magick/api.h>
123       #endif // HAVE_MAGICK_API_H
124     #endif //HAVE_MAGICKCORE_MAGICKCORE_H
125   #endif //HAVE_MAGICK
126   #undef PACKAGE_BUGREPORT
127   #define PACKAGE_BUGREPORT XASTIR_PACKAGE_BUGREPORT
128   #undef XASTIR_PACKAGE_BUGREPORT
129   #undef PACKAGE_NAME
130   #define PACKAGE_NAME XASTIR_PACKAGE_NAME
131   #undef XASTIR_PACKAGE_NAME
132   #undef PACKAGE_STRING
133   #define PACKAGE_STRING XASTIR_PACKAGE_STRING
134   #undef XASTIR_PACKAGE_STRING
135   #undef PACKAGE_TARNAME
136   #define PACKAGE_TARNAME XASTIR_PACKAGE_TARNAME
137   #undef XASTIR_PACKAGE_TARNAME
138   #undef PACKAGE_VERSION
139   #define PACKAGE_VERSION XASTIR_PACKAGE_VERSION
140   #undef XASTIR_PACKAGE_VERSION
141 
142   // This matte color was chosen emphirically to work well with the
143   // contours from topOSM.
144   #if (QuantumDepth == 8)
145     #define MATTE_RED   (0xa7)
146     #define MATTE_GREEN (0xa7)
147     #define MATTE_BLUE  (0xa7)
148     #define MATTE_OPACITY (0x00)
149     #define MATTE_COLOR_STRING "xc:#a7a7a7"
150     //#define MATTE_COLOR_STRING "xc:#a7a7a700"
151     //#define MATTE_OPACITY (0xff)
152     //#define MATTE_COLOR_STRING "xc:#a7a7a7ff"
153   #elif (QuantumDepth == 16)
154     #define MATTE_RED   (0xa700)
155     #define MATTE_GREEN (0xa700)
156     #define MATTE_BLUE  (0xa700)
157     #define MATTE_OPACITY (0x0000)
158     #define MATTE_COLOR_STRING "xc:#a700a700a700"
159     //#define MATTE_COLOR_STRING "xc:#a700a700a700ffff"
160   #else
161     #error "QuantumDepth != 16 or 8"
162   #endif // QuantumDepth
163 
164 #endif // HAVE_MAGICK
165 
166 // Must be last include file
167 #include "leak_detection.h"
168 
169 #define xastirColorsMatch(p,q) (((p).red == (q).red) && ((p).blue == (q).blue) \
170         && ((p).green == (q).green))
171 
172 // osm_scale_x - map Xastir scale_x value to an OSM binned value
173 //
174 // Note that the terms 'higher' and 'lower' are confusing because a
175 // smaller Xastir scale number is a larger OSM zoom level. OSM zoom level
176 // 0 would show the whole world in a 256x256 pixel tile, OSM zoom level
177 // 18 (the max) would require 2^18 tiles to simple wrap the equator.
178 //
179 // On the equator, OSM zoom level 0 equates to ~97 miles/pixel
180 // and OSM zoom level 18 equates to ~2 ft/pixel
181 //
182 // direction = -1, zoom in
183 // direction = 1, zoom out
184 // direction = 0, nearst level out from the xastir scale
185 //
186 #define MAX_OSM_ZOOM_LEVEL 18
187 #define OSM_ZOOM_LEVELS    (MAX_OSM_ZOOM_LEVEL + 1)
188 
osm_scale_x(long xastir_scale_x)189 static long osm_scale_x(long xastir_scale_x)
190 {
191 
192   long osm_level[OSM_ZOOM_LEVELS] = {1, 2, 4, 8, 15, 31, 62, 124, \
193                                      247, 494, 989, 1978, 3955, 7910, 15820, 31641,\
194                                      63281, 126563, 253125
195                                     };
196   long osm_scale_x = osm_level[0];
197   int i = 0;
198 
199   for (i=1; i <= MAX_OSM_ZOOM_LEVEL; i++)
200   {
201     if (xastir_scale_x > osm_level[i])
202     {
203       continue;
204     }
205     else
206     {
207       if (labs(osm_level[i - 1] - xastir_scale_x) < labs(osm_level[i] - xastir_scale_x))
208       {
209         osm_scale_x = osm_level[i - 1];
210       }
211       else
212       {
213         osm_scale_x = osm_level[i];
214       }
215       break;
216     }
217   }
218 
219   if (i > MAX_OSM_ZOOM_LEVEL)
220   {
221     i = MAX_OSM_ZOOM_LEVEL;
222     osm_scale_x = osm_level[i];
223   }
224 
225   return(osm_scale_x);
226 
227 } // osm_scale_x()
228 
229 
230 /*
231  * adj_to_OSM_level - adjust scale_x and scale_y to approximate an OSM zoom level
232  *
233  * The OSM zoom level closest to the scale_x value will be chosen and scale_x is modified.
234  * The scale_y value (pointed to by the second argument) is scaled proportionaly. Both
235  * values pointed to by the arguments are modified.
236  */
adj_to_OSM_level(long * new_scale_x,long * new_scale_y)237 void adj_to_OSM_level( long *new_scale_x, long *new_scale_y)
238 {
239 
240   long scale;
241 
242   scale = osm_scale_x(*new_scale_x);
243 
244   // the y scale must also be adjusted.
245   *new_scale_y = (int)(((double)(*new_scale_y) * ((double)scale / (double)(*new_scale_x)) + 0.5));
246   *new_scale_x = scale;
247 
248   return;
249 
250 } // adj_to_OSM_level()
251 
252 
253 /*
254  * osm_zoom_level - translate the longitude scale to the nearest OSM zoom level
255  *
256  * OSM tile scaling is based on the number of tiles needed to wrap the earth at the equator.
257  * A tile is 256x256 pixels.
258  */
osm_zoom_level(long scale_x)259 unsigned int osm_zoom_level(long scale_x)
260 {
261   double circumference = 360.0*3600.0*100.0; // Xastir Units = 1/100 second.
262   double zf;
263   int z;
264   zf = (log(circumference / (double)scale_x) / log(2.0)) - 8.0;
265   z = (int)(zf + 0.5);
266 
267   // OSM levels run from 0 to 18. Not all levels are available for all views.
268   if (z < 0)
269   {
270     z = 0;
271   }
272   if (z > 18)
273   {
274     z = 18;
275   }
276 
277   return((unsigned int)z);
278 
279 } // osm_zoom_level()
280 
281 
282 static KeySym OptimizeKey = 0;
283 static KeySym ReportScaleKey = 0;
284 
init_OSM_values(void)285 void init_OSM_values(void)
286 {
287   OptimizeKey = 0;
288   ReportScaleKey = 0;
289   return;
290 }
291 
OSM_optimize_key(KeySym key)292 int OSM_optimize_key(KeySym key)
293 {
294   return (key == OptimizeKey ? TRUE : FALSE);
295 }
296 
set_OSM_optimize_key(KeySym key)297 void set_OSM_optimize_key(KeySym key)
298 {
299   OptimizeKey = key;
300   return;
301 }
302 
OSM_report_scale_key(KeySym key)303 int OSM_report_scale_key(KeySym key)
304 {
305   return (key == ReportScaleKey ? TRUE : FALSE);
306 }
307 
set_OSM_report_scale_key(KeySym key)308 void set_OSM_report_scale_key(KeySym key)
309 {
310   ReportScaleKey = key;
311   return;
312 }
313 
314 #ifdef HAVE_MAGICK
get_OSM_local_file(char * local_filename,char * fileimg)315 static void get_OSM_local_file(char * local_filename, char * fileimg)
316 {
317   time_t query_start_time, query_end_time;
318 
319 #ifdef USE_MAP_CACHE
320   int map_cache_return = 1; // Default = cache miss
321   char *cache_file_id;
322 #endif  // USE_MAP_CACHE
323 
324   char temp_file_path[MAX_VALUE];
325 
326   if (debug_level & 512)
327   {
328     query_start_time=time(&query_start_time);
329   }
330 
331 
332 #ifdef USE_MAP_CACHE
333 
334   if (!map_cache_fetch_disable)
335   {
336 
337     // Delete old copy from the cache
338     if (map_cache_fetch_disable && fileimg[0] != '\0')
339     {
340       if (map_cache_del(fileimg))
341       {
342         if (debug_level & 512)
343         {
344           fprintf(stderr,"Couldn't delete old map from cache\n");
345         }
346       }
347     }
348 
349     set_dangerous("map_OSM: map_cache_get");
350     map_cache_return = map_cache_get(fileimg,local_filename);
351     clear_dangerous();
352   }
353 
354   if (debug_level & 512)
355   {
356     fprintf(stderr,"map_cache_return: <%d> bytes returned: %d\n",
357             map_cache_return,
358             (int) strlen(local_filename));
359   }
360 
361   if (map_cache_return != 0 )
362   {
363 
364     set_dangerous("map_OSM: map_cache_fileid");
365     cache_file_id = map_cache_fileid();
366     xastir_snprintf(local_filename,
367                     MAX_FILENAME,           // hardcoded to avoid sizeof()
368                     "%s/map_%s.%s",
369                     get_user_base_dir("map_cache", temp_file_path, sizeof(temp_file_path)),
370                     cache_file_id,
371                     "png");
372     free(cache_file_id);
373     clear_dangerous();
374 
375 #else   // USE_MAP_CACHE
376 
377   xastir_snprintf(local_filename,
378                   MAX_FILENAME,               // hardcoded to avoid sizeof()
379                   "%s/map.%s",
380                   get_user_base_dir("tmp", temp_file_path, sizeof(temp_file_path)),
381                   "png");
382 
383 #endif  // USE_MAP_CACHE
384 
385 
386     // Erase any previously existing local file by the same name.
387     // This avoids the problem of having an old map image here and
388     // the code trying to display it when the download fails.
389 
390     unlink( local_filename );
391 
392     if (fetch_remote_file(fileimg, local_filename))
393     {
394       // Had trouble getting the file.  Abort.
395       return;
396     }
397 
398     // For debugging the MagickError/MagickWarning segfaults.
399     //system("cat /dev/null >/var/tmp/xastir_hacker_map.png");
400 
401 
402 #ifdef USE_MAP_CACHE
403 
404     set_dangerous("map_OSM: map_cache_put");
405     map_cache_put(fileimg,local_filename);
406     clear_dangerous();
407 
408   } // end if is cached  DHBROWN
409 
410 #endif // MAP_CACHE
411 
412 
413   if (debug_level & 512)
414   {
415     fprintf (stderr, "Fetch or query took %d seconds\n",
416              (int) (time(&query_end_time) - query_start_time));
417   }
418 
419   // Set permissions on the file so that any user can overwrite it.
420   chmod(local_filename, 0666);
421 
422 } // end get_OSM_local_file
423 #endif  //HAVE_MAGICK
424 
425 
426 
427 #ifdef HAVE_MAGICK
xastirLat2pixelLat(long xlat,int osm_zoom)428 static long xastirLat2pixelLat(
429   long xlat, int osm_zoom )
430 {
431   double lat;  // in radians
432   double projection, y;
433   long pixelLat;
434 
435   lat = convert_lat_l2r(xlat);
436 
437   // xastir latitude values can exceed +/- 90.0 degrees because
438   // the latitude is the extent of the display window. Limit the
439   // OSM latitute to less than +/- 90.0 degrees so that the projection
440   // calculation does not blow up or return unreasonable values.
441   if (lat > ((89.0/180.0) * M_PI))
442   {
443     lat = ((89.0/180.0) * M_PI);
444   }
445   else if (lat < ((-89.0/180.0) * M_PI))
446   {
447     lat = ((-89.0/180.0) * M_PI);
448   }
449 
450 
451   projection = log(tan(lat) + (1.0 / cos(lat)));
452   y = projection / M_PI;
453   y = 1.0 - y;
454   pixelLat = (long)((y * (double)(1<<(osm_zoom + 8))) / 2.0);
455   return(pixelLat);
456 } // xastirLat2pixelLat()
457 #endif  // HAVE_MAGICK
458 
459 
460 #ifdef HAVE_MAGICK
pixelLat2Lat(long osm_lat,int osm_zoom)461 static double pixelLat2Lat(long osm_lat, int osm_zoom)
462 {
463   double lat, projection, y;
464   y = (double)osm_lat * 2.0 / (double)(1<<(osm_zoom + 8));
465   y = 1.0 - y;
466   projection = y * M_PI;
467   lat = 2.0 * atan(exp(projection)) - (M_PI / 2.0);
468   lat = (lat * 180.0 ) / M_PI;
469   return(lat);
470 } // pixelLat2Lat()
471 #endif  // HAVE_MAGICK
472 
473 
474 #ifdef HAVE_MAGICK
pixelLat2xastirLat(long osm_lat,int osm_zoom)475 static long pixelLat2xastirLat(long osm_lat, int osm_zoom)
476 {
477   double lat;
478   long xastirLat;
479   lat = pixelLat2Lat(osm_lat, osm_zoom);
480   xastirLat = (long)((90.0 - lat) * 3600.0 * 100.0);
481   return (xastirLat);
482 } // pixelLat2xastirLat()
483 #endif  // HAVE_MAGICK
484 
485 
486 #ifdef HAVE_MAGICK
xastirLon2pixelLon(long xlon,int osm_zoom)487 static long xastirLon2pixelLon(
488   long xlon, int osm_zoom)
489 {
490   double lon;
491   long pixelLon;
492   lon = xlon / (3600.0 * 100.0);
493   lon = lon * (1<<(osm_zoom +8));
494   lon = lon / 360.0;
495   pixelLon = lon;
496   return(pixelLon);
497 } // xastirLon2pixelLon()
498 #endif  // HAVE_MAGICK
499 
500 
501 #ifdef HAVE_MAGICK
pixelLon2Lon(long osm_lon,int osm_zoom)502 static double pixelLon2Lon(long osm_lon, int osm_zoom)
503 {
504   double lon;
505   lon = osm_lon * 360.0 ;
506   lon = lon / (1<<(osm_zoom + 8));
507   return(lon);
508 } // pixelLon2Lon()
509 #endif  // HAVE_MAGICK
510 
511 
512 #ifdef HAVE_MAGICK
pixelLon2xastirLon(long osm_lon,int osm_zoom)513 static long pixelLon2xastirLon(long osm_lon, int osm_zoom)
514 {
515   long xastirLon;
516   xastirLon = (long)(pixelLon2Lon(osm_lon, osm_zoom) * 3600.0 * 100.0);
517   return(xastirLon);
518 } // pixelLon2xastirLon()
519 #endif  // HAVE_MAGICK
520 
521 
522 #ifdef HAVE_MAGICK
523 
524 /**********************************************************
525  * draw_image() - copy a image onto the display
526  **********************************************************/
draw_image(Widget w,Image * image,ExceptionInfo * except_ptr,unsigned offsetx,unsigned offsety)527 static void draw_image(
528   Widget w,
529   Image *image,
530   ExceptionInfo *except_ptr,
531   unsigned offsetx,
532   unsigned offsety)
533 {
534   int l;
535   XColor my_colors[256];
536   PixelPacket *pixel_pack;
537   PixelPacket temp_pack;
538   IndexPacket *index_pack;
539   unsigned image_row;
540   unsigned image_col;
541   unsigned scr_x, scr_y;             // screen pixel plot positions
542 
543   //if (debug_level & 512)
544   //    fprintf(stderr,"Color depth is %i \n", (int)image->depth);
545 
546   /*
547       if (image->colorspace != RGBColorspace) {
548           TransformImageColorspace(image, RGBColorspace);
549           //fprintf(stderr,"TBD: I don't think we can deal with colorspace != RGB");
550           //return;
551       }
552   */
553 
554   // If were are drawing to a low bpp display (typically < 8bpp)
555   // try to reduce the number of colors in an image.
556   // This may take some time, so it would be best to do ahead of
557   // time if it is a static image.
558 #if (MagickLibVersion < 0x0540)
559   if (visual_type == NOT_TRUE_NOR_DIRECT && GetNumberColors(image, NULL) > 128)
560   {
561 #else   // MagickLib >= 540
562   if (visual_type == NOT_TRUE_NOR_DIRECT && GetNumberColors(image, NULL, except_ptr) > 128)
563   {
564 #endif  // MagickLib Version
565 
566     if (image->storage_class == PseudoClass)
567     {
568 #if (MagickLibVersion < 0x0549)
569       CompressColormap(image); // Remove duplicate colors
570 #else // MagickLib >= 0x0549
571       CompressImageColormap(image); // Remove duplicate colors
572 #endif  // MagickLibVersion < 0x0549
573     }
574 
575     // Quantize down to 128 will go here...
576   }
577 
578 
579 #if defined(HAVE_GRAPHICSMAGICK) || (MagickLibVersion < 0x0669)
580   pixel_pack = GetImagePixels(image, 0, 0, image->columns, image->rows);
581 #else
582   pixel_pack = GetAuthenticPixels(image, 0, 0, image->columns, image->rows, except_ptr);
583 #endif
584   if (!pixel_pack)
585   {
586     fprintf(stderr,"pixel_pack == NULL!!!");
587     return;
588   }
589 
590 #if defined(HAVE_GRAPHICSMAGICK)
591 #if (MagickLibVersion < 0x201702)
592   index_pack = GetIndexes(image);
593 #else
594   index_pack = AccessMutableIndexes(image);
595 #endif
596 #else
597 #if (MagickLibVersion < 0x0669)
598   index_pack = GetIndexes(image);
599 #else
600   index_pack = GetAuthenticIndexQueue(image);
601 #endif
602 #endif
603   if (image->storage_class == PseudoClass && !index_pack)
604   {
605     fprintf(stderr,"PseudoClass && index_pack == NULL!!!");
606     return;
607   }
608 
609 
610   if (image->storage_class == PseudoClass && image->colors <= 256)
611   {
612     for (l = 0; l < (int)image->colors; l++)
613     {
614       // Need to check how to do this for ANY image, as
615       // ImageMagick can read in all sorts of image files
616       temp_pack = image->colormap[l];
617       //if (debug_level & 512)
618       //    fprintf(stderr,"Colormap color is %i  %i  %i \n",
619       //           temp_pack.red, temp_pack.green, temp_pack.blue);
620 
621       // Here's a tricky bit:  PixelPacket entries are defined as
622       // Quantum's.  Quantum is defined in
623       // /usr/include/magick/image.h as either an unsigned short
624       // or an unsigned char, depending on what "configure"
625       // decided when ImageMagick was installed.  We can determine
626       // which by looking at MaxRGB or QuantumDepth.
627       //
628       if (QuantumDepth == 16)    // Defined in /usr/include/magick/image.h
629       {
630         if (debug_level & 512)
631         {
632           fprintf(stderr,"Color quantum is [0..65535]\n");
633         }
634         my_colors[l].red   = temp_pack.red * raster_map_intensity;
635         my_colors[l].green = temp_pack.green * raster_map_intensity;
636         my_colors[l].blue  = temp_pack.blue * raster_map_intensity;
637       }
638       else    // QuantumDepth = 8
639       {
640         if (debug_level & 512)
641         {
642           fprintf(stderr,"Color quantum is [0..255]\n");
643         }
644         my_colors[l].red   = (temp_pack.red * 256) * raster_map_intensity;
645         my_colors[l].green = (temp_pack.green * 256) * raster_map_intensity;
646         my_colors[l].blue  = (temp_pack.blue * 256) * raster_map_intensity;
647       }
648 
649       // Get the color allocated on < 8bpp displays. pixel color is written to my_colors.pixel
650       if (visual_type == NOT_TRUE_NOR_DIRECT)
651       {
652 //                XFreeColors(XtDisplay(w), cmap, &(my_colors[l].pixel),1,0);
653         XAllocColor(XtDisplay(w), cmap, &my_colors[l]);
654       }
655       else
656       {
657         pack_pixel_bits(my_colors[l].red, my_colors[l].green, my_colors[l].blue,
658                         &my_colors[l].pixel);
659       }
660 
661       //if (debug_level & 512)
662       //    fprintf(stderr,"Color allocated is %li  %i  %i  %i \n", my_colors[l].pixel,
663       //           my_colors[l].red, my_colors[l].blue, my_colors[l].green);
664     }
665   }
666 
667   // loop over image pixel rows
668   for (image_row = 0; image_row < image->rows; image_row++)
669   {
670 
671     HandlePendingEvents(app_context);
672     if (interrupt_drawing_now)
673     {
674       // Update to screen
675       (void)XCopyArea(XtDisplay(da),
676                       pixmap,
677                       XtWindow(da),
678                       gc,
679                       0,
680                       0,
681                       (unsigned int)screen_width,
682                       (unsigned int)screen_height,
683                       0,
684                       0);
685       return;
686     }
687 
688     scr_y = image_row + offsety;
689 
690     // loop over image pixel colums
691     for (image_col = 0; image_col < image->columns; image_col++)
692     {
693       scr_x = image_col + offsetx;
694       // now copy a pixel from the image to the screen
695       l = image_col + (image_row * image->columns);
696       if (image->storage_class == PseudoClass)
697       {
698         // Make matte transparent
699         if (xastirColorsMatch(pixel_pack[l],image->matte_color))
700         {
701           continue;
702         }
703         XSetForeground(XtDisplay(w), gc, my_colors[(int)index_pack[l]].pixel);
704       }
705       else
706       {
707         // Skip transparent pixels
708         if (pixel_pack[l].opacity == TransparentOpacity)
709         {
710           continue;
711         }
712 
713         // It is not safe to assume that the red/green/blue
714         // elements of pixel_pack of type Quantum are the
715         // same as the red/green/blue of an XColor!
716         if (QuantumDepth==16)
717         {
718           my_colors[0].red=pixel_pack[l].red;
719           my_colors[0].green=pixel_pack[l].green;
720           my_colors[0].blue=pixel_pack[l].blue;
721         }
722         else   // QuantumDepth=8
723         {
724           // shift the bits of the 8-bit quantity so that
725           // they become the high bigs of my_colors.*
726           my_colors[0].red=pixel_pack[l].red*256;
727           my_colors[0].green=pixel_pack[l].green*256;
728           my_colors[0].blue=pixel_pack[l].blue*256;
729         }
730         // NOW my_colors has the right r,g,b range for
731         // pack_pixel_bits
732         pack_pixel_bits(my_colors[0].red * raster_map_intensity,
733                         my_colors[0].green * raster_map_intensity,
734                         my_colors[0].blue * raster_map_intensity,
735                         &my_colors[0].pixel);
736         XSetForeground(XtDisplay(w), gc, my_colors[0].pixel);
737       }
738       // write the pixel from the map image to the
739       // screen.
740       (void)XFillRectangle (XtDisplay (w),pixmap,gc,scr_x,scr_y,1,1);
741     } // loop over map pixel columns
742   } // loop over map pixel rows
743 
744   return;
745 }  // end draw_image()
746 
747 
748 /**********************************************************
749  * draw_OSM_image() - copy map image to display
750  **********************************************************/
751 static void draw_OSM_image(
752   Widget w,
753   Image *image,
754   ExceptionInfo *except_ptr,
755   tiepoint *tpNW,
756   tiepoint *tpSE,
757   int osm_zl)
758 {
759   int l;
760   XColor my_colors[256];
761   PixelPacket *pixel_pack;
762   PixelPacket temp_pack;
763   IndexPacket *index_pack;
764   long map_image_row;
765   long map_image_col;
766   long map_x_min, map_x_max;      // map boundaries for in screen part of map
767   long map_y_min, map_y_max;
768   int map_seen = 0;
769   int map_act;
770   int map_done;
771 
772   long scr_x,  scr_y;             // screen pixel plot positions
773   long scr_xp, scr_yp;            // previous screen plot positions
774   int  scr_dx, scr_dy;            // increments in screen plot positions
775 
776   //if (debug_level & 512)
777   //    fprintf(stderr,"Color depth is %i \n", (int)image->depth);
778 
779   /*
780       if (image->colorspace != RGBColorspace) {
781           TransformImageColorspace(image, RGBColorspace);
782           //fprintf(stderr,"TBD: I don't think we can deal with colorspace != RGB");
783           //return;
784       }
785   */
786 
787   // If were are drawing to a low bpp display (typically < 8bpp)
788   // try to reduce the number of colors in an image.
789   // This may take some time, so it would be best to do ahead of
790   // time if it is a static image.
791 #if (MagickLibVersion < 0x0540)
792   if (visual_type == NOT_TRUE_NOR_DIRECT && GetNumberColors(image, NULL) > 128)
793   {
794 #else   // MagickLib >= 540
795   if (visual_type == NOT_TRUE_NOR_DIRECT && GetNumberColors(image, NULL, except_ptr) > 128)
796   {
797 #endif  // MagickLib Version
798 
799     if (image->storage_class == PseudoClass)
800     {
801 #if (MagickLibVersion < 0x0549)
802       CompressColormap(image); // Remove duplicate colors
803 #else // MagickLib >= 0x0549
804       CompressImageColormap(image); // Remove duplicate colors
805 #endif  // MagickLibVersion < 0x0549
806     }
807 
808     // Quantize down to 128 will go here...
809   }
810 
811 
812 #if defined(HAVE_GRAPHICSMAGICK) || (MagickLibVersion < 0x669)
813   pixel_pack = GetImagePixels(image, 0, 0, image->columns, image->rows);
814 #else
815   pixel_pack = GetAuthenticPixels(image, 0, 0, image->columns, image->rows, except_ptr);
816 #endif
817   if (!pixel_pack)
818   {
819     fprintf(stderr,"pixel_pack == NULL!!!");
820     return;
821   }
822 
823 #if defined(HAVE_GRAPHICSMAGICK)
824 #if (MagickLibVersion < 0x201702)
825   index_pack = GetIndexes(image);
826 #else
827   index_pack = AccessMutableIndexes(image);
828 #endif
829 #else
830 #if (MagickLibVersion < 0x0669)
831   index_pack = GetIndexes(image);
832 #else
833   index_pack = GetAuthenticIndexQueue(image);
834 #endif
835 #endif
836   if (image->storage_class == PseudoClass && !index_pack)
837   {
838     fprintf(stderr,"PseudoClass && index_pack == NULL!!!");
839     return;
840   }
841 
842 
843   if (image->storage_class == PseudoClass && image->colors <= 256)
844   {
845     for (l = 0; l < (int)image->colors; l++)
846     {
847       // Need to check how to do this for ANY image, as
848       // ImageMagick can read in all sorts of image files
849       temp_pack = image->colormap[l];
850       //if (debug_level & 512)
851       //    fprintf(stderr,"Colormap color is %i  %i  %i \n",
852       //           temp_pack.red, temp_pack.green, temp_pack.blue);
853 
854       // Here's a tricky bit:  PixelPacket entries are defined as
855       // Quantum's.  Quantum is defined in
856       // /usr/include/magick/image.h as either an unsigned short
857       // or an unsigned char, depending on what "configure"
858       // decided when ImageMagick was installed.  We can determine
859       // which by looking at MaxRGB or QuantumDepth.
860       //
861       if (QuantumDepth == 16)    // Defined in /usr/include/magick/image.h
862       {
863         //if (debug_level & 512)
864         //    fprintf(stderr,"Color quantum is [0..65535]\n");
865         my_colors[l].red   = temp_pack.red * raster_map_intensity;
866         my_colors[l].green = temp_pack.green * raster_map_intensity;
867         my_colors[l].blue  = temp_pack.blue * raster_map_intensity;
868       }
869       else    // QuantumDepth = 8
870       {
871         //if (debug_level & 512)
872         //    fprintf(stderr,"Color quantum is [0..255]\n");
873         my_colors[l].red   = (temp_pack.red * 256) * raster_map_intensity;
874         my_colors[l].green = (temp_pack.green * 256) * raster_map_intensity;
875         my_colors[l].blue  = (temp_pack.blue * 256) * raster_map_intensity;
876       }
877 
878       // Get the color allocated on < 8bpp displays. pixel color is written to my_colors.pixel
879       if (visual_type == NOT_TRUE_NOR_DIRECT)
880       {
881 //                XFreeColors(XtDisplay(w), cmap, &(my_colors[l].pixel),1,0);
882         XAllocColor(XtDisplay(w), cmap, &my_colors[l]);
883       }
884       else
885       {
886         pack_pixel_bits(my_colors[l].red, my_colors[l].green, my_colors[l].blue,
887                         &my_colors[l].pixel);
888       }
889 
890       //if (debug_level & 512)
891       //    fprintf(stderr,"Color allocated is %li  %i  %i  %i \n", my_colors[l].pixel,
892       //           my_colors[l].red, my_colors[l].blue, my_colors[l].green);
893     }
894   }
895 
896   /*
897   * Here are the corners of our viewport, using the Xastir
898   * coordinate system.  Notice that Y is upside down:
899   *
900   *   left edge of view = NW_corner_longitude
901   *  right edge of view = SE_corner_longitude
902   *    top edge of view = NW_corner_latitude
903   * bottom edge of view = SE_corner_latitude
904   *
905   * The corners of our image were calculated and stored
906   * above as tiepoints using OSM units (pixels/circle). They are:
907   *
908   *   left edge of map = tp[0].x_long
909   *  right edge of map = tp[1].x_long
910   *    top edge of map = tp[0].y_lat
911   * bottom edge of map = tp[1].y_lat
912   *
913   */
914 
915   scr_dx = 1;
916   scr_dy = 1;
917 
918   // calculate map pixel range in y direction that falls into screen area
919   map_y_min = map_y_max = 0l;
920   for (map_image_row = 0; map_image_row < (long)image->rows; map_image_row++)
921   {
922     scr_y = (pixelLat2xastirLat(map_image_row + tpNW->y_lat, osm_zl) - NW_corner_latitude) / scale_y;
923     if (scr_y > 0)
924     {
925       if (scr_y < screen_height)
926       {
927         map_y_max = map_image_row;  // update last map pixel in y
928       }
929       else
930       {
931         break;  // done, reached bottom screen border
932       }
933     }
934     else                                // pixel is above screen
935     {
936       map_y_min = map_image_row;     // update first map pixel in y
937     }
938   }
939 
940   // Calculate the position of the map image relative to the screen
941   map_x_min = map_x_max = 0l;
942   for (map_image_col = 0; map_image_col < (long)image->columns; map_image_col++)
943   {
944     scr_x = (pixelLon2xastirLon(map_image_col + tpNW->x_long, osm_zl) - NW_corner_longitude) / scale_x;
945     if (scr_x > 0)
946     {
947       if (scr_x < screen_width)
948       {
949         map_x_max = map_image_col;  // update last map pixel in x
950       }
951       else
952       {
953         break;  // done, reached right screen border
954       }
955     }
956     else                                // pixel is left from screen
957     {
958       map_x_min = map_image_col;              // update first map pixel in x
959     }
960   }
961 
962   scr_yp = -1;
963   map_done = 0;
964   map_act  = 0;
965   map_seen = 0;
966 
967   // loop over map pixel rows
968   for (map_image_row = map_y_min; (map_image_row <= map_y_max); map_image_row++)
969   {
970 
971     HandlePendingEvents(app_context);
972     if (interrupt_drawing_now)
973     {
974       // Update to screen
975       (void)XCopyArea(XtDisplay(da),
976                       pixmap,
977                       XtWindow(da),
978                       gc,
979                       0,
980                       0,
981                       (unsigned int)screen_width,
982                       (unsigned int)screen_height,
983                       0,
984                       0);
985       return;
986     }
987 
988     scr_y = (pixelLat2xastirLat(map_image_row + tpNW->y_lat, osm_zl)
989              - NW_corner_latitude) / scale_y;
990 
991     // image rows do not match 1:1 with screen rows due to Mercator
992     // scaling, so scr_dy will be passed to XFillRectangle to
993     // handle that issue.
994     // scr_dy is in rows and must be a minimum of 1 row.
995     scr_dy = ((  pixelLat2xastirLat(map_image_row + 1 + tpNW->y_lat, osm_zl)
996                  - NW_corner_latitude) / scale_y) - scr_y;
997     if (scr_dy < 1)
998     {
999       scr_dy = 1;
1000     }
1001 
1002     if (scr_y != scr_yp)                    // don't do a row twice
1003     {
1004       scr_yp = scr_y;                     // remember as previous y
1005       scr_xp = -1;
1006       // loop over map pixel columns
1007       map_act = 0;
1008       for (map_image_col = map_x_min; map_image_col <= map_x_max; map_image_col++)
1009       {
1010         scr_x = (  pixelLon2xastirLon(map_image_col + tpNW->x_long, osm_zl)
1011                    - NW_corner_longitude) / scale_x;
1012         // handle the case when here the horizontal resolution
1013         // of the image is less than the horizontal resolution
1014         // displayed. scr_dx is passed to XFillRectangle() below
1015         // and must be at least 1 column.
1016         scr_dx = ( (pixelLon2xastirLon(map_image_col + 1 + tpNW->x_long, osm_zl)
1017                     - NW_corner_longitude) / scale_x) - scr_x;
1018         if (scr_dx < 1)
1019         {
1020           scr_dx = 1;
1021         }
1022         if (scr_x != scr_xp)        // don't do a pixel twice
1023         {
1024           scr_xp = scr_x;         // remember as previous x
1025 
1026           // check map boundaries in y direction
1027           if (map_image_row >= 0 && map_image_row <= tpSE->img_y)
1028           {
1029             map_seen = 1;
1030             map_act = 1;   // detects blank screen rows (end of map)
1031 
1032             // now copy a pixel from the map image to the screen
1033             l = map_image_col + map_image_row * image->columns;
1034             if (image->storage_class == PseudoClass)
1035             {
1036               // Make matte transparent by skipping pixels
1037               if (xastirColorsMatch(pixel_pack[l],image->matte_color))
1038               {
1039                 continue;
1040               }
1041               XSetForeground(XtDisplay(w), gc, my_colors[(int)index_pack[l]].pixel);
1042             }
1043             else
1044             {
1045               // Skip transparent pixels and make matte
1046               // colored pixels transparent (by skipping)
1047               if ((pixel_pack[l].opacity == TransparentOpacity)
1048                   || (xastirColorsMatch(pixel_pack[l], image->matte_color)))
1049               {
1050                 continue;
1051               }
1052 
1053               // It is not safe to assume that the red/green/blue
1054               // elements of pixel_pack of type Quantum are the
1055               // same as the red/green/blue of an XColor!
1056               if (QuantumDepth==16)
1057               {
1058                 my_colors[0].red=pixel_pack[l].red;
1059                 my_colors[0].green=pixel_pack[l].green;
1060                 my_colors[0].blue=pixel_pack[l].blue;
1061               }
1062               else   // QuantumDepth=8
1063               {
1064                 // shift the bits of the 8-bit quantity so that
1065                 // they become the high bigs of my_colors.*
1066                 my_colors[0].red=pixel_pack[l].red*256;
1067                 my_colors[0].green=pixel_pack[l].green*256;
1068                 my_colors[0].blue=pixel_pack[l].blue*256;
1069               }
1070               // NOW my_colors has the right r,g,b range for
1071               // pack_pixel_bits
1072               pack_pixel_bits(my_colors[0].red * raster_map_intensity,
1073                               my_colors[0].green * raster_map_intensity,
1074                               my_colors[0].blue * raster_map_intensity,
1075                               &my_colors[0].pixel);
1076               XSetForeground(XtDisplay(w), gc, my_colors[0].pixel);
1077             }
1078             // write the pixel from the map image to the
1079             // screen. Strech to a rectangle as needed
1080             // specified by scr_dx and scr_dy.
1081             (void)XFillRectangle (XtDisplay (w),pixmap,gc,scr_x,scr_y,scr_dx,scr_dy);
1082           } // check map boundaries in y direction
1083         }  // don't do a screen pixel twice (in the same row)
1084       } // loop over map pixel columns
1085 
1086       if (map_seen && !map_act)
1087       {
1088         map_done = 1;
1089       }
1090       (void)map_done; // map_done is never used, but this takes away the compile warning.
1091     } // don't do a screen row twice.
1092   } // loop over map pixel rows
1093 }  // end draw_OSM_image()
1094 
1095 
1096 /**********************************************************
1097  * draw_OSM_tiles() - retrieve enough map tiles to fill the display
1098  **********************************************************/
1099 // MaxTextExtent is an ImageMagick/GraphicMagick constant
1100 #define MAX_TMPSTRING MaxTextExtent
1101 
1102 void draw_OSM_tiles (Widget w,
1103                      char *filenm,           // this is the name of the xastir map file
1104                      int destination_pixmap,
1105                      char *server_url,      // if specified in xastir map file
1106                      char *tileCacheDir,    // if specified in xastir map file
1107                      char *mapName,         // if specified in xastir map file
1108                      char *tileExt)         // if specified in xastir map file
1109 {
1110 
1111   char serverURL[MAX_FILENAME];
1112   char tileRootDir[MAX_FILENAME];
1113   char map_it[MAX_FILENAME];
1114   char short_filenm[MAX_FILENAME];
1115   int osm_zl;
1116   tileArea_t tiles;
1117   coord_t corner;
1118   tiepoint NWcorner;
1119   tiepoint SEcorner;
1120   unsigned long tilex, tiley;
1121   unsigned long tileCnt = 0;
1122   unsigned long numTiles;
1123   int interrupted = 0;
1124 
1125   ExceptionInfo exception;
1126   Image *canvas = NULL;
1127   Image *tile = NULL;
1128   ImageInfo *canvas_info = NULL;
1129   ImageInfo *tile_info = NULL;
1130   unsigned int row, col;
1131   unsigned int offset_x, offset_y;
1132   char tmpString[MAX_TMPSTRING];
1133 
1134   char temp_file_path[MAX_VALUE];
1135 
1136   // Check whether we're indexing or drawing the map
1137   if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
1138        || (destination_pixmap == INDEX_NO_TIMESTAMPS) )
1139   {
1140 
1141     // We're indexing only.  Save the extents in the index.
1142     // Force the extents to the edges of the earth for the
1143     // index file.
1144     index_update_xastir(filenm, // Filename only
1145                         64800000l,      // Bottom
1146                         0l,             // Top
1147                         0l,             // Left
1148                         129600000l,     // Right
1149                         0);             // Default Map Level
1150 
1151     // Update statusline
1152     xastir_snprintf(map_it,
1153                     sizeof(map_it),
1154                     langcode ("BBARSTA039"),  // Indexing %s
1155                     short_filenm);
1156     statusline(map_it,0);       // Indexing
1157 
1158     return; // Done indexing this file
1159   }
1160 
1161   if (tileCacheDir[0] != '\0')
1162   {
1163     if (tileCacheDir[0] == '/')
1164     {
1165       xastir_snprintf(tileRootDir, sizeof(tileRootDir),
1166                       "%s", tileCacheDir);
1167     }
1168     else
1169     {
1170       xastir_snprintf(tileRootDir, sizeof(tileRootDir),
1171                       "%s", get_user_base_dir(tileCacheDir, temp_file_path, sizeof(temp_file_path)));
1172     }
1173   }
1174   else
1175   {
1176     xastir_snprintf(tileRootDir, sizeof(tileRootDir),
1177                     "%s", get_user_base_dir("OSMtiles", temp_file_path, sizeof(temp_file_path)));
1178   }
1179 
1180   if (mapName[0] != '\0')
1181   {
1182     xastir_snprintf(tmpString, sizeof(tmpString), "/%s", mapName);
1183     strncat(tileRootDir, tmpString, sizeof(tileRootDir) - 1 - strlen(tileRootDir));
1184   }
1185 
1186   if (server_url[0] != '\0')
1187   {
1188     xastir_snprintf(serverURL, sizeof(serverURL),
1189                     "%s", server_url);
1190   }
1191   else
1192   {
1193     xastir_snprintf(serverURL, sizeof(serverURL),
1194                     "%s", "http://tile.openstreetmap.org");
1195   }
1196 
1197   if (server_url[strlen(serverURL) - 1] == '/')
1198   {
1199     serverURL[strlen(serverURL) - 1] = '\0';
1200   }
1201 
1202   // Create a shorter filename for display (one that fits the
1203   // status line more closely).  Allow space for the
1204   // "Indexing " or "Loading " strings.
1205   if (strlen(filenm) > (41 - 9))
1206   {
1207     int avail = 41 - 11;
1208     int new_len = strlen(filenm) - avail;
1209 
1210     xastir_snprintf(short_filenm,
1211                     sizeof(short_filenm),
1212                     "..%s",
1213                     &filenm[new_len]);
1214   }
1215   else
1216   {
1217     xastir_snprintf(short_filenm,
1218                     sizeof(short_filenm),
1219                     "%s",
1220                     filenm);
1221   }
1222 
1223   GetExceptionInfo(&exception);
1224 
1225   if (debug_level & 512)
1226   {
1227     unsigned long lat, lon;
1228     (void)convert_to_xastir_coordinates(&lon, &lat,
1229                                         f_NW_corner_longitude, f_NW_corner_latitude);
1230     fprintf(stderr, "NW_corner_longitude = %f, %ld, %ld\n",
1231             f_NW_corner_longitude, NW_corner_longitude, lon);
1232     fprintf(stderr, "NW_corner_latitude = %f, %ld, %ld\n",
1233             f_NW_corner_latitude, NW_corner_latitude, lat);
1234 
1235     (void)convert_to_xastir_coordinates(&lon, &lat,
1236                                         f_SE_corner_longitude, f_SE_corner_latitude);
1237     fprintf(stderr, "SE_corner_longitude = %f, %ld, %ld\n",
1238             f_SE_corner_longitude, SE_corner_longitude, lon);
1239     fprintf(stderr, "SE_corner_latitude = %f, %ld, %ld\n",
1240             f_SE_corner_latitude, SE_corner_latitude, lat);
1241   }
1242 
1243   osm_zl = osm_zoom_level(scale_x);
1244   calcTileArea(f_NW_corner_longitude, f_NW_corner_latitude,
1245                f_SE_corner_longitude, f_SE_corner_latitude,
1246                osm_zl, &tiles);
1247 
1248   xastir_snprintf(map_it, sizeof(map_it), "%s",
1249                   langcode ("BBARSTA050")); // Downloading tiles...
1250   statusline(map_it,0);
1251   XmUpdateDisplay(text);
1252 
1253   // make sure all the map directories exist
1254   mkOSMmapDirs(tileRootDir, tiles.startx, tiles.endx, osm_zl);
1255 
1256   // Check to see how many tiles need to be downloaded
1257   // A simple calculation doesn't work well here because some
1258   // (possibly all) of the tiles may exist in the cache.
1259   numTiles = tilesMissing(tiles.startx, tiles.endx, tiles.starty,
1260                           tiles.endy, osm_zl, tileRootDir,
1261                           tileExt[0] != '\0' ? tileExt : "png");
1262 
1263   // get the tiles
1264   tileCnt = 1;
1265   for (tilex = tiles.startx; tilex <= tiles.endx; tilex++)
1266   {
1267     for (tiley = tiles.starty; tiley <= tiles.endy; tiley++)
1268     {
1269       if ((numTiles > 0) & (tileCnt <= numTiles))
1270       {
1271         xastir_snprintf(map_it, sizeof(map_it), langcode("BBARSTA051"),
1272                         tileCnt, numTiles);  // Downloading tile %ls of %ls
1273         statusline(map_it,0);
1274         XmUpdateDisplay(text);
1275       }
1276 
1277       DLM_queue_tile(serverURL, tilex, tiley,
1278                      osm_zl, tileRootDir, tileExt[0] != '\0' ? tileExt : "png");
1279 
1280     }
1281   }
1282 
1283   // if the Download Manager is not using threaded (background) mode,
1284   // we need this to actually do the downloads.
1285   // In threaded mode, it does nothing
1286   DLM_do_transfers();
1287   if (interrupt_drawing_now)
1288   {
1289     interrupted = 1;
1290   }
1291 
1292   if (interrupted != 1)
1293   {
1294     // calculate tie points
1295     NWcorner.img_x = 0;
1296     NWcorner.img_y = 0;
1297     NWcorner.x_long = tiles.startx * 256;
1298     NWcorner.y_lat = tiles.starty * 256;
1299 
1300     if (debug_level & 512)
1301     {
1302       fprintf(stderr, "scale = %ld, zoom = %d\n", scale_x, osm_zl);
1303       fprintf(stderr, "NW corner:\n");
1304       fprintf(stderr, "  img_x = %d, img_y = %d\n", NWcorner.img_x, NWcorner.img_y);
1305       fprintf(stderr, "  x_long = %ld, y_lat = %ld\n", NWcorner.x_long, NWcorner.y_lat);
1306       fprintf(stderr, "req. lon = %f, lat = %f\n", f_NW_corner_longitude,
1307               f_NW_corner_latitude);
1308       tile2coord(tiles.startx, tiles.starty, osm_zl, &corner);
1309       fprintf(stderr, "ret. lon = %f, lat = %f\n", corner.lon, corner.lat);
1310       fprintf(stderr, "tile x = %li, y = %li\n", tiles.startx, tiles.starty);
1311     }
1312 
1313     // The NW corner of the next tile is the SE corner of the last tile
1314     // we fetched. So add one to the end tile numbers before calculating
1315     // the coordinates.
1316     SEcorner.img_x = (256 * ((tiles.endx + 1) - tiles.startx)) - 1;
1317     SEcorner.img_y = (256 * ((tiles.endy + 1) - tiles.starty)) - 1;
1318     SEcorner.x_long = (tiles.endx + 1) * 256;
1319     SEcorner.y_lat = (tiles.endy + 1) * 256;
1320 
1321     if (debug_level & 512)
1322     {
1323       fprintf(stderr, "SE corner:\n");
1324       fprintf(stderr, "  img_x = %d, img_y = %d\n", SEcorner.img_x, SEcorner.img_y);
1325       fprintf(stderr, "  x_long = %ld, y_lat = %ld\n", SEcorner.x_long, SEcorner.y_lat);
1326       fprintf(stderr, "req. lon = %f, lat = %f\n", f_SE_corner_longitude,
1327               f_SE_corner_latitude);
1328       tile2coord(tiles.endx + 1, tiles.endy + 1, osm_zl, &corner);
1329       fprintf(stderr, "ret. lon = %f, lat = %f\n", corner.lon, corner.lat);
1330       fprintf(stderr, "tile x = %li, y = %li\n", tiles.endx, tiles.endy);
1331     }
1332 
1333     /*
1334      * Create a canvas upon which the tiles will be composited.
1335     */
1336     canvas_info=CloneImageInfo((ImageInfo *)NULL);
1337 
1338     // Set canvas dimensions in pixels
1339     xastir_snprintf(tmpString, sizeof(tmpString), "%lix%li",
1340                     ((tiles.endx + 1) - tiles.startx) * 256,
1341                     ((tiles.endy + 1) - tiles.starty) * 256);
1342     (void)CloneString(&canvas_info->size, tmpString);
1343 
1344     /*
1345      * A file name based on a color creates an image filled
1346      * with that color. The matte color will be treated as
1347      * transparent when the completed OSM map gets copied to the X
1348      * display.
1349      */
1350     xastir_snprintf(canvas_info->filename, sizeof(canvas_info->filename),
1351                     "%s", MATTE_COLOR_STRING);
1352     canvas = ReadImage(canvas_info, &exception);
1353     if (exception.severity != UndefinedException)
1354     {
1355       CatchException(&exception);
1356       fprintf(stderr, "Could not allocate canvas to hold tiles.\n");
1357 
1358       if (canvas_info != NULL)
1359       {
1360         DestroyImageInfo(canvas_info);
1361       }
1362       return;
1363     }
1364     // Make sure that the canvas is an image type that uses the
1365     // opacity channel for compositing.
1366     SetImageType(canvas, PaletteMatteType);
1367 
1368     // Fill the image with an opaque color. Ultimately pixels that
1369     // are this color will be skipped when the image is written to
1370     // the screen.
1371     canvas->background_color.red = MATTE_RED;
1372     canvas->background_color.green = MATTE_GREEN;
1373     canvas->background_color.blue = MATTE_BLUE;
1374     canvas->background_color.opacity = MATTE_OPACITY;
1375 #if defined(HAVE_GRAPHICSMAGICK) || (MagickLibVersion < 0x0669)
1376     SetImage(canvas, MATTE_OPACITY);
1377 #else
1378     SetImageBackgroundColor(canvas);
1379     SetImageOpacity(canvas, MATTE_OPACITY);
1380 #endif
1381 
1382     xastir_snprintf(map_it, sizeof(map_it), "%s",
1383                     langcode ("BBARSTA049")); // Reading tiles...
1384     statusline(map_it,0);
1385     XmUpdateDisplay(text);
1386 
1387     tile_info = CloneImageInfo((ImageInfo *)NULL);
1388 
1389     // Read the tile and composite them onto the canvas
1390     for (col = tiles.starty, offset_y = 0;
1391          col <= tiles.endy;
1392          col++, offset_y += 256)
1393     {
1394       for (row = tiles.startx, offset_x = 0;
1395            row <= tiles.endx;
1396            row++, offset_x += 256)
1397       {
1398 
1399         xastir_snprintf(tmpString, sizeof(tmpString),
1400                         "%s/%d/%d/%d.%s", tileRootDir, osm_zl, row, col,
1401                         tileExt[0] != '\0' ? tileExt : "png");
1402         strncpy(tile_info->filename, tmpString, MaxTextExtent);
1403 
1404         tile = ReadImage(tile_info,&exception);
1405 
1406         if (exception.severity != UndefinedException)
1407         {
1408           //fprintf(stderr,"Exception severity:%d\n", exception.severity);
1409           if (exception.severity==FileOpenError)
1410           {
1411             //fprintf(stderr, "%s NOT available\n", tile_info->filename);
1412 #if !defined(HAVE_GRAPHICSMAGICK) && (MagickLibVersion > 0x0669)
1413             ClearMagickException(&exception);
1414 #endif
1415           }
1416           else
1417           {
1418             xastir_snprintf(tmpString, sizeof(tmpString), "%s/%d/%d/%d.%s",
1419                             tileRootDir, osm_zl, row, col,
1420                             tileExt[0] != '\0' ? tileExt : "png");
1421             if (debug_level & 512)
1422             {
1423               fprintf(stderr, "%s NOT removed.\n", tmpString);
1424             }
1425             else
1426             {
1427               fprintf(stderr, "Removing %s\n", tmpString);
1428               unlink(tmpString);
1429             }
1430             CatchException(&exception);
1431           }
1432           // clear exception so next iteration doesn't fail
1433           GetExceptionInfo(&exception);
1434 
1435           // replace the missing tile with a place holder
1436           //(void)strcpy(tile_info->filename, "xc:red");
1437           //tile = ReadImage(tile_info, &exception);
1438         }
1439         if (tile)
1440         {
1441           (void)CompositeImage(canvas, OverCompositeOp,
1442                                tile, offset_x, offset_y);
1443           DestroyImage(tile);
1444         }
1445       }
1446     }
1447 
1448     // Set the matte color for use in transparentency testing
1449     canvas->matte_color.red = MATTE_RED;
1450     canvas->matte_color.green = MATTE_GREEN;
1451     canvas->matte_color.blue = MATTE_BLUE;
1452 
1453     if (debug_level & 512)
1454     {
1455 #if defined(HAVE_GRAPHICSMAGICK) || (MagickLibVersion < 0x0669)
1456       DescribeImage(canvas, stderr, 0);
1457 #else
1458       IdentifyImage(canvas, stderr, 0);
1459 #endif
1460       WriteImages(canvas_info, canvas, "/tmp/xastirOSMTiledMap.png", &exception);
1461     }
1462 
1463     draw_OSM_image(w, canvas, &exception, &NWcorner, &SEcorner, osm_zl);
1464 
1465     // Display the OpenStreetMap attribution
1466     // Just resuse the tile structure rather than creating another.
1467     xastir_snprintf(tmpString, sizeof(tmpString),
1468                     "%s/CC_OpenStreetMap.png", get_data_base_dir("maps"));
1469     strncpy(tile_info->filename, tmpString, MaxTextExtent);
1470 
1471     tile = ReadImage(tile_info,&exception);
1472     if (exception.severity != UndefinedException)
1473     {
1474       CatchException(&exception);
1475     }
1476     else
1477     {
1478       draw_image(w, tile, &exception, 4, 4);
1479       DestroyImage(tile);
1480     }
1481   }
1482   else
1483   {
1484     // map draw was interrupted
1485     // Update to screen
1486     (void)XCopyArea(XtDisplay(da),
1487                     pixmap,
1488                     XtWindow(da),
1489                     gc,
1490                     0,
1491                     0,
1492                     (unsigned int)screen_width,
1493                     (unsigned int)screen_height,
1494                     0,
1495                     0);
1496   }
1497 
1498   /*
1499    * Release resources
1500   */
1501   if (tile_info != NULL)
1502   {
1503     DestroyImageInfo(tile_info);
1504   }
1505   if (canvas_info != NULL)
1506   {
1507     DestroyImageInfo(canvas_info);
1508   }
1509   if (canvas != NULL)
1510   {
1511     DestroyImage(canvas);
1512   }
1513   DestroyExceptionInfo(&exception);
1514   return;
1515 
1516 } // draw_OSM_tiles()
1517 
1518 
1519 /**********************************************************
1520  * draw_OSM_map() - retreive an image that is the size of the display
1521  **********************************************************/
1522 void draw_OSM_map (Widget w,
1523                    char *filenm,
1524                    int destination_pixmap,
1525                    char *url,
1526                    char *style,
1527                    int UNUSED(nocache) )       // For future implementation of a "refresh cached map" option
1528 {
1529   char file[MAX_FILENAME];        // Complete path/name of image file
1530   char short_filenm[MAX_FILENAME];
1531   FILE *f;                        // Filehandle of image file
1532   char fileimg[MAX_FILENAME];     // Ascii name of image file, read from GEO file
1533   char OSMtmp[MAX_FILENAME*2];    // Used for putting together the OSMmap query
1534   tiepoint tp[2];                 // Calibration points for map
1535 
1536   char local_filename[MAX_FILENAME];
1537 
1538   ExceptionInfo exception;
1539   Image *image;
1540   ImageInfo *image_info;
1541   double left, right, top, bottom;
1542   double lat_center  = 0;
1543   double long_center = 0;
1544 
1545   char map_it[MAX_FILENAME];
1546   char tmpstr[1001];
1547   int osm_zl = 18;                 // OSM zoom level, at 18, the whole
1548   // world fits in one 256x256 tile.
1549   unsigned map_image_width;        // Image width
1550   unsigned map_image_height;       // Image height
1551   // TODO: put the max_image_* limits in the .geo/.osm file because it could change on a by-server
1552   //       basis and the server URL can be specified in the .geo/.osm file.
1553   unsigned max_image_width = 2000;  // This value is for the default server
1554   unsigned max_image_height = 2000; // This value is for the default server
1555 
1556   // initialize this
1557   local_filename[0]='\0';
1558 
1559   // Create a shorter filename for display (one that fits the
1560   // status line more closely).  Subtract the length of the
1561   // "Indexing " and/or "Loading " strings as well.
1562   if (strlen(filenm) > (41 - 9))
1563   {
1564     int avail = 41 - 11;
1565     int new_len = strlen(filenm) - avail;
1566 
1567     xastir_snprintf(short_filenm,
1568                     sizeof(short_filenm),
1569                     "..%s",
1570                     &filenm[new_len]);
1571   }
1572   else
1573   {
1574     xastir_snprintf(short_filenm,
1575                     sizeof(short_filenm),
1576                     "%s",
1577                     filenm);
1578   }
1579 
1580   xastir_snprintf(map_it,
1581                   sizeof(map_it),
1582                   langcode ("BBARSTA028"),
1583                   short_filenm);
1584   statusline(map_it,0);       // Loading ...
1585   XmUpdateDisplay(text);
1586 
1587   // Check whether we're indexing or drawing the map
1588   if ( (destination_pixmap == INDEX_CHECK_TIMESTAMPS)
1589        || (destination_pixmap == INDEX_NO_TIMESTAMPS) )
1590   {
1591 
1592     // We're indexing only.  Save the extents in the index.
1593     // Force the extents to the edges of the earth for the
1594     // index file.
1595     index_update_xastir(filenm, // Filename only
1596                         64800000l,      // Bottom
1597                         0l,             // Top
1598                         0l,             // Left
1599                         129600000l,     // Right
1600                         0);             // Default Map Level
1601 
1602     // Update statusline
1603     xastir_snprintf(map_it,
1604                     sizeof(map_it),
1605                     langcode ("BBARSTA039"),
1606                     short_filenm);
1607     statusline(map_it,0);       // Loading/Indexing ...
1608 
1609     return; // Done indexing this file
1610   }
1611 
1612   // calculate the OSM zoom level (osm_zl) that is nearest the xastir scale
1613   osm_zl = osm_zoom_level(scale_x);
1614 
1615   // Calculate the image size to request. The size will be saved as tiepoints
1616   // for the top-left and bottom-right of the image.
1617   tp[0].x_long = xastirLon2pixelLon(NW_corner_longitude, osm_zl); // OSM pixels
1618   tp[1].x_long = xastirLon2pixelLon(SE_corner_longitude, osm_zl); // OSM pixels
1619   tp[0].y_lat = xastirLat2pixelLat(NW_corner_latitude, osm_zl);  // OSM pixels
1620   tp[1].y_lat = xastirLat2pixelLat(SE_corner_latitude, osm_zl);  // OSM pixels
1621 
1622   map_image_height = tp[1].y_lat - tp[0].y_lat;
1623   map_image_width = tp[1].x_long - tp[0].x_long;
1624 
1625   // Limit dimensions to the max the server will allow.
1626   if (map_image_width > max_image_width)
1627   {
1628     int tmp = ((map_image_width - map_image_height) / 2) + 1;
1629     tp[0].x_long += tmp;
1630     tp[1].x_long -= tmp;
1631     map_image_width = tp[1].x_long - tp[0].x_long;
1632   }
1633 
1634   if (map_image_height > max_image_height)
1635   {
1636     int tmp = ((map_image_height - max_image_height) / 2) + 1;
1637     tp[0].y_lat += tmp;
1638     tp[1].y_lat -= tmp;
1639     map_image_height = tp[1].y_lat - tp[0].y_lat;
1640   }
1641 
1642   // Size and coordinates for the tiepoints in pixels
1643   tp[0].img_x = 0;
1644   tp[0].img_y = 0;
1645   tp[1].img_x = map_image_width - 1;
1646   tp[1].img_y = map_image_height - 1;
1647 
1648   // calculate the center coordinates for the image request
1649   left = (double)((NW_corner_longitude - 64800000l )/360000.0);   // Lat/long Coordinates, degrees
1650   top = (double)(-((NW_corner_latitude - 32400000l )/360000.0));  // Lat/long Coordinates, degrees
1651   right = (double)((SE_corner_longitude - 64800000l)/360000.0);   //Lat/long Coordinates, degrees
1652   bottom = (double)(-((SE_corner_latitude - 32400000l)/360000.0));//Lat/long Coordinates, degrees
1653 
1654   long_center = (left + right)/2.0l; // degrees
1655 
1656   // The vertical center of the image must be calculated from the OSM image size to
1657   // compensate for latitude scaling (Mercator). This is particularly important for small image/screen
1658   // sizes and may not be apparent on large displays.
1659   lat_center = pixelLat2Lat((map_image_height / 2) + tp[0].y_lat, osm_zl);
1660 
1661   /*
1662    * Query format to the StaticMap
1663    * See: http://ojw.dev.openstreetmap.org/StaticMap/?mode=API&
1664    *
1665    * http://ojw.dev.openstreetmap.org/StaticMap/?lat=LL.LLLLLL&lon=-LLL.LLLLL&z=15& \
1666    *     w=WWW&h=HHH&layer=osmarender&mode=Export&att=none&show=1
1667    */
1668 
1669   if (url[0] != '\0')
1670   {
1671     xastir_snprintf(OSMtmp, sizeof(OSMtmp), "%s", url);
1672   }
1673   else
1674   {
1675     xastir_snprintf(OSMtmp, sizeof(OSMtmp), "http://ojw.dev.openstreetmap.org/StaticMap/");
1676   }
1677   //xastir_snprintf(tmpstr, sizeof(tmpstr), "?mode=Export&att=text&show=1&");
1678   xastir_snprintf(tmpstr, sizeof(tmpstr), "?mode=Export&show=1&");
1679   strncat (OSMtmp, tmpstr, sizeof(OSMtmp) - 1 - strlen(OSMtmp));
1680 
1681   if (style[0] != '\0')
1682   {
1683     xastir_snprintf(tmpstr, sizeof(tmpstr), "%s", style);
1684     strncat (OSMtmp, tmpstr, sizeof(OSMtmp) - 1 - strlen(OSMtmp));
1685   }
1686   else
1687   {
1688     xastir_snprintf(tmpstr, sizeof(tmpstr), "layer=osmarender&");
1689     strncat (OSMtmp, tmpstr, sizeof(OSMtmp) - 1 - strlen(OSMtmp));
1690   }
1691 
1692   xastir_snprintf(tmpstr, sizeof(tmpstr), "&lat=%f\046lon=%f\046", lat_center, long_center);
1693   strncat (OSMtmp, tmpstr, sizeof(OSMtmp) - 1 - strlen(OSMtmp));
1694 
1695   xastir_snprintf(tmpstr, sizeof(tmpstr), "w=%i\046h=%i\046", map_image_width, map_image_height);
1696   strncat (OSMtmp, tmpstr, sizeof(OSMtmp) - 1 - strlen(OSMtmp));
1697 
1698   xastir_snprintf(tmpstr, sizeof(tmpstr), "z=%d", osm_zl);
1699   strncat (OSMtmp, tmpstr, sizeof(OSMtmp) - 1 - strlen(OSMtmp));
1700 
1701   memcpy(fileimg, OSMtmp, sizeof(fileimg));
1702   fileimg[sizeof(fileimg)-1] = '\0';  // Terminate string
1703 
1704   if (debug_level & 512)
1705   {
1706     fprintf(stderr,"left side is %f\n", left);
1707     fprintf(stderr,"right side is %f\n", right);
1708     fprintf(stderr,"top  is %f\n", top);
1709     fprintf(stderr,"bottom is %f\n", bottom);
1710     fprintf(stderr,"lat center is %f\n", lat_center);
1711     fprintf(stderr,"long center is %f\n", long_center);
1712     fprintf(stderr,"screen width is %li\n", screen_width);
1713     fprintf(stderr,"screen height is %li\n", screen_height);
1714     fprintf(stderr,"OSM image width is %i\n", map_image_width);
1715     fprintf(stderr,"OSM image height is %i\n", map_image_height);
1716     fprintf(stderr,"scale_y = %li\n", scale_y);
1717     fprintf(stderr,"scale_x = %li\n", scale_x);
1718     fprintf(stderr,"OSM zoom level = %i\n", osm_zl);
1719     fprintf(stderr,"fileimg is %s\n", fileimg);
1720     fprintf(stderr,"ftp or http file: %s\n", fileimg);
1721   }
1722 
1723   HandlePendingEvents(app_context);
1724   if (interrupt_drawing_now)
1725   {
1726     // Update to screen
1727     (void)XCopyArea(XtDisplay(da),
1728                     pixmap,
1729                     XtWindow(da),
1730                     gc,
1731                     0,
1732                     0,
1733                     (unsigned int)screen_width,
1734                     (unsigned int)screen_height,
1735                     0,
1736                     0);
1737     return;
1738   }
1739 
1740   get_OSM_local_file(local_filename,fileimg);
1741 
1742   // Tell ImageMagick where to find it
1743   xastir_snprintf(file,
1744                   sizeof(file),
1745                   "%s",
1746                   local_filename);
1747 
1748   GetExceptionInfo(&exception);
1749 
1750   image_info=CloneImageInfo((ImageInfo *) NULL);
1751 
1752   xastir_snprintf(image_info->filename,
1753                   sizeof(image_info->filename),
1754                   "%s",
1755                   file);
1756 
1757   if (debug_level & 512)
1758   {
1759     fprintf(stderr,"Copied %s into image info.\n", file);
1760     fprintf(stderr,"image_info got: %s\n", image_info->filename);
1761     fprintf(stderr,"Entered ImageMagick code.\n");
1762     fprintf(stderr,"Attempting to open: %s\n", image_info->filename);
1763   }
1764 
1765   // We do a test read first to see if the file exists, so we
1766   // don't kill Xastir in the ReadImage routine.
1767   f = fopen (image_info->filename, "r");
1768   if (f == NULL)
1769   {
1770     if (debug_level & 512)
1771     {
1772       fprintf(stderr,"File could not be read\n");
1773     }
1774 
1775 #ifdef USE_MAP_CACHE
1776 
1777     // clear from cache if bad
1778     if (map_cache_del(fileimg))
1779     {
1780       if (debug_level & 512)
1781       {
1782         fprintf(stderr,"Couldn't delete unreadable map from cache\n");
1783       }
1784     }
1785 #endif
1786 
1787     if (image_info)
1788     {
1789       DestroyImageInfo(image_info);
1790     }
1791     DestroyExceptionInfo(&exception);
1792     return;
1793   }
1794   (void)fclose (f);
1795 
1796 
1797   image = ReadImage(image_info, &exception);
1798 
1799   if (image == (Image *) NULL)
1800   {
1801     MagickWarning(exception.severity, exception.reason, exception.description);
1802     //fprintf(stderr,"MagickWarning\n");
1803 
1804 #ifdef USE_MAP_CACHE
1805     // clear from cache if bad
1806     if (map_cache_del(fileimg))
1807     {
1808       if (debug_level & 512)
1809       {
1810         fprintf(stderr,"Couldn't delete map from cache\n");
1811       }
1812     }
1813 #endif
1814 
1815     if (image_info)
1816     {
1817       DestroyImageInfo(image_info);
1818     }
1819     DestroyExceptionInfo(&exception);
1820     return;
1821 
1822   }
1823   else if ( (image->columns != map_image_width)
1824             || (image->rows != map_image_height))
1825   {
1826     fprintf(stderr, "Server returned an image size different than requested!\n");
1827 
1828 #ifdef USE_MAP_CACHE
1829     // clear from cache if bad
1830     if (map_cache_del(fileimg))
1831     {
1832       if (debug_level & 512)
1833       {
1834         fprintf(stderr,"Couldn't delete map from cache\n");
1835       }
1836     }
1837 #endif
1838     if (image)
1839     {
1840       DestroyImage(image);
1841     }
1842     if (image_info)
1843     {
1844       DestroyImageInfo(image_info);
1845     }
1846     DestroyExceptionInfo(&exception);
1847     return;
1848   }
1849 
1850   if (debug_level & 512)
1851   {
1852     fprintf(stderr,"Image: %s\n", file);
1853     fprintf(stderr,"Image size %d %d\n", map_image_width, map_image_height);
1854 #if (MagickLibVersion < 0x0540)
1855     fprintf(stderr,"Unique colors = %d\n", GetNumberColors(image, NULL));
1856 #else // MagickLib < 540
1857     fprintf(stderr,"Unique colors = %ld\n", GetNumberColors(image, NULL, &exception));
1858 #endif // MagickLib < 540
1859     fprintf(stderr,"image matte is %i\n", image->matte);
1860   } // debug_level & 512
1861 
1862   draw_OSM_image(w, image, &exception, &(tp[0]), &(tp[1]), osm_zl);
1863   DestroyImage(image);
1864 
1865   // Display the OpenStreetMap attribution
1866   xastir_snprintf(OSMtmp, sizeof(OSMtmp),
1867                   "%s/CC_OpenStreetMap.png", get_data_base_dir("maps"));
1868   strncpy(image_info->filename, OSMtmp, MaxTextExtent);
1869 
1870   image = ReadImage(image_info,&exception);
1871   if (exception.severity != UndefinedException)
1872   {
1873     CatchException(&exception);
1874   }
1875   else
1876   {
1877     draw_image(w, image, &exception, 4, 4);
1878   }
1879 
1880 
1881   // Clean up
1882   if (image)
1883   {
1884     DestroyImage(image);
1885   }
1886   if (image_info)
1887   {
1888     DestroyImageInfo(image_info);
1889   }
1890   DestroyExceptionInfo(&exception);
1891 
1892 }  // end draw_OSM_map()
1893 
1894 
1895 #endif //HAVE_MAGICK
1896 ///////////////////////////////////////////// End of OpenStreetMap code ///////////////////////////////////////
1897 
1898 
1899 
1900