source: trunk/src/astrometry/render/tilerender.c @ 12166

Revision 12166, 24.8 KB checked in by dstn, 15 months ago (diff)

tilerender: configurable bg color; match files

Line 
1/*
2   This file is part of the Astrometry.net suite.
3   Copyright 2007 Keir Mierle and Dustin Lang.
4   Copyright 2009 Dustin Lang.
5
6   The Astrometry.net suite is free software; you can redistribute
7   it and/or modify it under the terms of the GNU General Public License
8   as published by the Free Software Foundation, version 2.
9
10   The Astrometry.net suite is distributed in the hope that it will be
11   useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with the Astrometry.net suite ; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18*/
19#include <stdio.h>
20#include <stdlib.h>
21#include <math.h>
22#include <errno.h>
23#include <string.h>
24#include <limits.h>
25#include <unistd.h>
26#include <sys/param.h>
27#include <assert.h>
28#include <stdarg.h>
29
30#include <zlib.h>
31#include <cairo.h>
32
33#include "an-bool.h"
34#include "tilerender.h"
35#include "starutil.h"
36#include "cairoutils.h"
37#include "ioutils.h"
38#include "fitsioutils.h"
39#include "bl.h"
40#include "log.h"
41#include "errors.h"
42
43#include "render_tycho.h"
44#include "render_gridlines.h"
45#include "render_usnob.h"
46#include "render_rdls.h"
47#include "render_boundary.h"
48#include "render_constellation.h"
49#include "render_messier.h"
50#include "render_solid.h"
51#include "render_images.h"
52#include "render_cairo.h"
53#include "render_skdt.h"
54#include "render_quads.h"
55#include "render_match.h"
56#include "render_healpixes.h"
57
58// Ugh, zlib before 1.2.0 didn't include compressBound()...
59// And ZLIB_VERNUM wasn't defined until 1.2.0.2
60#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1200)
61// This was copy-n-pasted directly from the zlib source code version 1.2.3:
62/* compress.c -- compress a memory buffer
63 * Copyright (C) 1995-2003 Jean-loup Gailly.
64 * For conditions of distribution and use, see copyright notice in zlib.h
65 */
66uLong compressBound (uLong sourceLen) {
67  return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
68}
69#endif
70
71/**
72  This program gets called by "tile.php"/django in response to a
73  client requesting a Google maps tile.  The coordinates of the tile
74  are specified as a range of RA and DEC values.
75
76  The RA coordinates are passed as    -x <lower-RA> -X <upper-RA>
77  The DEC coordinates are             -y <lower-DEC> -Y <upper-DEC>
78  The width and height in pixels are  -w <width> -h <height>
79  */
80
81static void print_help(char* prog) {
82        printf("Usage: %s\n"
83                   "\n"
84                   "     These four flags are required: they specify the rectangle in RA,Dec space\n"
85                   "     that this tile will cover.\n"
86                   "  -x <lower-RA>\n"
87                   "  -X <upper-RA>\n"
88                   "  -y <lower-Dec>\n"
89                   "  -Y <upper-Dec>\n"
90                   "\n"
91                   "     If you use this flag, the coordinates are instead interpreted as Mercator\n"
92                   "     coordinates in the unit square.\n"
93                   "  [-M]: in Mercator coords\n"
94                   "\n"
95                   "     These two flags are required: they specify the output image size.\n"
96                   "  -w <image-width>\n"
97                   "  -h <image-height>\n"
98                   "\n"
99                   "     Output options: the default is to write a PNG image to standard out.\n"
100                   "  [-J]: write jpeg\n"
101                   "  [-R]: write a raw floating-point image\n"
102                   "\n"
103                   "\n"
104                   "     Argument files: in order to pass a large number of filenames or other arguments,\n"
105                   "     you can pass a file containing:\n"
106                   "          <keyword> <arguments>\n"
107                   "          <keyword> <arguments>\n"
108                   "          ...\n"
109                   "  [-A <argument-file>]\n"
110                   "\n"
111                   "\n"
112                   "     Layers: the output image is rendered one layer at a time, in the specified order.\n"
113                   "             Each rendering layer takes its own arguments, listed below.\n"
114                   "  -l <layer-name> [-l <layer-name> ...]\n"
115                   "\n"
116                   "  -l solid   -- Renders a solid, opaque, black background.\n"
117                   "                By default the background is black but transparent.\n"
118                   "\n"
119                   "  -l tycho   -- Renders Tycho-2 stars.\n"
120                   "    -T <tycho-mkdt-file>: path to the Tycho-2 Mercator-kdtree data file.\n"
121                   "                          This file can be created with \"make-merctree\".\n"
122                   "    [-c]: apply Hogg's color-correction\n"
123                   "    [-s]: apply arcsinh brightness mapping\n"
124                   "    [-q]: apply sqrt brightness mapping\n"
125                   "    [-e <scale>]: apply nonlinearities at the given scale\n"
126                   "    [-g <gain>]: apply this gain factor to brightnesses (makes the stars brighter)\n"
127                   "\n"
128                   "  -l images  -- Renders images.\n"
129                   "    [-n]: plot pixel density, not the pixels themselves.\n"
130                   "    [-s, -e, -g]: as above.\n"
131                   "                Reads from argument file (-A):\n"
132                   "     wcsfn <wcs-file>\n"
133                   "     jpegfn <jpeg-file>\n"
134                   "\n"
135                   "  -l quads   -- Renders Astrometry.net index qudas\n"
136                   "                Reads from argument file (-A):\n"
137                   "     index <index-filename>\n"
138                   "\n"
139                   "  -l cairo   -- Renders cairo commands.\n"
140                   "                Reads from argument file (-A):\n"
141                   "     cairo color <r> <g> <b> [<a>]\n"
142                   "     cairo moveto <ra> <dec>\n"
143                   "     cairo lineto <ra> <dec>\n"
144                   "     cairo stroke\n"
145                   "\n"
146                   "  -l healpix -- Renders healpix boundaries.\n"
147                   "     [-f <nside>]: default 1\n"
148                   "\n"
149                   "\n", prog);
150}
151
152const char* OPTIONS = "ab:c:de:f:g:h:i:k:l:npqr:svw:x:y:zA:B:C:D:F:I:JK:L:MN:PRS:T:V:W:X:Y:";
153
154struct renderer {
155        char* name;
156        render_func_t imgrender;
157        render_cairo_func_t cairorender;
158        // don't change the order of these fields!
159};
160typedef struct renderer renderer_t;
161
162/* All render layers must go in here */
163static renderer_t renderers[] = {
164        { "tycho",     render_tycho,        NULL },
165        { "grid",      NULL,                render_gridlines },
166        { "healpix",   NULL,                render_healpixes },
167        { "usnob",     render_usnob,        NULL },
168        { "rdls",      render_rdls,         NULL },
169        { "constellation", render_constellation, NULL },
170        { "messier",   render_messier,      NULL },
171        { "clean",     render_usnob,        NULL },
172        { "dirty",     render_usnob,        NULL },
173        { "solid",     NULL,                render_solid },
174        { "images",    render_images,       NULL },
175        { "userimage", render_images,       NULL },
176        { "boundaries",NULL,                render_boundary },
177        { "userboundary", NULL,             render_boundary },
178        { "userdot",   NULL,                render_boundary },
179        { "cairo",     NULL,                render_cairo },
180        { "skdt",      NULL,                render_skdt },
181        { "quads",     NULL,                render_quads },
182        { "match",     NULL,                render_match },
183};
184
185static void default_rdls_args(render_args_t* args) {
186        // Set the other RDLS-related args if they haven't been set already.
187        if (sl_size(args->rdlscolors) < sl_size(args->rdlsfns))
188                sl_append(args->rdlscolors, NULL);
189        if (il_size(args->Nstars) < sl_size(args->rdlsfns))
190                il_append(args->Nstars, 0);
191        if (il_size(args->fieldnums) < sl_size(args->rdlsfns))
192                il_append(args->fieldnums, 0);
193}
194
195void get_string_args_of_types(render_args_t* args, const char* prefixes[], int Nprefixes, sl* lst, sl* matched_prefixes) {
196    int i, j;
197    if (!args->arglist)
198        return;
199    for (i=0; i<sl_size(args->arglist); i++) {
200        char* str = sl_get(args->arglist, i);
201                for (j=0; j<Nprefixes; j++)
202                        if (starts_with(str, prefixes[j])) {
203                                sl_append(lst, str + strlen(prefixes[j]));
204                                if (matched_prefixes)
205                                        sl_append(matched_prefixes, prefixes[j]);
206                        }
207    }
208}
209
210void get_string_args_of_type(render_args_t* args, const char* prefix, sl* lst) {
211    int i;
212    int skip = strlen(prefix);
213    if (!args->arglist)
214        return;
215    for (i=0; i<sl_size(args->arglist); i++) {
216        char* str = sl_get(args->arglist, i);
217        if (starts_with(str, prefix)) {
218            sl_append(lst, str + skip);
219        }
220    }
221}
222
223int parse_rgba_arg(const char* argstr, double* rgba) {
224        dl* dlst;
225        dlst = dl_new(4);
226        get_double_args(argstr, dlst);
227        if (dl_size(dlst) != 4) {
228                logmsg("Argument: \"%s\": expected 4 numbers, got %i.\n", argstr, dl_size(dlst));
229                return -1;
230        }
231        dl_copy(dlst, 0, 4, rgba);
232        dl_free(dlst);
233        return 0;
234}
235
236int get_first_rgba_arg_of_type(render_args_t* args, const char* prefix, double* rgba) {
237        const char* argstr;
238        argstr = get_first_arg_of_type(args, prefix);
239        if (!argstr)
240                return -1;
241        return parse_rgba_arg(argstr, rgba);
242}
243
244const char* get_first_arg_of_type(render_args_t* args, const char* prefix) {
245    int i;
246    if (!args->arglist)
247        return NULL;
248    for (i=0; i<sl_size(args->arglist); i++) {
249        char* str = sl_get(args->arglist, i);
250        if (starts_with(str, prefix))
251                        return str;
252    }
253        return NULL;
254}
255
256int get_int_arg(const char* arg, int def) {
257        char* c = index(arg, ' ');
258        char* endp;
259        int val;
260        if (!c) return def;
261        val = strtol(c+1, &endp, 0);
262        if (endp == c) return def;
263        return val;
264}
265
266double get_double_arg(const char* arg, double def) {
267        char* c = index(arg, ' ');
268        char* endp;
269        double val;
270        if (!c) return def;
271        c++;
272        val = strtod(c, &endp);
273        if (endp == c) return def;
274        return val;
275}
276
277void get_double_args(const char* arg, dl* lst) {
278        char* c = index(arg, ' ');
279        char* endp;
280        double val;
281        if (!c) return;
282        while (c && *c) {
283                c++;
284                val = strtod(c, &endp);
285                if (endp == c) break;
286                dl_append(lst, val);
287                c = endp;
288        }
289}
290
291void get_double_args_of_type(render_args_t* args, const char* prefix, dl* lst) {
292    int i;
293    int skip = strlen(prefix);
294    if (!args->arglist)
295        return;
296    for (i=0; i<sl_size(args->arglist); i++) {
297        double d;
298        char* str = sl_get(args->arglist, i);
299        if (!starts_with(str, prefix))
300            continue;
301        d = atof(str + skip);
302        dl_append(lst, d);
303    }
304}
305
306double get_double_arg_of_type(render_args_t* args, const char* name, double def) {
307        double rtn;
308        dl* lst = dl_new(4);
309        get_double_args_of_type(args, name, lst);
310        if (dl_size(lst) == 0)
311                rtn = def;
312        else
313                rtn = dl_get(lst, 0);
314        dl_free(lst);
315        return rtn;
316}
317
318extern char *optarg;
319extern int optind, opterr, optopt;
320
321int main(int argc, char *argv[]) {
322        int argchar;
323        int gotx, goty, gotX, gotY, gotw, goth;
324        double xzoom;
325        unsigned char* img;
326        render_args_t args;
327        sl* layers;
328        int i;
329        bool inmerc = 0;
330    bool writejpeg = FALSE;
331        int loglvl = LOG_MSG;
332
333        if (argc == 1) {
334                print_help(argv[0]);
335                exit(0);
336        }
337
338        memset(&args, 0, sizeof(render_args_t));
339
340        // default args:
341        args.colorcor = 1.44;
342        args.linewidth = 2.0;
343
344        args.rdlsfns = sl_new(4);
345        args.rdlscolors = sl_new(4);
346        args.Nstars = il_new(4);
347        args.fieldnums = il_new(4);
348        args.imagefns = sl_new(4);
349        args.imwcsfns = sl_new(4);
350        args.argfilenames = sl_new(4);
351
352        layers = sl_new(16);
353        gotx = goty = gotX = gotY = gotw = goth = FALSE;
354
355        while ((argchar = getopt (argc, argv, OPTIONS)) != -1)
356                switch (argchar) {
357                case 'f':
358                        args.nside = atoi(optarg);
359                        break;
360                case 'T':
361                        args.tycho_mkdt = optarg;
362                        break;
363                case 'v':
364                        loglvl++;
365                        break;
366        case 'A':
367            sl_append(args.argfilenames, optarg);
368            break;
369        case 'K':
370            args.colorlist = optarg;
371            break;
372                case 'b':
373                        args.ubstyle = optarg;
374                        break;
375        case 'J':
376            writejpeg = TRUE;
377            break;
378        case 'S':
379                                args.filelist = strdup(optarg);
380                                break;
381                        case 'n':
382                                args.density = TRUE;
383                                break;
384                        case 'D':
385                                args.cachedir = strdup(optarg);
386                                break;
387                        case 'V':
388                                args.version = optarg;
389                                break;
390                        case 'z':
391                                args.zoomright = TRUE;
392                                break;
393                        case 'd':
394                                args.zoomdown = TRUE;
395                                break;
396                        case 'p':
397                                args.nopre = TRUE;
398                                break;
399                        case 'C':
400                                args.cmap = strdup(optarg);
401                                break;
402                        case 'M':
403                                inmerc = TRUE;
404                                break;
405                        case 'R':
406                                args.makerawfloatimg = 1;
407                                break;
408                        case 'B':
409                                args.dashbox = atof(optarg);
410                                break;
411                        case 'F':
412                                il_append(args.fieldnums, atoi(optarg));
413                                break;
414                        case 'N':
415                                il_append(args.Nstars, atoi(optarg));
416                                break;
417                        case 'r':
418                                default_rdls_args(&args);
419                                sl_append(args.rdlsfns, optarg);
420                                break;
421                        case 'k':
422                                sl_append(args.rdlscolors, optarg);
423                                break;
424                        case 'i':
425                                sl_append(args.imagefns, optarg);
426                                break;
427                        case 'W':
428                                args.wcsfn = strdup(optarg);
429                                break;
430                        case 'I':
431                                sl_append(args.imwcsfns, optarg);
432                                break;
433                        case 'c':
434                                args.colorcor = atof(optarg);
435                                break;
436                        case 's':
437                                args.arc = TRUE;
438                                break;
439                case 'q':
440                        args.sqrt = TRUE;
441                        break;
442                case 'e':
443                        args.nlscale = atof(optarg);
444                        break;
445                        case 'a':
446                                args.arith = TRUE;
447                                break;
448                        case 'g':
449                                args.gain = atof(optarg);
450                                break;
451                        case 'l':
452                                sl_append(layers, optarg);
453                                break;
454                        case 'L':
455                                args.linewidth = atof(optarg);
456                                break;
457                        case 'x':
458                                args.ramin = atof(optarg);
459                                gotx = TRUE;
460                                break;
461                        case 'X':
462                                args.ramax = atof(optarg);
463                                goty = TRUE;
464                                break;
465                        case 'y':
466                                args.decmin = atof(optarg);
467                                gotX = TRUE;
468                                break;
469                        case 'Y':
470                                args.decmax = atof(optarg);
471                                gotY = TRUE;
472                                break;
473                        case 'w':
474                                args.W = atoi(optarg);
475                                gotw = TRUE;
476                                break;
477                        case 'h':
478                                args.H = atoi(optarg);
479                                goth = TRUE;
480                                break;
481                }
482        log_init(loglvl);
483        log_to(stderr);
484
485        if (!(gotx && goty && gotX && gotY && gotw && goth)) {
486                logmsg("tilecache: Invalid inputs: need ");
487                if (!gotx) logmsg("-x ");
488                if (!gotX) logmsg("-X ");
489                if (!goty) logmsg("-y ");
490                if (!gotY) logmsg("-Y ");
491                if (!gotw) logmsg("-w ");
492                if (!goth) logmsg("-h ");
493                logmsg("\n");
494                exit(-1);
495        }
496
497        default_rdls_args(&args);
498
499    if (args.W > 4096 || args.H > 4096) {
500        logmsg("tilecache: Width or height too large (limit 1024)\n");
501        exit(-1);
502    }
503
504        logmsg("tilecache: BEGIN TILECACHE\n");
505
506    fits_use_error_system();
507
508        if (inmerc) {
509                // -x -X -y -Y were given in Mercator coordinates - convert to deg.
510                // this is for cases where it's more convenient to specify the coords
511                // in Merc coords (eg prerendering)
512                args.ramin  = merc2radeg(args.ramin);
513                args.ramax  = merc2radeg(args.ramax);
514                args.decmin = merc2decdeg(args.decmin);
515                args.decmax = merc2decdeg(args.decmax);
516        }
517
518        // min ra -> max merc.
519        args.xmercmax =  radeg2merc(args.ramin);
520        args.xmercmin =  radeg2merc(args.ramax);
521        args.ymercmin = decdeg2merc(args.decmin);
522        args.ymercmax = decdeg2merc(args.decmax);
523
524        // The y mercator position can end up *near* but not exactly
525        // equal to the boundary conditions... clamp.
526        args.ymercmin = MAX(0.0, args.ymercmin);
527        args.ymercmax = MIN(1.0, args.ymercmax);
528
529        args.xpixelpermerc = (double)args.W / (args.xmercmax - args.xmercmin);
530        args.ypixelpermerc = (double)args.H / (args.ymercmax - args.ymercmin);
531        args.xmercperpixel = 1.0 / args.xpixelpermerc;
532        args.ymercperpixel = 1.0 / args.ypixelpermerc;
533
534        xzoom = args.xpixelpermerc / 256.0;
535        args.zoomlevel = (int)rint(log(fabs(xzoom)) / log(2.0));
536        logmsg("tilecache: zoomlevel: %d\n", args.zoomlevel);
537
538        // Rescue boneheads.
539        if (!sl_size(layers)) {
540                logmsg("tilecache: Do you maybe want to try rendering some layers?\n");
541        }
542
543        for (i=0; i<sl_size(args.argfilenames); i++) {
544                sl* lines;
545                char* fn = sl_get(args.argfilenames, i);
546        lines = file_get_lines(fn, FALSE);
547                if (!lines) {
548                        ERROR("Failed to read args file: \"%s\"", fn);
549                        return -1;
550                }
551                if (!args.arglist)
552                        args.arglist = lines;
553                else {
554                        sl_merge_lists(args.arglist, lines);
555                        sl_free2(lines);
556                }
557        }
558
559        // Allocate a black image.
560        img = calloc(4 * args.W * args.H, 1);
561
562        for (i=0; i<sl_size(layers); i++) {
563                int j, k;
564                int NR = sizeof(renderers) / sizeof(renderer_t);
565                char* layer = sl_get(layers, i);
566                bool gotit = FALSE;
567                uchar* thisimg = calloc(4 * args.W * args.H, 1);
568
569                for (j=0; j<NR; j++) {
570                        renderer_t* r = renderers + j;
571                        int res = -1;
572                        if (!streq(layer, r->name))
573                                continue;
574                        args.currentlayer = r->name;
575                        if (r->cairorender) {
576                                // hacky... we really should do the compositing in cairo.
577                                cairo_t* cairo;
578                                cairo_surface_t* target;
579                                target = cairo_image_surface_create_for_data(thisimg, CAIRO_FORMAT_ARGB32, args.W, args.H, args.W*4);
580                                cairo = cairo_create(target);
581                                res = r->cairorender(cairo, &args);
582                                cairoutils_argb32_to_rgba(thisimg, args.W, args.H);
583                                cairo_surface_destroy(target);
584                                cairo_destroy(cairo);
585                        } else if (r->imgrender) {
586                                res = r->imgrender(thisimg, &args);
587                        } else {
588                                logmsg("tilecache: neither 'imgrender' nor 'cairorender' is defined for renderer \"%s\"\n", r->name);
589                                continue;
590                        }
591                        if (res) {
592                                logmsg("tilecache: Renderer \"%s\" failed.\n", r->name);
593                        } else {
594                                logmsg("tilecache: Renderer \"%s\" succeeded.\n", r->name);
595                        }
596                        gotit = TRUE;
597                        break;
598                }
599                // Save a different kind of bonehead.
600                if (!gotit) {
601                        logmsg("tilecache: No renderer found for layer \"%s\".\n", layer);
602                }
603
604                // Composite.
605                for (j=0; j<args.H; j++) {
606                        for (k=0; k<args.W; k++) {
607                                float alpha;
608                                uchar* newpix = pixel(k, j, thisimg, &args);
609                                uchar* accpix = pixel(k, j, img, &args);
610                                alpha = newpix[3] / 255.0;
611
612                                accpix[0] = accpix[0]*(1.0 - alpha) + newpix[0] * alpha;
613                                accpix[1] = accpix[1]*(1.0 - alpha) + newpix[1] * alpha;
614                                accpix[2] = accpix[2]*(1.0 - alpha) + newpix[2] * alpha;
615                                accpix[3] = MIN(255, accpix[3] + newpix[3]);
616                        }
617                }
618
619                free(thisimg);
620        }
621
622        if (args.makerawfloatimg) {
623                fwrite(args.rawfloatimg, sizeof(float), args.W * args.H * 3, stdout);
624                free(args.rawfloatimg);
625        } else {
626        if (writejpeg)
627            cairoutils_stream_jpeg(stdout, img, args.W, args.H);
628                else
629            cairoutils_stream_png(stdout, img, args.W, args.H);
630        }
631
632        free(img);
633
634        sl_free2(args.arglist);
635        sl_free2(args.rdlsfns);
636        sl_free2(args.rdlscolors);
637        sl_free2(args.imagefns);
638        sl_free2(args.imwcsfns);
639        sl_free2(layers);
640
641        il_free(args.Nstars);
642        il_free(args.fieldnums);
643
644        free(args.wcsfn);
645        free(args.cmap);
646
647        logmsg("tilecache: END TILECACHE\n");
648
649        return 0;
650}
651
652int parse_color(char c, double* p_r, double* p_g, double* p_b) {
653        double r, g, b;
654        switch (c) {
655        case 'r': // red
656                r = 1.0;
657                g = b = 0.0;
658                break;
659        case 'b': // blue
660                r = g = 0.0;
661                b = 1.0;
662                break;
663        case 'm': // magenta
664                r = b = 1.0;
665                g = 0.0;
666                break;
667        case 'y': // yellow
668                r = g = 1.0;
669                b = 0.0;
670                break;
671        case 'g': // green
672                r = b = 0.0;
673                g = 1.0;
674                break;
675        case 'c': // cyan
676                r = 0.0;
677                g = b = 1.0;
678                break;
679        case 'w': // white
680                r = g = b = 1.0;
681                break;
682        default:
683                return -1;
684        }
685        if (p_r) *p_r = r;
686        if (p_g) *p_g = g;
687        if (p_b) *p_b = b;
688        return 0;
689}
690
691/*
692 We need to flip RA somewhere...
693
694 We convert Longitude to RA to Mercator to Pixels.
695
696 We choose to insert the flip in the conversion from RA to Mercator.
697*/
698
699double xpixel2mercf(double pix, render_args_t* args) {
700        return args->xmercmin + pix * args->xmercperpixel;
701}
702
703double ypixel2mercf(double pix, render_args_t* args) {
704        return args->ymercmax - pix * args->ymercperpixel;
705}
706
707double xmerc2pixelf(double x, render_args_t* args) {
708        return (x - args->xmercmin) * args->xpixelpermerc;
709}
710
711double ymerc2pixelf(double y, render_args_t* args) {
712        return (args->ymercmax - y) * args->ypixelpermerc;
713}
714
715////// The following are just composed of simpler conversions.
716
717// RA in degrees
718int ra2pixel(double ra, render_args_t* args) {
719        return xmerc2pixel(radeg2merc(ra), args);
720}
721
722// DEC in degrees
723int dec2pixel(double dec, render_args_t* args) {
724        return ymerc2pixel(decdeg2merc(dec), args);
725}
726
727// RA in degrees
728double ra2pixelf(double ra, render_args_t* args) {
729        return xmerc2pixelf(radeg2merc(ra), args);
730}
731
732// DEC in degrees
733double dec2pixelf(double dec, render_args_t* args) {
734        return ymerc2pixelf(decdeg2merc(dec), args);
735}
736
737// to RA in degrees
738double pixel2ra(double pix, render_args_t* args) {
739        return merc2radeg(xpixel2mercf(pix, args));
740}
741
742// to DEC in degrees
743double pixel2dec(double pix, render_args_t* args) {
744        return merc2decdeg(ypixel2mercf(pix, args));
745}
746
747int xmerc2pixel(double x, render_args_t* args) {
748        return (int)floor(xmerc2pixelf(x, args));
749}
750
751int ymerc2pixel(double y, render_args_t* args) {
752        return (int)floor(ymerc2pixelf(y, args));
753}
754
755int in_image(int x, int y, render_args_t* args) {
756        return (x >= 0 && x < args->W && y >=0 && y < args->H);
757}
758
759int in_image_margin(int x, int y, int margin, render_args_t* args) {
760        return (x >= -margin && x < (args->W + margin) && y >= -margin && y < (args->H + margin));
761}
762
763uchar* pixel(int x, int y, uchar* img, render_args_t* args) {
764        return img + 4*(y*args->W + x);
765}
766
767// draw a line in Mercator space, handling wrap-around if necessary.
768void draw_line_merc(double mx1, double my1, double mx2, double my2,
769                cairo_t* cairo, render_args_t* args) {
770        cairo_move_to(cairo, xmerc2pixel(mx1, args), ymerc2pixel(my1, args));
771        cairo_line_to(cairo, xmerc2pixel(mx2, args), ymerc2pixel(my2, args));
772        if (MIN(mx1,mx2) < 0) {
773                cairo_move_to(cairo, xmerc2pixel(mx1+1, args), ymerc2pixel(my1, args));
774                cairo_line_to(cairo, xmerc2pixel(mx2+1, args), ymerc2pixel(my2, args));
775        }
776        if (MAX(mx1,mx2) > 1) {
777                cairo_move_to(cairo, xmerc2pixel(mx1-1, args), ymerc2pixel(my1, args));
778                cairo_line_to(cairo, xmerc2pixel(mx2-1, args), ymerc2pixel(my2, args));
779        }
780}
781
782// ra,dec in degrees.
783void draw_segmented_line(double ra1, double dec1,
784                double ra2, double dec2,
785                int SEGS,
786                cairo_t* cairo, render_args_t* args) {
787        int i, s, k;
788        double xyz1[3], xyz2[3];
789        bool wrap;
790
791        radecdeg2xyzarr(ra1, dec1, xyz1);
792        radecdeg2xyzarr(ra2, dec2, xyz2);
793
794        wrap = (fabs(ra1 - ra2) >= 180.0) ||
795                (args->ramin < 0) || (args->ramax > 360.0);
796
797        // Draw segmented line.
798        for (i=0; i<(1 + (wrap?1:0)); i++) {
799                for (s=0; s<SEGS; s++) {
800                        double xyz[3], frac;
801                        double ra, dec;
802                        double mx;
803                        double px, py;
804                        frac = (double)s / (double)(SEGS-1);
805                        for (k=0; k<3; k++)
806                                xyz[k] = xyz1[k]*(1.0-frac) + xyz2[k]*frac;
807                        normalize_3(xyz);
808                        xyzarr2radecdeg(xyz, &ra, &dec);
809                        mx = radeg2merc(ra);
810
811                        if (wrap) {
812                                // in the first pass we draw the left side (mx>0.5)
813                                if ((i==0) && (mx < 0.5)) mx += 1.0;
814                                // in the second pass we draw the right side (wx<0.5)
815                                if ((i==1) && (mx > 0.5)) mx -= 1.0;
816                        }
817                        px = xmerc2pixelf(mx, args);
818                        py = dec2pixelf(dec, args);
819
820                        if (s==0)
821                                cairo_move_to(cairo, px, py);
822                        else
823                                cairo_line_to(cairo, px, py);
824                }
825        }
826}
827
828static int cache_get_filename(render_args_t* args,
829                const char* cachedomain, const char* key,
830                char* fn, int fnlen) {
831        if (snprintf(fn, fnlen, "%s/%s/%s", args->cachedir, cachedomain, key) > fnlen) {
832                logmsg("Filename truncated in cache_load/cache_save.\n");
833                return -1;
834        }
835        return 0;
836}
837
838void* cache_load(render_args_t* args,
839                const char* cachedomain, const char* key, int* length) {
840        char fn[1024];
841        unsigned char* buf;
842        size_t len;
843        uint32_t typeid;
844        unsigned char* orig;
845        int rtn;
846        uLong origlen;
847        uint32_t* ubuf;
848
849        if (!args->cachedir)
850                return NULL;
851        if (cache_get_filename(args, cachedomain, key, fn, sizeof(fn))) {
852                return NULL;
853        }
854        if (!file_exists(fn))
855                return NULL;
856        buf = file_get_contents(fn, &len, FALSE);
857        if (!buf) {
858                logmsg("Failed to read file contents in cache_load.\n");
859                return NULL;
860        }
861        if (len < 2*sizeof(uint32_t)) {
862                logmsg("Cache file too small: \"%s\n", fn);
863                free(buf);
864                return NULL;
865        }
866
867    // Pull the two header values off the front...
868    ubuf = (uint32_t*)buf;
869        // Grab typeid.
870        typeid = ubuf[0];
871        // Grab original (uncompressed) length.
872        origlen = ubuf[1];
873
874        if (typeid != 1) {
875                logmsg("File \"%s\" does not have typeid 1.\n", fn);
876                free(buf);
877                return NULL;
878        }
879        orig = malloc(origlen);
880        if (!orig) {
881                logmsg("Failed to allocate %i bytes for uncompressed cache file \"%s\".\n", (int)origlen, fn);
882                free(buf);
883                return NULL;
884        }
885        if (length)
886                *length = origlen;
887        //logmsg("Origlen as described by the cache file: %d\n", ulen);
888        //logmsg("File size as determined by file_get_contents() = %d\n", len);
889    rtn = uncompress(orig, &origlen, buf + 2*sizeof(uint32_t), len - 2*sizeof(uint32_t));
890        free(buf);
891        if (rtn != Z_OK) {
892                logmsg("Failed to uncompress() file \"%s\": %s\n", fn, zError(rtn));
893                free(orig);
894                return NULL;
895        }
896        return orig;
897}
898
899int cache_save(render_args_t* args,
900                const char* cachedomain, const char* key,
901                const void* data, int length) {
902        char fn[1024];
903        FILE* fid;
904        uint32_t typeid;
905        unsigned char* compressed = NULL;
906        uLong complen;
907        uint32_t ulen;
908        int rtn;
909
910        if (!args->cachedir)
911                return -1;
912        if (cache_get_filename(args, cachedomain, key, fn, sizeof(fn))) {
913                return -1;
914        }
915        fid = fopen(fn, "wb");
916        if (!fid) {
917                logmsg("Failed to open cache file \"%s\": %s\n", fn, strerror(errno));
918                goto cleanup;
919        }
920
921        complen = compressBound(length);
922        compressed = malloc(complen + 2*sizeof(uint32_t));
923        if (!compressed) {
924                logmsg("Failed to allocate compressed cache buffer\n");
925                goto cleanup;
926        }
927
928        // first four bytes: type id
929        typeid = 1;
930        if (fwrite(&typeid, sizeof(uint32_t), 1, fid) != 1) {
931                logmsg("Failed to write cache file \"%s\": %s\n", fn, strerror(errno));
932                goto cleanup;
933        }
934        ulen = length;
935        if (fwrite(&ulen, sizeof(uint32_t), 1, fid) != 1) {
936                logmsg("Failed to write cache file \"%s\": %s\n", fn, strerror(errno));
937                goto cleanup;
938        }
939        rtn = compress(compressed, &complen, data, length);
940        if (rtn != Z_OK) {
941                logmsg("compress() error: %s\n", zError(rtn));
942                goto cleanup;
943        }
944        if (fwrite(compressed, 1, complen, fid) != complen) {
945                logmsg("Failed to write cache file \"%s\": %s\n", fn, strerror(errno));
946                goto cleanup;
947        }
948        if (fclose(fid)) {
949                logmsg("Failed to close cache file \"%s\": %s\n", fn, strerror(errno));
950                goto cleanup;
951        }
952
953        free(compressed);
954        return 0;
955
956cleanup:
957        free(compressed);
958        if (fid)
959                fclose(fid);
960        unlink(fn);
961        return -1;
962}
963
964
Note: See TracBrowser for help on using the repository browser.