GRASS GIS 8 Programmer's Manual  8.5.0dev(2024)-36359e2344
break_lines.c
Go to the documentation of this file.
1 /*!
2  * \file lib/vector/Vlib/break_lines.c
3  *
4  * \brief Vector library - Clean vector map (break lines)
5  *
6  * (C) 2001-2009 by the GRASS Development Team
7  *
8  * This program is free software under the
9  * GNU General Public License (>=v2).
10  * Read the file COPYING that comes with GRASS
11  * for details.
12  *
13  * \author Radim Blazek
14  */
15 
16 #include <stdlib.h>
17 #include <grass/vector.h>
18 #include <grass/glocale.h>
19 
20 static int break_lines(struct Map_info *, struct ilist *, struct ilist *, int,
21  struct Map_info *, int);
22 
23 /*!
24  \brief Break lines in vector map at each intersection.
25 
26  For details see Vect_break_lines_list().
27 
28  \param Map input vector map
29  \param type feature type
30  \param[out] Err vector map where points at intersections will be written or
31  NULL
32  */
33 void Vect_break_lines(struct Map_info *Map, int type, struct Map_info *Err)
34 {
35  break_lines(Map, NULL, NULL, type, Err, 0);
36 
37  return;
38 }
39 
40 /*!
41  \brief Break selected lines in vector map at each intersection.
42 
43  Breaks selected lines specified by type in vector map. Points at
44  intersections may be optionally written to error map. Input vector map
45  must be opened on level 2 for update at least on GV_BUILD_BASE.
46 
47  The function also breaks lines forming collapsed loop, for example
48  0,0;1,0;0,0 is broken at 1,0.
49 
50  If reference lines are given (<i>List_ref</i>) break only lines
51  which intersect reference lines.
52 
53  \param Map input vector map
54  \param List_break list of lines (NULL for all lines in vector map)
55  \param List_ref list of reference lines or NULL
56  \param type feature type
57  \param[out] Err vector map where points at intersections will be written or
58  NULL
59 
60  \return number of intersections
61  */
62 
63 int Vect_break_lines_list(struct Map_info *Map, struct ilist *List_break,
64  struct ilist *List_ref, int type,
65  struct Map_info *Err)
66 {
67  return break_lines(Map, List_break, List_ref, type, Err, 0);
68 }
69 
70 /*!
71  \brief Check for and count intersecting lines, do not break.
72 
73  For details see Vect_check_line_breaks_list().
74 
75  \param Map input vector map
76  \param type feature type
77  \param[out] Err vector map where points at intersections will be written or
78  NULL
79 
80  \return number for intersections
81  */
82 int Vect_check_line_breaks(struct Map_info *Map, int type, struct Map_info *Err)
83 {
84  return break_lines(Map, NULL, NULL, type, Err, 1);
85 }
86 
87 /*!
88  \brief Check for and count intersecting lines, do not break.
89 
90  If <i>List_break</i> is given, only lines in the list are checked for
91  intersections.
92 
93  If reference lines are given (<i>List_ref</i>) break only lines
94  which intersect reference lines.
95 
96  \param Map input vector map
97  \param List_break list of lines (NULL for all lines in vector map)
98  \param List_ref list of reference lines or NULL
99  \param type feature type
100  \param[out] Err vector map where points at intersections will be written or
101  NULL
102 
103  \return number of intersections
104  */
105 int Vect_check_line_breaks_list(struct Map_info *Map, struct ilist *List_break,
106  struct ilist *List_ref, int type,
107  struct Map_info *Err)
108 {
109  return break_lines(Map, List_break, List_ref, type, Err, 1);
110 }
111 
112 static int cmp(const void *a, const void *b)
113 {
114  int ai = *(int *)a;
115  int bi = *(int *)b;
116 
117  /* ai - bi is ok because ai and bi are positive integers
118  * -> no integer overflow */
119  return (ai - bi);
120 }
121 
122 static void sort_ilist(struct ilist *List)
123 {
124  int i, j, is_sorted = 1;
125 
126  for (i = 1; i < List->n_values; i++) {
127  if (List->value[i - 1] > List->value[i]) {
128  is_sorted = 0;
129  break;
130  }
131  }
132 
133  if (!is_sorted)
134  qsort(List->value, List->n_values, sizeof(int), cmp);
135 
136  if (List->n_values > 1) {
137  j = 1;
138  for (i = 1; i < List->n_values; i++) {
139  if (List->value[j - 1] != List->value[i]) {
140  List->value[j] = List->value[i];
141  j++;
142  }
143  }
144  List->n_values = j;
145  }
146 }
147 
148 int break_lines(struct Map_info *Map, struct ilist *List_break,
149  struct ilist *List_ref, int type, struct Map_info *Err,
150  int check)
151 {
152  struct line_pnts *APoints, *BPoints, *Points;
153  struct line_pnts **AXLines, **BXLines;
154  struct line_cats *ACats, *BCats, *Cats;
155  int i, j, k, l, ret, atype, btype, aline, bline, found, iline;
156  int nlines, nlines_org;
157  int naxlines, nbxlines, nx;
158  double *xx = NULL, *yx = NULL, *zx = NULL;
159  struct bound_box ABox, *BBox;
160  struct boxlist *List;
161  int nbreaks;
162  int touch1_n = 0, touch1_s = 0, touch1_e = 0,
163  touch1_w = 0; /* other vertices except node1 touching box */
164  int touch2_n = 0, touch2_s = 0, touch2_e = 0,
165  touch2_w = 0; /* other vertices except node2 touching box */
166  int is3d;
167  int node, anode1, anode2, bnode1, bnode2;
168  double nodex, nodey;
169  int a_is_ref, b_is_ref, break_a, break_b;
170 
171  type &= GV_LINES;
172  if (!type)
173  return 0;
174 
175  APoints = Vect_new_line_struct();
176  BPoints = Vect_new_line_struct();
177  Points = Vect_new_line_struct();
178  ACats = Vect_new_cats_struct();
179  BCats = Vect_new_cats_struct();
180  Cats = Vect_new_cats_struct();
181  List = Vect_new_boxlist(1);
182 
183  is3d = Vect_is_3d(Map);
184 
185  if (List_ref)
186  sort_ilist(List_ref);
187  if (List_break)
188  sort_ilist(List_break);
189 
190  if (List_ref) {
191  nlines = List_ref->n_values;
192  nlines_org = List_ref->value[List_ref->n_values - 1];
193  }
194  else if (List_break) {
195  nlines = List_break->n_values;
196  nlines_org = List_break->value[List_break->n_values - 1];
197  }
198  else {
199  nlines = Vect_get_num_lines(Map);
200  nlines_org = nlines;
201  }
202  G_debug(3, "nlines = %d", nlines);
203 
204  /* TODO:
205  * 1. It seems that lines/boundaries are not broken at intersections
206  * with points/centroids. Check if true, if yes, skip GV_POINTS
207  * 2. list of lines to break and list of reference lines
208  * aline: reference line, if List_ref == NULL, use all
209  * break aline only if it is in the list of lines to break
210  * bline: line to break, if List_break == NULL, break all
211  */
212 
213  /* To find intersection of two lines (Vect_line_intersection) is quite slow.
214  * Fortunately usual lines/boundaries in GIS often forms a network where
215  * lines are connected by end points, and touch by MBR. This function checks
216  * and occasionally skips such cases. This is currently done for 2D only
217  */
218 
219  /* Go through all lines in vector, for each select lines which overlap MBR
220  * of this line exclude those connected by one endpoint (see above) and try
221  * to intersect, if lines intersect write new lines at the end of the file,
222  * and process next line (remaining lines overlapping box are skipped)
223  */
224  nbreaks = 0;
225 
226  for (iline = 0; iline < nlines; iline++) {
227  G_percent(iline, nlines, 1);
228 
229  /* aline: reference line */
230  if (List_ref) {
231  aline = List_ref->value[iline];
232  }
233  else if (List_break) {
234  aline = List_break->value[iline];
235  }
236  else {
237  aline = iline + 1;
238  }
239 
240  G_debug(3, "aline = %d", aline);
241  if (!Vect_line_alive(Map, aline))
242  continue;
243 
244  a_is_ref = 0;
245  break_a = 1;
246  if (List_ref) {
247  a_is_ref = 1;
248  }
249 
250  if (List_break) {
251  break_a = 0;
252  if (bsearch(&aline, List_break->value, List_break->n_values,
253  sizeof(int), cmp)) {
254  break_a = 1;
255  }
256  }
257 
258  atype = Vect_read_line(Map, APoints, ACats, aline);
259  if (!(atype & type))
260  continue;
261 
262  Vect_line_prune(APoints);
263  Vect_line_box(APoints, &ABox);
264 
265  /* Find which sides of the box are touched by intermediate (non-end)
266  * points of line */
267  if (!is3d) {
268  touch1_n = touch1_s = touch1_e = touch1_w = 0;
269  for (j = 1; j < APoints->n_points; j++) {
270  if (APoints->y[j] == ABox.N)
271  touch1_n = 1;
272  if (APoints->y[j] == ABox.S)
273  touch1_s = 1;
274  if (APoints->x[j] == ABox.E)
275  touch1_e = 1;
276  if (APoints->x[j] == ABox.W)
277  touch1_w = 1;
278  }
279  G_debug(3, "touch1: n = %d s = %d e = %d w = %d", touch1_n,
280  touch1_s, touch1_e, touch1_w);
281  touch2_n = touch2_s = touch2_e = touch2_w = 0;
282  for (j = 0; j < APoints->n_points - 1; j++) {
283  if (APoints->y[j] == ABox.N)
284  touch2_n = 1;
285  if (APoints->y[j] == ABox.S)
286  touch2_s = 1;
287  if (APoints->x[j] == ABox.E)
288  touch2_e = 1;
289  if (APoints->x[j] == ABox.W)
290  touch2_w = 1;
291  }
292  G_debug(3, "touch2: n = %d s = %d e = %d w = %d", touch2_n,
293  touch2_s, touch2_e, touch2_w);
294  }
295 
296  Vect_select_lines_by_box(Map, &ABox, type, List);
297  G_debug(3, " %d lines selected by box", List->n_values);
298 
299  for (j = -1; j < List->n_values; j++) {
300 
301  /* bline: line to break */
302 
303  if (j == -1) {
304  /* check first for self-intersections */
305  if (aline <= nlines_org)
306  bline = aline;
307  else
308  continue;
309  }
310  else {
311  bline = List->id[j];
312  if (bline == aline)
313  continue;
314  }
315 
316  b_is_ref = 0;
317  break_b = 1;
318  if (List_ref && bsearch(&bline, List_ref->value, List_ref->n_values,
319  sizeof(int), cmp)) {
320  b_is_ref = 1;
321  /* reference bline will be broken when it is aline */
322  break_b = 0;
323  }
324 
325  if (List_break) {
326  break_b = 0;
327  if (bsearch(&bline, List_break->value, List_break->n_values,
328  sizeof(int), cmp)) {
329  break_b = 1;
330  }
331  }
332 
333  if (!break_a && !break_b)
334  continue;
335 
336  /* check intersection of aline with bline only once
337  * if possible */
338  if (break_a && break_b && aline > bline &&
339  (!List_ref || b_is_ref)) {
340  continue;
341  }
342 
343  G_debug(3, " j = %d bline = %d", j, bline);
344 
345  btype = Vect_read_line(Map, BPoints, BCats, bline);
346  Vect_line_prune(BPoints);
347 
348  if (j == -1)
349  BBox = &ABox;
350  else
351  BBox = &List->box[j];
352 
353  /* Check if touch by end node only */
354  if (!is3d) {
355  Vect_get_line_nodes(Map, aline, &anode1, &anode2);
356  Vect_get_line_nodes(Map, bline, &bnode1, &bnode2);
357 
358  node = 0;
359  if (anode1 == bnode1 || anode1 == bnode2)
360  node = anode1;
361  else if (anode2 == bnode1 || anode2 == bnode2)
362  node = anode2;
363 
364  if (node) {
365  Vect_get_node_coor(Map, node, &nodex, &nodey, NULL);
366  if ((node == anode1 && nodey == ABox.N && !touch1_n &&
367  nodey == BBox->S) ||
368  (node == anode2 && nodey == ABox.N && !touch2_n &&
369  nodey == BBox->S) ||
370  (node == anode1 && nodey == ABox.S && !touch1_s &&
371  nodey == BBox->N) ||
372  (node == anode2 && nodey == ABox.S && !touch2_s &&
373  nodey == BBox->N) ||
374  (node == anode1 && nodex == ABox.E && !touch1_e &&
375  nodex == BBox->W) ||
376  (node == anode2 && nodex == ABox.E && !touch2_e &&
377  nodex == BBox->W) ||
378  (node == anode1 && nodex == ABox.W && !touch1_w &&
379  nodex == BBox->E) ||
380  (node == anode2 && nodex == ABox.W && !touch2_w &&
381  nodex == BBox->E)) {
382 
383  G_debug(3,
384  "lines %d and %d touching by end nodes only -> "
385  "no intersection",
386  aline, bline);
387  continue;
388  }
389  }
390  }
391 
392  AXLines = NULL;
393  BXLines = NULL;
394 
395  if (aline != bline) {
396  Vect_line_intersection2(APoints, BPoints, &ABox, BBox, &AXLines,
397  &BXLines, &naxlines, &nbxlines, 0);
398  }
399  else {
400  Vect_line_intersection2(APoints, NULL, &ABox, BBox, &AXLines,
401  &BXLines, &naxlines, &nbxlines, 0);
402  }
403 
404  G_debug(3, " naxlines = %d nbxlines = %d", naxlines, nbxlines);
405 
406  /* This part handles a special case when aline == bline, no other
407  * intersection was found and the line is forming collapsed loop,
408  * for example 0,0;1,0;0,0 should be broken at 1,0.
409  * ---> */
410  if (aline == bline && naxlines == 0 && nbxlines == 0 &&
411  APoints->n_points >= 3 && break_a) {
412  int centre;
413 
414  G_debug(3, " Check collapsed loop");
415  if (APoints->n_points % 2) { /* odd number of vertices */
416  centre = APoints->n_points / 2; /* index of centre */
417  if (APoints->x[centre - 1] == APoints->x[centre + 1] &&
418  APoints->y[centre - 1] == APoints->y[centre + 1] &&
419  APoints->z[centre - 1] ==
420  APoints->z[centre + 1]) { /* -> break */
421  AXLines = (struct line_pnts **)G_malloc(
422  2 * sizeof(struct line_pnts *));
423  AXLines[0] = Vect_new_line_struct();
424  AXLines[1] = Vect_new_line_struct();
425 
426  for (i = 0; i <= centre; i++)
427  Vect_append_point(AXLines[0], APoints->x[i],
428  APoints->y[i], APoints->z[i]);
429 
430  for (i = centre; i < APoints->n_points; i++)
431  Vect_append_point(AXLines[1], APoints->x[i],
432  APoints->y[i], APoints->z[i]);
433 
434  naxlines = 2;
435  }
436  }
437  }
438  /* <--- */
439 
440  if (Err) { /* array for intersections (more than needed */
441  xx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double));
442  yx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double));
443  zx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double));
444  }
445  nx = 0; /* number of intersections to be written to Err */
446  if (naxlines > 0) { /* intersection -> write out */
447 
448  G_debug(3, " aline = %d, bline = %d, naxlines = %d", aline,
449  bline, naxlines);
450 
451  if (!check && break_a)
452  Vect_delete_line(Map, aline);
453  for (k = 0; k < naxlines; k++) {
454  /* Write new line segments */
455  /* line may collapse, don't write zero length lines */
456  Vect_line_prune(AXLines[k]);
457  if ((atype & GV_POINTS) || AXLines[k]->n_points > 1) {
458  if (!check && break_a) {
459  ret =
460  Vect_write_line(Map, atype, AXLines[k], ACats);
461  G_debug(3, "Line %d written, npoints = %d", ret,
462  AXLines[k]->n_points);
463  if (List_ref && a_is_ref) {
464  G_ilist_add(List_ref, ret);
465  }
466  if (List_break && break_a) {
467  G_ilist_add(List_break, ret);
468  }
469  }
470  }
471  else
472  G_debug(3, "axline %d has zero length", k);
473 
474  /* Write intersection points */
475  if (Err) {
476  if (k > 0) {
477  xx[nx] = AXLines[k]->x[0];
478  yx[nx] = AXLines[k]->y[0];
479  zx[nx] = AXLines[k]->z[0];
480  nx++;
481  }
482  }
483  Vect_destroy_line_struct(AXLines[k]);
484  }
485  nbreaks += naxlines - 1;
486  }
487  if (AXLines)
488  G_free(AXLines);
489 
490  if (nbxlines > 0) {
491  if (aline != bline) { /* Self intersection, do not write twice,
492  TODO: is it OK? */
493 
494  G_debug(3, " aline = %d, bline = %d, nbxlines = %d",
495  aline, bline, nbxlines);
496 
497  if (!check && break_b)
498  Vect_delete_line(Map, bline);
499  for (k = 0; k < nbxlines; k++) {
500  /* Write new line segments */
501  /* line may collapse, don't write zero length lines */
502  Vect_line_prune(BXLines[k]);
503  if ((btype & GV_POINTS) || BXLines[k]->n_points > 1) {
504  if (!check && break_b) {
505  ret = Vect_write_line(Map, btype, BXLines[k],
506  BCats);
507  G_debug(5, "Line %d written", ret);
508  if (List_ref && b_is_ref) {
509  G_ilist_add(List_ref, ret);
510  }
511  if (List_break) {
512  G_ilist_add(List_break, ret);
513  }
514  }
515  }
516  else
517  G_debug(3, "bxline %d has zero length", k);
518 
519  /* Write intersection points */
520  if (Err) {
521  if (k > 0) {
522  found = 0;
523  for (l = 0; l < nx; l++) {
524  if (xx[l] == BXLines[k]->x[0] &&
525  yx[l] == BXLines[k]->y[0] &&
526  zx[l] == BXLines[k]->z[0]) {
527  found = 1;
528  break;
529  }
530  }
531  if (!found) {
532  xx[nx] = BXLines[k]->x[0];
533  yx[nx] = BXLines[k]->y[0];
534  zx[nx] = BXLines[k]->z[0];
535  nx++;
536  }
537  }
538  }
539  }
540  nbreaks += nbxlines - 1;
541  }
542  for (k = 0; k < nbxlines; k++)
543  Vect_destroy_line_struct(BXLines[k]);
544  }
545  if (BXLines)
546  G_free(BXLines);
547  if (Err) {
548  for (l = 0; l < nx; l++) { /* Write out errors */
549  Vect_reset_line(Points);
550  Vect_append_point(Points, xx[l], yx[l], zx[l]);
551  ret = Vect_write_line(Err, GV_POINT, Points, Cats);
552  }
553 
554  G_free(xx);
555  G_free(yx);
556  G_free(zx);
557  }
558  if (naxlines > 0 && !check && break_a) {
559  G_debug(3, "aline was broken, use next one");
560  break; /* first line was broken and deleted -> take the next one
561  */
562  }
563  }
564 
565  if (List_ref) {
566  nlines = List_ref->n_values;
567  }
568  else if (List_break) {
569  nlines = List_break->n_values;
570  }
571  else {
572  nlines = Vect_get_num_lines(Map);
573  }
574  G_debug(3, "nlines = %d", nlines);
575  } /* for each line */
576  G_percent(nlines, nlines, 1); /* finish it */
577 
578  G_verbose_message(_("Intersections: %d"), nbreaks);
579 
580  Vect_destroy_line_struct(APoints);
581  Vect_destroy_line_struct(BPoints);
582  Vect_destroy_line_struct(Points);
586  Vect_destroy_boxlist(List);
587 
588  return nbreaks;
589 }
void Vect_break_lines(struct Map_info *Map, int type, struct Map_info *Err)
Break lines in vector map at each intersection.
Definition: break_lines.c:33
int Vect_break_lines_list(struct Map_info *Map, struct ilist *List_break, struct ilist *List_ref, int type, struct Map_info *Err)
Break selected lines in vector map at each intersection.
Definition: break_lines.c:63
int Vect_check_line_breaks(struct Map_info *Map, int type, struct Map_info *Err)
Check for and count intersecting lines, do not break.
Definition: break_lines.c:82
int Vect_check_line_breaks_list(struct Map_info *Map, struct ilist *List_break, struct ilist *List_ref, int type, struct Map_info *Err)
Check for and count intersecting lines, do not break.
Definition: break_lines.c:105
#define NULL
Definition: ccmath.h:32
void G_percent(long, long, int)
Print percent complete messages.
Definition: percent.c:61
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
#define G_malloc(n)
Definition: defs/gis.h:94
void void G_verbose_message(const char *,...) __attribute__((format(printf
void G_ilist_add(struct ilist *, int)
Add item to ilist.
Definition: ilist.c:78
int G_debug(int, const char *,...) __attribute__((format(printf
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition: line.c:77
int Vect_get_line_nodes(struct Map_info *, int, int *, int *)
Get line nodes.
Definition: level_two.c:303
int Vect_get_node_coor(struct Map_info *, int, double *, double *, double *)
Get node coordinates.
Definition: level_two.c:274
plus_t Vect_get_num_lines(struct Map_info *)
Fetch number of features (points, lines, boundaries, centroids) in vector map.
Definition: level_two.c:75
void Vect_line_box(const struct line_pnts *, struct bound_box *)
Get bounding box of line.
Definition: line.c:888
struct boxlist * Vect_new_boxlist(int)
Creates and initializes a struct boxlist.
int Vect_line_intersection2(struct line_pnts *, struct line_pnts *, struct bound_box *, struct bound_box *, struct line_pnts ***, struct line_pnts ***, int *, int *, int)
Intersect 2 lines.
Definition: intersect2.c:675
void Vect_destroy_boxlist(struct boxlist *)
Frees all memory associated with a struct boxlist, including the struct itself.
void Vect_destroy_cats_struct(struct line_cats *)
Frees all memory associated with line_cats structure, including the struct itself.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
int Vect_line_alive(struct Map_info *, int)
Check if feature is alive or dead (topological level required)
int Vect_delete_line(struct Map_info *, off_t)
Delete existing feature (topological level required)
off_t Vect_write_line(struct Map_info *, int, const struct line_pnts *, const struct line_cats *)
Writes a new feature.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition: line.c:45
int Vect_select_lines_by_box(struct Map_info *, const struct bound_box *, int, struct boxlist *)
Select lines with bounding boxes by box.
Definition: sindex.c:32
void Vect_reset_line(struct line_pnts *)
Reset line.
Definition: line.c:129
int Vect_line_prune(struct line_pnts *)
Remove duplicate points, i.e. zero length segments.
Definition: line.c:279
struct line_cats * Vect_new_cats_struct(void)
Creates and initializes line_cats structure.
int Vect_is_3d(struct Map_info *)
Check if vector map is 3D.
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition: line.c:148
#define GV_POINT
Feature types used in memory on run time (may change)
Definition: dig_defines.h:183
#define GV_LINES
Definition: dig_defines.h:193
#define GV_POINTS
Definition: dig_defines.h:192
#define _(str)
Definition: glocale.h:10
double b
Definition: r_raster.c:39
double l
Definition: r_raster.c:39
Vector map info.
Definition: dig_structs.h:1243
Bounding box.
Definition: dig_structs.h:64
double W
West.
Definition: dig_structs.h:80
double S
South.
Definition: dig_structs.h:72
double N
North.
Definition: dig_structs.h:68
double E
East.
Definition: dig_structs.h:76
List of bounding boxes with id.
Definition: dig_structs.h:1723
int * id
Array of ids.
Definition: dig_structs.h:1727
struct bound_box * box
Array of bounding boxes.
Definition: dig_structs.h:1731
int n_values
Number of items in the list.
Definition: dig_structs.h:1739
List of integers.
Definition: gis.h:709
int n_values
Number of values in the list.
Definition: gis.h:717
int * value
Array of values.
Definition: gis.h:713
Feature category info.
Definition: dig_structs.h:1677
Feature geometry info - coordinates.
Definition: dig_structs.h:1651
double * y
Array of Y coordinates.
Definition: dig_structs.h:1659
double * x
Array of X coordinates.
Definition: dig_structs.h:1655
int n_points
Number of points.
Definition: dig_structs.h:1667
double * z
Array of Z coordinates.
Definition: dig_structs.h:1663
#define x