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