GRASS Programmer's Manual  6.5.svn(2014)-r66266
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
plot.c
Go to the documentation of this file.
1 
2 /*****************************************************************
3  * Plot lines and filled polygons. Input space is database window.
4  * Output space and output functions are user defined.
5  * Converts input east,north lines and polygons to output x,y
6  * and calls user supplied line drawing routines to do the plotting.
7  *
8  * Handles global wrap-around for lat-lon databases.
9  *
10  * Does not perform window clipping.
11  * Clipping must be done by the line draw routines supplied by the user.
12  *
13  * Note:
14  * Hopefully, cartographic style projection plotting will be added later.
15  *******************************************************************/
16 #include <stdlib.h>
17 #include <math.h>
18 #include <grass/gis.h>
19 
20 static double xconv, yconv;
21 static double left, right, top, bottom;
22 static int ymin, ymax;
23 static struct Cell_head window;
24 static int fastline(double, double, double, double);
25 static int slowline(double, double, double, double);
26 static int plot_line(double, double, double, double, int (*)());
27 static double wrap_east(double, double);
28 static int edge(double, double, double, double);
29 static int edge_point(double, int);
30 
31 #define POINT struct point
33  double x;
34  int y;
35 };
36 static int edge_order(const void *, const void *);
37 static int row_solid_fill(int, double, double);
38 static int row_dotted_fill(int, double, double);
39 static int dotted_fill_gap = 2;
40 static int ifloor(double);
41 static int iceil(double);
42 static int (*row_fill) () = row_solid_fill;
43 static int (*move) (int, int);
44 static int (*cont) (int, int);
45 
60 /*
61  * G_setup_plot (t, b, l, r, Move, Cont)
62  * double t, b, l, r;
63  * int (*Move)(), (*Cont)();
64  *
65  * initialize the plotting capability.
66  * t,b,l,r: top, bottom, left, right of the output x,y coordinate space.
67  * Move,Cont: subroutines that will draw lines in x,y space.
68  * Move(x,y) move to x,y (no draw)
69  * Cont(x,y) draw from previous position to x,y
70  * Notes:
71  * Cont() is responsible for clipping.
72  * The t,b,l,r are only used to compute coordinate transformations.
73  * The input space is assumed to be the current GRASS window.
74  */
75 
97 int G_setup_plot(double t, double b, double l, double r,
98  int (*Move) (int, int), int (*Cont) (int, int))
99 {
101 
102  left = l;
103  right = r;
104  top = t;
105  bottom = b;
106 
107  xconv = (right - left) / (window.east - window.west);
108  yconv = (bottom - top) / (window.north - window.south);
109 
110  if (top < bottom) {
111  ymin = iceil(top);
112  ymax = ifloor(bottom);
113  }
114  else {
115  ymin = iceil(bottom);
116  ymax = ifloor(top);
117  }
118 
119  move = Move;
120  cont = Cont;
121 
122  return 0;
123 }
124 
136 int G_setup_fill(int gap)
137 {
138  if (gap > 0) {
139  row_fill = row_dotted_fill;
140  dotted_fill_gap = gap + 1;
141  }
142  else
143  row_fill = row_solid_fill;
144 
145  return 0;
146 }
147 
148 #define X(e) (left + xconv * ((e) - window.west))
149 #define Y(n) (top + yconv * (window.north - (n)))
150 
151 #define EAST(x) (window.west + ((x)-left)/xconv)
152 #define NORTH(y) (window.north - ((y)-top)/yconv)
153 
154 
168 int G_plot_where_xy(double east, double north, int *x, int *y)
169 {
170  *x = ifloor(X(G_adjust_easting(east, &window)) + 0.5);
171  *y = ifloor(Y(north) + 0.5);
172 
173  return 0;
174 }
175 
176 
190 int G_plot_where_en(int x, int y, double *east, double *north)
191 {
192  *east = G_adjust_easting(EAST(x), &window);
193  *north = NORTH(y);
194 
195  return 0;
196 }
197 
198 int G_plot_point(double east, double north)
199 {
200  int x, y;
201 
202  G_plot_where_xy(east, north, &x, &y);
203  move(x, y);
204  cont(x, y);
205 
206  return 0;
207 }
208 
209 /*
210  * Line in map coordinates is plotted in output x,y coordinates
211  * This routine handles global wrap-around for lat-long databses.
212  *
213  */
214 
230 int G_plot_line(double east1, double north1, double east2, double north2)
231 {
232  return plot_line(east1, north1, east2, north2, fastline);
233 }
234 
235 int G_plot_line2(double east1, double north1, double east2, double north2)
236 {
237  return plot_line(east1, north1, east2, north2, slowline);
238 }
239 
240 /* fastline converts double rows/cols to ints then plots
241  * this is ok for graphics, but not the best for vector to raster
242  */
243 
244 static int fastline(double x1, double y1, double x2, double y2)
245 {
246  move(ifloor(x1 + 0.5), ifloor(y1 + 0.5));
247  cont(ifloor(x2 + 0.5), ifloor(y2 + 0.5));
248 
249  return 0;
250 }
251 
252 /* NOTE (shapiro):
253  * I think the adding of 0.5 in slowline is not correct
254  * the output window (left, right, top, bottom) should already
255  * be adjusted for this: left=-0.5; right = window.cols-0.5;
256  */
257 
258 static int slowline(double x1, double y1, double x2, double y2)
259 {
260  double dx, dy;
261  double m, b;
262  int xstart, xstop, ystart, ystop;
263 
264  dx = x2 - x1;
265  dy = y2 - y1;
266 
267  if (fabs(dx) > fabs(dy)) {
268  m = dy / dx;
269  b = y1 - m * x1;
270 
271  if (x1 > x2) {
272  xstart = iceil(x2 - 0.5);
273  xstop = ifloor(x1 + 0.5);
274  }
275  else {
276  xstart = iceil(x1 - 0.5);
277  xstop = ifloor(x2 + 0.5);
278  }
279  if (xstart <= xstop) {
280  ystart = ifloor(m * xstart + b + 0.5);
281  move(xstart, ystart);
282  while (xstart <= xstop) {
283  cont(xstart++, ystart);
284  ystart = ifloor(m * xstart + b + 0.5);
285  }
286  }
287  }
288  else {
289  if (dx == dy) /* they both might be 0 */
290  m = 1.;
291  else
292  m = dx / dy;
293  b = x1 - m * y1;
294 
295  if (y1 > y2) {
296  ystart = iceil(y2 - 0.5);
297  ystop = ifloor(y1 + 0.5);
298  }
299  else {
300  ystart = iceil(y1 - 0.5);
301  ystop = ifloor(y2 + 0.5);
302  }
303  if (ystart <= ystop) {
304  xstart = ifloor(m * ystart + b + 0.5);
305  move(xstart, ystart);
306  while (ystart <= ystop) {
307  cont(xstart, ystart++);
308  xstart = ifloor(m * ystart + b + 0.5);
309  }
310  }
311  }
312 
313  return 0;
314 }
315 
316 static int plot_line(double east1, double north1, double east2, double north2,
317  int (*line) (double, double, double, double))
318 {
319  double x1, x2, y1, y2;
320 
321  y1 = Y(north1);
322  y2 = Y(north2);
323 
324  if (window.proj == PROJECTION_LL) {
325  if (east1 > east2)
326  while ((east1 - east2) > 180)
327  east2 += 360;
328  else if (east2 > east1)
329  while ((east2 - east1) > 180)
330  east1 += 360;
331  while (east1 > window.east) {
332  east1 -= 360.0;
333  east2 -= 360.0;
334  }
335  while (east1 < window.west) {
336  east1 += 360.0;
337  east2 += 360.0;
338  }
339  x1 = X(east1);
340  x2 = X(east2);
341 
342  line(x1, y1, x2, y2);
343 
344  if (east2 > window.east || east2 < window.west) {
345  while (east2 > window.east) {
346  east1 -= 360.0;
347  east2 -= 360.0;
348  }
349  while (east2 < window.west) {
350  east1 += 360.0;
351  east2 += 360.0;
352  }
353  x1 = X(east1);
354  x2 = X(east2);
355  line(x1, y1, x2, y2);
356  }
357  }
358  else {
359  x1 = X(east1);
360  x2 = X(east2);
361  line(x1, y1, x2, y2);
362  }
363 
364  return 0;
365 }
366 
367 /*
368  * G_plot_polygon (x, y, n)
369  *
370  * double *x x coordinates of vertices
371  * double *y y coordinates of vertices
372  * int n number of verticies
373  *
374  * polygon fill from map coordinate space to plot x,y space.
375  * for lat-lon, handles global wrap-around as well as polar polygons.
376  *
377  * returns 0 ok, 2 n<3, -1 weird internal error, 1 no memory
378  */
379 
380 static POINT *P;
381 static int np;
382 static int npalloc = 0;
383 
384 #define OK 0
385 #define TOO_FEW_EDGES 2
386 #define NO_MEMORY 1
387 #define OUT_OF_SYNC -1
388 
389 static double wrap_east(double e0, double e1)
390 {
391  while (e0 - e1 > 180)
392  e1 += 360.0;
393  while (e1 - e0 > 180)
394  e1 -= 360.0;
395 
396  return e1;
397 }
398 
399 
412 int G_plot_polygon(const double *x, const double *y, int n)
413 {
414  int i;
415  int pole;
416  double x0, x1;
417  double y0, y1;
418  double shift, E, W = 0L;
419  double e0, e1;
420  int shift1, shift2;
421 
422  if (n < 3)
423  return TOO_FEW_EDGES;
424 
425  /* traverse the perimeter */
426 
427  np = 0;
428  shift1 = 0;
429 
430  /* global wrap-around for lat-lon, part1 */
431  if (window.proj == PROJECTION_LL) {
432  /*
433  pole = G_pole_in_polygon(x,y,n);
434  */
435  pole = 0;
436 
437  e0 = x[n - 1];
438  E = W = e0;
439 
440  x0 = X(e0);
441  y0 = Y(y[n - 1]);
442 
443  if (pole && !edge(x0, y0, x0, Y(90.0 * pole)))
444  return NO_MEMORY;
445 
446  for (i = 0; i < n; i++) {
447  e1 = wrap_east(e0, x[i]);
448  if (e1 > E)
449  E = e1;
450  if (e1 < W)
451  W = e1;
452 
453  x1 = X(e1);
454  y1 = Y(y[i]);
455 
456  if (!edge(x0, y0, x1, y1))
457  return NO_MEMORY;
458 
459  x0 = x1;
460  y0 = y1;
461  e0 = e1;
462  }
463  if (pole && !edge(x0, y0, x0, Y(90.0 * pole)))
464  return NO_MEMORY;
465 
466  shift = 0; /* shift into window */
467  while (E + shift > window.east)
468  shift -= 360.0;
469  while (E + shift < window.west)
470  shift += 360.0;
471  shift1 = X(x[n - 1] + shift) - X(x[n - 1]);
472  }
473  else {
474  x0 = X(x[n - 1]);
475  y0 = Y(y[n - 1]);
476 
477  for (i = 0; i < n; i++) {
478  x1 = X(x[i]);
479  y1 = Y(y[i]);
480  if (!edge(x0, y0, x1, y1))
481  return NO_MEMORY;
482  x0 = x1;
483  y0 = y1;
484  }
485  }
486 
487  /* check if perimeter has odd number of points */
488  if (np % 2) {
489  G_warning("Weird internal error: perimeter has odd number of points");
490  return OUT_OF_SYNC;
491  }
492 
493  /* sort the edge points by col(x) and then by row(y) */
494  qsort(P, np, sizeof(POINT), &edge_order);
495 
496  /* plot */
497  for (i = 1; i < np; i += 2) {
498  if (P[i].y != P[i - 1].y) {
499  G_warning("Weird internal error: edge leaves row");
500  return OUT_OF_SYNC;
501  }
502  row_fill(P[i].y, P[i - 1].x + shift1, P[i].x + shift1);
503  }
504  if (window.proj == PROJECTION_LL) { /* now do wrap-around, part 2 */
505  shift = 0;
506  while (W + shift < window.west)
507  shift += 360.0;
508  while (W + shift > window.east)
509  shift -= 360.0;
510  shift2 = X(x[n - 1] + shift) - X(x[n - 1]);
511  if (shift2 != shift1) {
512  for (i = 1; i < np; i += 2) {
513  row_fill(P[i].y, P[i - 1].x + shift2, P[i].x + shift2);
514  }
515  }
516  }
517  return OK;
518 }
519 
520 /*
521  * G_plot_area (xs, ys, rpnts, rings)
522  * double **xs; -- pointer to pointer for X's
523  * double **ys; -- pointer to pointer for Y's
524  * int *rpnts; -- array of ints w/ num points per ring
525  * int rings; -- number of rings
526  *
527  * Essentially a copy of G_plot_polygon, with minor mods to
528  * handle a set of polygons. return values are the same.
529  */
530 
546 int G_plot_area(double *const *xs, double *const *ys, int *rpnts, int rings)
547 {
548  int i, j, n;
549  int pole;
550  double x0, x1, *x;
551  double y0, y1, *y;
552  double shift, E, W = 0L;
553  double e0, e1;
554  int *shift1 = NULL, shift2;
555 
556  /* traverse the perimeter */
557 
558  np = 0;
559  shift1 = (int *)G_calloc(sizeof(int), rings);
560 
561  for (j = 0; j < rings; j++) {
562  n = rpnts[j];
563 
564  if (n < 3)
565  return TOO_FEW_EDGES;
566 
567  x = xs[j];
568  y = ys[j];
569 
570  /* global wrap-around for lat-lon, part1 */
571  if (window.proj == PROJECTION_LL) {
572  /*
573  pole = G_pole_in_polygon(x,y,n);
574  */
575  pole = 0;
576 
577  e0 = x[n - 1];
578  E = W = e0;
579 
580  x0 = X(e0);
581  y0 = Y(y[n - 1]);
582 
583  if (pole && !edge(x0, y0, x0, Y(90.0 * pole)))
584  return NO_MEMORY;
585 
586  for (i = 0; i < n; i++) {
587  e1 = wrap_east(e0, x[i]);
588  if (e1 > E)
589  E = e1;
590  if (e1 < W)
591  W = e1;
592 
593  x1 = X(e1);
594  y1 = Y(y[i]);
595 
596  if (!edge(x0, y0, x1, y1))
597  return NO_MEMORY;
598 
599  x0 = x1;
600  y0 = y1;
601  e0 = e1;
602  }
603  if (pole && !edge(x0, y0, x0, Y(90.0 * pole)))
604  return NO_MEMORY;
605 
606  shift = 0; /* shift into window */
607  while (E + shift > window.east)
608  shift -= 360.0;
609  while (E + shift < window.west)
610  shift += 360.0;
611  shift1[j] = X(x[n - 1] + shift) - X(x[n - 1]);
612  }
613  else {
614  x0 = X(x[n - 1]);
615  y0 = Y(y[n - 1]);
616 
617  for (i = 0; i < n; i++) {
618  x1 = X(x[i]);
619  y1 = Y(y[i]);
620  if (!edge(x0, y0, x1, y1))
621  return NO_MEMORY;
622  x0 = x1;
623  y0 = y1;
624  }
625  }
626  } /* for() */
627 
628  /* check if perimeter has odd number of points */
629  if (np % 2) {
630  G_warning("Weird internal error: perimeter has odd number of points");
631  return OUT_OF_SYNC;
632  }
633 
634  /* sort the edge points by col(x) and then by row(y) */
635  qsort(P, np, sizeof(POINT), &edge_order);
636 
637  /* plot */
638  for (j = 0; j < rings; j++) {
639  for (i = 1; i < np; i += 2) {
640  if (P[i].y != P[i - 1].y) {
641  G_warning("Weird internal error: edge leaves row");
642  return OUT_OF_SYNC;
643  }
644  row_fill(P[i].y, P[i - 1].x + shift1[j], P[i].x + shift1[j]);
645  }
646  if (window.proj == PROJECTION_LL) { /* now do wrap-around, part 2 */
647  n = rpnts[j];
648  x = xs[j];
649  y = ys[j];
650 
651  shift = 0;
652  while (W + shift < window.west)
653  shift += 360.0;
654  while (W + shift > window.east)
655  shift -= 360.0;
656  shift2 = X(x[n - 1] + shift) - X(x[n - 1]);
657  if (shift2 != shift1[j]) {
658  for (i = 1; i < np; i += 2) {
659  row_fill(P[i].y, P[i - 1].x + shift2, P[i].x + shift2);
660  }
661  }
662  }
663  }
664  G_free(shift1);
665  return OK;
666 
667 }
668 
669 static int edge(double x0, double y0, double x1, double y1)
670 {
671  register double m;
672  double x, d;
673  int ystart, ystop;
674  int exp;
675 
676  /* tolerance to avoid FPE */
677  d = GRASS_EPSILON;
678  if (y0 != y1) {
679  if (fabs(y0) > fabs(y1))
680  d = fabs(y0);
681  else
682  d = fabs(y1);
683 
684  d = frexp(d, &exp);
685  exp -= 53;
686  d = ldexp(d, exp);
687  }
688 
689  if (fabs(y0 - y1) < d)
690  return 1;
691 
692  if (y0 < y1) {
693  ystart = iceil(y0);
694  ystop = ifloor(y1);
695  if (ystop == y1)
696  ystop--; /* if line stops at row center, don't include point */
697  }
698  else {
699  ystart = iceil(y1);
700  ystop = ifloor(y0);
701  if (ystop == y0)
702  ystop--; /* if line stops at row center, don't include point */
703  }
704  if (ystart > ystop)
705  return 1; /* does not cross center line of row */
706 
707  m = (x0 - x1) / (y0 - y1);
708  x = m * (ystart - y0) + x0;
709  while (ystart <= ystop) {
710  if (!edge_point(x, ystart++))
711  return 0;
712  x += m;
713  }
714  return 1;
715 }
716 
717 static int edge_point(double x, int y)
718 {
719 
720  if (y < ymin || y > ymax)
721  return 1;
722  if (np >= npalloc) {
723  if (npalloc > 0) {
724  npalloc *= 2;
725  P = (POINT *) G_realloc(P, npalloc * sizeof(POINT));
726  }
727  else {
728  npalloc = 32;
729  P = (POINT *) G_malloc(npalloc * sizeof(POINT));
730  }
731  if (P == NULL) {
732  npalloc = 0;
733  return 0;
734  }
735  }
736  P[np].x = x;
737  P[np++].y = y;
738  return 1;
739 }
740 
741 static int edge_order(const void *aa, const void *bb)
742 {
743  const struct point *a = aa, *b = bb;
744 
745  if (a->y < b->y)
746  return (-1);
747  if (a->y > b->y)
748  return (1);
749 
750  if (a->x < b->x)
751  return (-1);
752  if (a->x > b->x)
753  return (1);
754 
755  return (0);
756 }
757 
758 static int row_solid_fill(int y, double x1, double x2)
759 {
760  int i1, i2;
761 
762  i1 = iceil(x1);
763  i2 = ifloor(x2);
764  if (i1 <= i2) {
765  move(i1, y);
766  cont(i2, y);
767  }
768 
769  return 0;
770 }
771 
772 static int row_dotted_fill(int y, double x1, double x2)
773 {
774  int i1, i2, i;
775 
776  if (y != iceil(y / dotted_fill_gap) * dotted_fill_gap)
777  return 0;
778 
779  i1 = iceil(x1 / dotted_fill_gap) * dotted_fill_gap;
780  i2 = ifloor(x2);
781  if (i1 <= i2) {
782  for (i = i1; i <= i2; i += dotted_fill_gap) {
783  move(i, y);
784  cont(i, y);
785  }
786  }
787 
788  return 0;
789 }
790 
791 static int ifloor(double x)
792 {
793  int i;
794 
795  i = (int)x;
796  if (i > x)
797  i--;
798  return i;
799 }
800 
801 static int iceil(double x)
802 {
803  int i;
804 
805  i = (int)x;
806  if (i < x)
807  i++;
808  return i;
809 }
810 
811 /*
812  * G_plot_fx(e1,e2)
813  *
814  * plot f(x) from x=e1 to x=e2
815  */
816 
817 
829 int G_plot_fx(double (*f) (double), double east1, double east2)
830 {
831  double east, north, north1;
832  double incr;
833 
834 
835  incr = fabs(1.0 / xconv);
836 
837  east = east1;
838  north = f(east1);
839 
840  if (east1 > east2) {
841  while ((east1 -= incr) > east2) {
842  north1 = f(east1);
843  G_plot_line(east, north, east1, north1);
844  north = north1;
845  east = east1;
846  }
847  }
848  else {
849  while ((east1 += incr) < east2) {
850  north1 = f(east1);
851  G_plot_line(east, north, east1, north1);
852  north = north1;
853  east = east1;
854  }
855  }
856  G_plot_line(east, north, east2, f(east2));
857 
858  return 0;
859 }
int G_plot_area(double *const *xs, double *const *ys, int *rpnts, int rings)
plot multiple polygons
Definition: plot.c:546
int G_plot_where_xy(double east, double north, int *x, int *y)
east,north to x,y
Definition: plot.c:168
int G_plot_point(double east, double north)
Definition: plot.c:198
void G_free(void *buf)
Free allocated memory.
Definition: gis/alloc.c:142
int l
Definition: dataquad.c:292
float b
Definition: named_colr.c:8
int G_get_set_window(struct Cell_head *window)
Get the current working window.
Definition: set_window.c:30
#define EAST(x)
Definition: plot.c:151
int G_setup_fill(int gap)
set row_fill routine to row_solid_fill or row_dotted_fill
Definition: plot.c:136
#define OUT_OF_SYNC
Definition: plot.c:387
float r
Definition: named_colr.c:8
#define NORTH(y)
Definition: plot.c:152
int G_plot_where_en(int x, int y, double *east, double *north)
x,y to east,north
Definition: plot.c:190
double ymin
Definition: dataquad.c:293
int y
Definition: plot.c:34
#define Y(n)
Definition: plot.c:149
#define NO_MEMORY
Definition: plot.c:386
int G_plot_line(double east1, double north1, double east2, double north2)
plot line between latlon coordinates
Definition: plot.c:230
tuple window
Definition: tools.py:543
#define OK
Definition: plot.c:384
int
Definition: g3dcolor.c:48
double ymax
Definition: dataquad.c:293
int G_plot_fx(double(*f)(double), double east1, double east2)
plot f(east1) to f(east2)
Definition: plot.c:829
#define TOO_FEW_EDGES
Definition: plot.c:385
return NULL
Definition: dbfopen.c:1394
int G_plot_line2(double east1, double north1, double east2, double north2)
Definition: plot.c:235
G_warning("category support for [%s] in mapset [%s] %s", name, mapset, type)
int G_setup_plot(double t, double b, double l, double r, int(*Move)(int, int), int(*Cont)(int, int))
returns east larger than west
Definition: plot.c:97
#define X(e)
Definition: plot.c:148
#define POINT
Definition: plot.c:31
int n
Definition: dataquad.c:291
double G_adjust_easting(double east, const struct Cell_head *window)
Returns east larger than west.
Definition: window_map.c:174
int G_plot_polygon(const double *x, const double *y, int n)
plot filled polygon with n vertices
Definition: plot.c:412