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