source: grass/trunk/ps/ps.map/ps_fclrtbl.c

Last change on this file was 60153, checked in by hamish, 2 years ago

better placement of horizontal legend tick labels and units text

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id
  • Property svn:mime-type set to text/x-csrc
File size: 9.9 KB
Line 
1/* Function: ps_fcolortable
2 **
3 ** Author: Radim Blazek, leto 02
4 */
5
6#include <string.h>
7#include <grass/raster.h>
8#include <grass/glocale.h>
9
10#include "colortable.h"
11#include "local_proto.h"
12
13#define LEFT 0
14#define RIGHT 1
15#define LOWER 0
16#define UPPER 1
17#define CENTER 2
18
19#define NSTEPS 3
20#define NNSTEP 4                /* number of nice steps */
21
22int PS_fcolortable(void)
23{
24    char buf[512], *ch, *units;
25    int i, k;
26    int R, G, B;
27    DCELL dmin, dmax, val;
28    double t, l;                /* legend top, left */
29    double x1, x2, y1, y2, x, y, dy, xu, yu;
30    double width;               /* width of legend in map units */
31    double height;              /* width of legend in map units */
32    double cwidth;              /* width of one color line */
33    double lwidth;              /* line width - frame, ... */
34    double step;                /* step between two values */
35    int ncols, cur_step, ddig;
36    double nice_steps[NNSTEP] = { 1.0, 2.0, 2.5, 5.0 }; /* nice steps */
37    struct Colors colors;
38    struct FPRange range;
39    double ex, cur_d, cur_ex;
40    int do_color, horiz = FALSE;
41    double grey_color_val, margin;
42    unsigned int max_label_length = 0;
43    int label_posn, label_xref, label_yref;
44
45    /* let user know what's happenning */
46    G_message(_("Creating color table for <%s in %s>..."),
47              ct.name, ct.mapset);
48
49    /* Get color range */
50    if (Rast_read_fp_range(ct.name, ct.mapset, &range) == -1) {
51        G_warning(_("Range information not available (run r.support)"));
52        return 1;
53    }
54
55    Rast_get_fp_range_min_max(&range, &dmin, &dmax);
56
57    /* override if range command is set */
58    if (ct.range_override) {
59        dmin = ct.min;
60        dmax = ct.max;
61    }
62
63    if (dmin == dmax) {         /* if step==0 all sorts of infinite loops and DIV by 0 errors follow */
64        G_warning(_("A floating point colortable must contain a range of values"));
65        return 1;
66    }
67
68    if (Rast_read_colors(ct.name, ct.mapset, &colors) == -1)
69        G_warning(_("Unable to read colors for colorbar"));
70
71    do_color = (PS.grey == 0 && PS.level == 2);
72
73    /* set font name, size, and color */
74    set_font_name(ct.font);
75    set_font_size(ct.fontsize);
76    set_ps_color(&ct.color);
77
78    /* set colortable location,  */
79    /* if height and width are not given, calculate defaults */
80    if (ct.width <= 0)
81        ct.width = 2 * ct.fontsize / 72.0;
82    if (ct.height <= 0) {
83        if (ct.width < 1.5)
84            ct.height = 10 * ct.fontsize / 72.0;
85        else /* very wide and height not set triggers a horizontal legend */
86            ct.height = 1.5 * ct.fontsize / 72.0;
87    }
88
89    dy = 1.5 * ct.fontsize;
90
91    G_debug(3, "pwidth = %f pheight = %f", PS.page_width, PS.page_height);
92    G_debug(3, "ct.width = %f ct.height = %f", ct.width, ct.height);
93    G_debug(3, "ct.x = %f ct.y = %f", ct.x, ct.y);
94
95    /* reset position to get at least something in BBox */
96    if (ct.y < PS.top_marg) {   /* higher than top margin */
97        G_warning(_("Colorbar y location beyond page margins. Adjusting."));
98        ct.y = PS.top_marg + 0.1;
99    }
100    else if (ct.y > PS.page_height - PS.bot_marg) {
101        /* lower than bottom margin - simply move one inch up from bottom margin */
102        G_warning(_("Colorbar y location beyond page margins. Adjusting."));
103        ct.y = PS.page_height - PS.bot_marg - 1;
104    }
105    t = 72.0 * (PS.page_height - ct.y);
106
107    if (ct.x < PS.left_marg) {
108        G_warning(_("Colorbar x location beyond page margins. Adjusting."));
109        ct.x = PS.left_marg + 0.1;
110    }
111    else if (ct.x > PS.page_width - PS.right_marg) {
112        /* move 1 inch to the left from right marg */
113        G_warning(_("Colorbar x location beyond page margins. Adjusting."));
114        ct.x = PS.page_width - PS.right_marg - 1;
115    }
116    l = 72.0 * ct.x;
117
118    G_debug(3, "corrected ct.x = %f ct.y = %f", ct.x, ct.y);
119
120    /* r = l + 72.0 * ct.width; */ /* unused */
121
122    /* Calc number of colors to print */
123    width = 72.0 * ct.width;
124    height = 72.0 * ct.height;
125    cwidth = 0.1;
126
127    if (width > height) {
128        horiz = TRUE;
129        ncols = (int)width / cwidth;
130        dy *= 1.4;  /* leave a bit more space so the tick labels don't overlap */
131    }
132    else
133        ncols = (int)height / cwidth;
134
135    step = (dmax - dmin) / (ncols - 1);
136    lwidth = ct.lwidth;  /* line width */
137
138    /* Print color band */
139    if (horiz) {
140        x = l + width;
141        y1 = t + height;
142        y2 = t;
143    }
144    else {  /* vertical */
145        y = t;
146        x1 = l;
147        x2 = x1 + width;
148    }
149
150    fprintf(PS.fp, "%.8f W\n", cwidth);
151
152    for (i = 0; i < ncols; i++) {
153        /*      val = dmin + i * step;   flip */
154        val = dmax - i * step;
155        Rast_get_d_color(&val, &R, &G, &B, &colors);
156
157        if (do_color)
158            fprintf(PS.fp, "%.3f %.3f %.3f C\n", (double)R / 255.,
159                    (double)G / 255., (double)B / 255.);
160        else {
161            grey_color_val =
162                (.3 * (double)R + .59 * (double)G + .11 * (double)B) / 255.;
163            fprintf(PS.fp, "%.3f setgray\n", grey_color_val);
164        }
165
166        fprintf(PS.fp, "NP\n");
167        if (horiz) {
168            fprintf(PS.fp, "%f %f M\n", x, y1);
169            fprintf(PS.fp, "%f %f LN\n", x, y2);
170            x -= cwidth;
171        }
172        else {  /* vertical */
173            fprintf(PS.fp, "%f %f M\n", x1, y);
174            fprintf(PS.fp, "%f %f LN\n", x2, y);
175            y -= cwidth;
176        }
177        fprintf(PS.fp, "D\n");
178    }
179
180    /* Frame around */
181    fprintf(PS.fp, "NP\n");
182    set_ps_color(&ct.color);
183    fprintf(PS.fp, "%.8f W\n", lwidth);
184    if (horiz) {
185        fprintf(PS.fp, "%f %f %f %f B\n",
186                l + width + (cwidth + lwidth) / 2, y1,
187                l + width - (ncols - 1) * cwidth - (cwidth + lwidth) / 2, y2);
188    }
189    else {
190        fprintf(PS.fp, "%f %f %f %f B\n", x1,
191                t - (ncols - 1) * cwidth - (cwidth + lwidth) / 2, x2,
192                t + (cwidth + lwidth) / 2);
193    }
194    fprintf(PS.fp, "D\n");
195
196    /* Print labels */
197    /* maximum number of parts we can divide into */
198    k = (ncols - 1) * cwidth / dy;
199    /* step in values for labels */
200    step = (dmax - dmin) / k;
201
202    /* raw step - usually decimal number with many places, not nice */
203    /* find nice step and first nice value for label: nice steps are
204     * 1, 2, 2.5 or 5 * 10^n,
205     * we need nice step which is first >= raw step, we take each nice
206     * step and find 'n' and then compare differences */
207
208    for (i = 0; i < NNSTEP; i++) {
209        /* smalest n for which nice step >= raw step */
210        if (nice_steps[i] <= step) {
211            ex = 1;
212            while (nice_steps[i] * ex < step)
213                ex *= 10;
214        }
215        else {
216            ex = 0.1;
217            while (nice_steps[i] * ex > step)
218                ex *= 0.1;
219            ex *= 10;
220        }
221        if (i == 0 || (nice_steps[i] * ex - step) < cur_d) {
222            cur_step = i;
223            cur_d = nice_steps[i] * ex - step;
224            cur_ex = ex;
225        }
226    }
227    step = nice_steps[cur_step] * cur_ex;
228
229    /* Nice first value: first multiple of step >= dmin */
230    k = dmin / step;
231    val = k * step;
232    if (val < dmin)
233        val += step;
234
235    if (horiz) {
236        y2 = t - 0.37 * height;
237        if (height > 36)
238            y2 = t - 0.37 * 36;
239
240        if (ct.tickbar)         /* this is the switch to draw tic all the way through bar */
241            y1 = t + height;
242        else
243            y1 = t;
244    }
245    else {
246        x1 = l + width + 0.1;
247        x2 = x1 + 0.37 * width;
248        if (width > 36)
249            x2 = x1 + 0.37 * 36;
250
251        if (ct.tickbar)
252            x1 -= width;
253    }
254
255    /* do nice label: we need so many decimal places to hold all step decimal digits */
256    if (step > 100) {           /* nice steps do not have > 2 digits, important separate, otherwise */
257        ddig = 0;               /* we can get something like 1000000.00000000765239 */
258    }
259    else {
260        sprintf(buf, "%.10f", step);
261        k = strlen(buf) - 1;
262        while (buf[k] == '0')
263            k--;
264        k = k - (int)(strchr(buf, '.') - buf);
265        ddig = k;
266    }
267
268    fprintf(PS.fp, "%.8f W\n", lwidth);
269
270    margin = 0.2 * ct.fontsize;
271    if (margin < 2)
272        margin = 2;
273
274    while (val <= dmax) {
275        fprintf(PS.fp, "NP\n");
276
277        if (horiz) {
278            x = l + width - (dmax - val) * width / (dmax - dmin);
279            fprintf(PS.fp, "%f %f M\n", x, y1);
280            fprintf(PS.fp, "%f %f LN\n", x, y2);
281        }
282        else {
283            /*  y = t - (val - dmin) * height / (dmax - dmin) ;   *** flip */
284            y = t - (dmax - val) * height / (dmax - dmin);
285            fprintf(PS.fp, "%f %f M\n", x1, y);
286            fprintf(PS.fp, "%f %f LN\n", x2, y);
287        }
288
289        fprintf(PS.fp, "D\n");
290
291        sprintf(buf, "%f", val);
292        ch = (char *)strchr(buf, '.');
293        ch += ddig;
294        if (ddig > 0)
295            ch++;
296        *ch = '\0';
297
298        if(strlen(buf) > max_label_length)
299            max_label_length = strlen(buf);
300
301        if (horiz)
302            fprintf(PS.fp,
303                    "%f %f M (%s) dup stringwidth pop 2 div neg 0 rmoveto show\n",
304                    x, y2 - margin/2 - ct.fontsize, buf);
305        else
306            fprintf(PS.fp, "(%s) %f %f MS\n", buf, x2 + 0.2 * ct.fontsize,
307                    y - 0.35 * ct.fontsize);
308
309        val += step;
310    }
311
312
313    /* print units label, if present */
314    units = Rast_read_units(ct.name, ct.mapset);
315    if (!units)
316        units = "";
317
318    if(strlen(units)) {
319        fprintf(PS.fp, "/mg %.1f def\n", margin);
320
321        /* Hint from Glynn:
322           You can use the `stringwidth` operator to obtain the width of a string
323           (the distance that the current point will move if you `show` the string).
324           If the string is short, you can obtain the bounding box with a combination
325           of the `charpath` and `pathbbox` operators (if the string is long,
326           `charpath` may overflow the maximum path length). */
327
328        /* select label position */
329        if (horiz)
330            label_posn = 5;
331        else
332            label_posn = 3;
333        /*  1 2
334              3
335            5 4 */
336
337        switch (label_posn) {
338        case 1:
339            /* above the tick numbers */
340            xu = x1;
341            yu = t + 0.05*72;
342            label_xref = LEFT;
343            label_yref = LOWER;
344            break;
345        case 2:
346            /* directly above the tick numbers */
347            xu = x2 + 0.2 * ct.fontsize;
348            yu = t + 0.05*72;
349            label_xref = LEFT;
350            label_yref = LOWER;
351            break;
352        case 3:
353            /* to the right of the tick numbers */
354            xu = 0.15*72 + max_label_length * ct.fontsize * 0.5;
355            xu += x2;
356            yu = t - height/2;
357            label_xref = LEFT;
358            label_yref = CENTER;
359            break;
360        case 4:
361            /* directly below the tick numbers */
362            xu = x2 + 0.2 * ct.fontsize;
363            yu = t - height - 0.05*72;
364            label_xref = LEFT;
365            label_yref = UPPER;
366            break;
367        case 5:
368            /* below the tick numbers */
369            if (horiz) {
370                xu = l + width/2.;
371                yu = y2 - margin - ct.fontsize;
372                label_xref = CENTER;
373            }
374            else {
375                xu = x1;
376                yu = t - height - 0.05*72;
377                label_xref = LEFT;
378            }
379            label_yref = UPPER;
380            break;
381        }
382
383        text_box_path(xu, yu, label_xref, label_yref, units, 0);
384        fprintf(PS.fp, "TIB\n");
385        set_rgb_color(BLACK);
386    }
387
388    Rast_free_colors(&colors);
389
390    return 0;
391}
Note: See TracBrowser for help on using the repository browser.