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