GRASS GIS 8 Programmer's Manual  8.5.0dev(2024)-d6dec75dd4
write_nat.c
Go to the documentation of this file.
1 /*!
2  \file lib/vector/Vlib/write_nat.c
3 
4  \brief Vector library - write/modify/delete vector feature (native format)
5 
6  Higher level functions for reading/writing/manipulating vectors.
7 
8  (C) 2001-2015 by the GRASS Development Team
9 
10  This program is free software under the GNU General Public License
11  (>=v2). Read the file COPYING that comes with GRASS for details.
12 
13  \author Original author CERL, probably Dave Gerdes or Mike Higgins.
14  \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
15  \author V*_restore_line() by Martin Landa <landa.martin gmail.com> (2008)
16  */
17 
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <math.h>
22 
23 #include <grass/vector.h>
24 #include <grass/glocale.h>
25 
26 #include "local_proto.h"
27 
28 static off_t V1__write_line_nat(struct Map_info *, off_t, int,
29  const struct line_pnts *,
30  const struct line_cats *);
31 static void V2__delete_area_cats_from_cidx_nat(struct Map_info *, int);
32 static void V2__add_area_cats_to_cidx_nat(struct Map_info *, int);
33 
34 /*!
35  \brief Writes feature to 'coor' file at level 1 (internal use only)
36 
37  \param Map pointer to Map_info structure
38  \param type feature type (GV_POINT, GV_LINE, ...)
39  \param points feature geometry
40  \param cats feature categories
41 
42  \return feature offset into file
43  \return -1 on error
44  */
45 off_t V1_write_line_nat(struct Map_info *Map, int type,
46  const struct line_pnts *points,
47  const struct line_cats *cats)
48 {
49  return V1__write_line_nat(Map, -1, type, points, cats);
50 }
51 
52 /*!
53  \brief Writes feature to 'coor' file at topological level (internal use only)
54 
55  Note: Function returns feature id, but is defined as off_t for
56  compatibility with level 1 functions.
57 
58  \param Map pointer to Map_info structure
59  \param type feature type (GV_POINT, GV_LINE, ...)
60  \param points feature geometry
61  \param cats feature categories
62 
63  \return new feature id
64  \return 0 topology is not requested to be built (build level < GV_BUILD_BASE)
65  \return -1 on error
66  */
67 off_t V2_write_line_nat(struct Map_info *Map, int type,
68  const struct line_pnts *points,
69  const struct line_cats *cats)
70 {
71  off_t offset;
72 
73  G_debug(3, "V2_write_line_nat(): type=%d", type);
74 
75  if (!(Map->plus.update_cidx)) {
76  Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
77  }
78  /* write feature to 'coor' file */
79  offset = V1_write_line_nat(Map, type, points, cats);
80  if (offset < 0)
81  return -1;
82 
83  /* update topology (build level >= GV_BUILD_BASE) */
84  return V2__add_line_to_topo_nat(Map, offset, type, points, cats, -1, NULL);
85 }
86 
87 /*!
88  \brief Rewrites feature to 'coor' file at level 1 (internal use only)
89 
90  If the number of points or cats differs from the original one or the
91  type is changed: GV_POINTS -> GV_LINES or GV_LINES -> GV_POINTS, the
92  old one is deleted and the new is appended to the end of the file.
93 
94  Old feature is deleted (marked as dead), and a new feature written.
95 
96  \param Map pointer to Map_info structure
97  \param offset feature offset
98  \param type feature type (GV_POINT, GV_LINE, ...)
99  \param points feature geometry
100  \param cats feature categories
101 
102  \return feature offset (rewritten feature)
103  \return -1 on error
104  */
105 off_t V1_rewrite_line_nat(struct Map_info *Map, off_t offset, int type,
106  const struct line_pnts *points,
107  const struct line_cats *cats)
108 {
109  int old_type;
110  static struct line_pnts *old_points = NULL;
111  static struct line_cats *old_cats = NULL;
112 
113  G_debug(3, "V1_rewrite_line_nat(): offset = %" PRId64, offset);
114 
115  /* First compare numbers of points and cats with the old one */
116  if (!old_points) {
117  old_points = Vect_new_line_struct();
118  old_cats = Vect_new_cats_struct();
119  }
120 
121  old_type = V1_read_line_nat(Map, old_points, old_cats, offset);
122  if (old_type == -1)
123  return (-1); /* error */
124 
125  if (old_type != -2 /* EOF -> write new line */
126  && points->n_points == old_points->n_points &&
127  cats->n_cats == old_cats->n_cats &&
128  (((type & GV_POINTS) && (old_type & GV_POINTS)) ||
129  ((type & GV_LINES) && (old_type & GV_LINES)))) {
130 
131  /* equal -> overwrite the old */
132  return V1__write_line_nat(Map, offset, type, points, cats);
133  }
134  else {
135  /* differ -> delete the old and append new */
136  /* delete old */
137  V1_delete_line_nat(Map, offset);
138 
139  return V1__write_line_nat(Map, -1, type, points, cats);
140  }
141 }
142 
143 /*!
144  \brief Rewrites feature to 'coor' file at topological level (internal use
145  only)
146 
147  Note: requires topology level >= GV_BUILD_BASE.
148 
149  Note: Function returns feature id, but is defined as off_t for
150  compatibility with level 1 functions.
151 
152  \param Map pointer to Map_info structure
153  \param line feature id to be rewritten
154  \param type feature type (GV_POINT, GV_LINE, ...)
155  \param points feature geometry
156  \param cats feature categories
157 
158  \return new feature id or 0 (build level < GV_BUILD_BASE)
159  \return -1 on error
160  */
161 off_t V2_rewrite_line_nat(struct Map_info *Map, off_t line, int type,
162  const struct line_pnts *points,
163  const struct line_cats *cats)
164 {
165  /* TODO: this is just quick shortcut because we have already V2_delete_nat()
166  * and V2_write_nat() this function first deletes old line
167  * and then writes new one. It is not very effective if number of
168  * points and cats was not changed or topology is not changed (nodes not
169  * moved, angles not changed etc.) */
170 
171  int old_type;
172  off_t offset, old_offset;
173  struct Plus_head *plus;
174  static struct line_cats *old_cats = NULL;
175  static struct line_pnts *old_points = NULL;
176 
177  plus = &(Map->plus);
178 
179  if (plus->uplist.do_uplist) {
180  /* list of updated lines: undo needs copy on write */
181  if (0 != V2_delete_line_nat(Map, line))
182  return -1;
183 
184  return V2_write_line_nat(Map, type, points, cats);
185  }
186 
187  if (line < 1 || line > plus->n_lines) {
188  G_warning(_("Attempt to access feature with invalid id (%d)"),
189  (int)line);
190  return -1;
191  }
192 
193  if (!(plus->update_cidx)) {
194  plus->cidx_up_to_date = FALSE; /* category index will be outdated */
195  }
196 
197  old_offset = plus->Line[line]->offset;
198 
199  /* read the line */
200  if (!old_points) {
201  old_points = Vect_new_line_struct();
202  }
203  if (!old_cats) {
204  old_cats = Vect_new_cats_struct();
205  }
206  old_type = V2_read_line_nat(Map, old_points, old_cats, line);
207  if (old_type == -1)
208  return -1;
209 
210  /* rewrite feature in coor file */
211  if (old_type != -2 /* EOF -> write new line */
212  && points->n_points == old_points->n_points &&
213  cats->n_cats == old_cats->n_cats &&
214  (((type & GV_POINTS) && (old_type & GV_POINTS)) ||
215  ((type & GV_LINES) && (old_type & GV_LINES)))) {
216 
217  /* equal -> overwrite the old */
218  offset = old_offset;
219  }
220  else {
221  /* differ -> delete the old and append new */
222  /* delete old */
223  V1_delete_line_nat(Map, old_offset);
224  offset = -1;
225  }
226 
227  /* delete feature from topology */
228  if (0 !=
229  V2__delete_line_from_topo_nat(Map, line, type, old_points, old_cats))
230  return -1;
231 
232  offset = V1__write_line_nat(Map, offset, type, points, cats);
233 
234  /* update topology (build level >= GV_BUILD_BASE) */
235  return V2__add_line_to_topo_nat(Map, offset, type, points, cats, line,
236  NULL);
237 }
238 
239 /*!
240  \brief Deletes feature at level 1 (internal use only)
241 
242  \param Map pointer Map_info structure
243  \param offset feature offset
244 
245  \return 0 on success
246  \return -1 on error
247  */
248 int V1_delete_line_nat(struct Map_info *Map, off_t offset)
249 {
250  char rhead;
251  struct gvfile *dig_fp;
252 
253  G_debug(3, "V1_delete_line_nat(): offset = %" PRId64, offset);
254 
255  dig_set_cur_port(&(Map->head.port));
256  dig_fp = &(Map->dig_fp);
257 
258  if (dig_fseek(dig_fp, offset, 0) == -1)
259  return -1;
260 
261  /* read old */
262  if (0 >= dig__fread_port_C(&rhead, 1, dig_fp))
263  return -1;
264 
265  rhead &= 0xFE;
266 
267  if (dig_fseek(dig_fp, offset, 0) == -1)
268  return -1;
269 
270  if (0 >= dig__fwrite_port_C(&rhead, 1, dig_fp))
271  return -1;
272 
273  if (0 != dig_fflush(dig_fp))
274  return -1;
275 
276  return 0;
277 }
278 
279 /*!
280  \brief Deletes feature at topological level (internal use only)
281 
282  Note: requires topology level >= GV_BUILD_BASE.
283 
284  \param pointer to Map_info structure
285  \param line feature id
286 
287  \return 0 on success
288  \return -1 on error
289  */
290 int V2_delete_line_nat(struct Map_info *Map, off_t line)
291 {
292  int type;
293  struct P_line *Line;
294  struct Plus_head *plus;
295  static struct line_cats *Cats = NULL;
296  static struct line_pnts *Points = NULL;
297 
298  G_debug(3, "V2_delete_line_nat(): line = %d", (int)line);
299 
300  Line = NULL;
301  plus = &(Map->plus);
302 
303  if (line < 1 || line > plus->n_lines) {
304  G_warning(_("Attempt to access feature with invalid id (%d)"),
305  (int)line);
306  return -1;
307  }
308 
309  Line = Map->plus.Line[line];
310  if (Line == NULL) {
311  G_warning(_("Attempt to access dead feature %d"), (int)line);
312  return -1;
313  }
314 
315  if (!(plus->update_cidx)) {
316  plus->cidx_up_to_date = FALSE; /* category index will be outdated */
317  }
318 
319  /* read the line */
320  if (!Points) {
321  Points = Vect_new_line_struct();
322  Cats = Vect_new_cats_struct();
323  }
324 
325  type = V2_read_line_nat(Map, Points, Cats, line);
326  if (type <= 0)
327  return -1;
328 
329  /* delete feature from coor file */
330  if (0 != V1_delete_line_nat(Map, Line->offset))
331  return -1;
332 
333  /* delete feature from topology */
334  if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, Cats))
335  return -1;
336 
337  return 0;
338 }
339 
340 /*!
341  \brief Restores feature at level 1 (internal use only)
342 
343  \param Map pointer to Map_info structure
344  \param offset feature offset
345  \param line feature id (not used)
346 
347  \return 0 on success
348  \return -1 on error
349  */
350 int V1_restore_line_nat(struct Map_info *Map, off_t offset, off_t line)
351 {
352  char rhead;
353  struct gvfile *dig_fp;
354 
355  G_debug(3,
356  "V1_restore_line_nat(): offset = %" PRId64
357  ", line (not used) = %" PRId64,
358  offset, line);
359 
360  dig_set_cur_port(&(Map->head.port));
361  dig_fp = &(Map->dig_fp);
362 
363  if (dig_fseek(dig_fp, offset, 0) == -1)
364  return -1;
365 
366  /* read old */
367  if (0 >= dig__fread_port_C(&rhead, 1, dig_fp))
368  return -1;
369 
370  /* mark as alive */
371  rhead |= 1;
372 
373  /* write new */
374  if (dig_fseek(dig_fp, offset, 0) == -1)
375  return -1;
376 
377  if (0 >= dig__fwrite_port_C(&rhead, 1, dig_fp))
378  return -1;
379 
380  if (0 != dig_fflush(dig_fp))
381  return -1;
382 
383  return 0;
384 }
385 
386 /*!
387  \brief Restores feature at topological level (internal use only)
388 
389  Note: requires topology level >= GV_BUILD_BASE.
390 
391  \param Map pointer to Map_info structure
392  \param offset feature offset to be restored
393  \param line feature id to be restored
394 
395  \return 0 on success
396  \return -1 on error
397  */
398 int V2_restore_line_nat(struct Map_info *Map, off_t offset, off_t line)
399 {
400  int type;
401  struct Plus_head *plus;
402  struct P_line *Line;
403  static struct line_cats *Cats = NULL;
404  static struct line_pnts *Points = NULL;
405 
406  plus = &(Map->plus);
407 
408  G_debug(3, "V2_restore_line_nat(): offset = %" PRId64 ", line = %" PRId64,
409  offset, line);
410 
411  if (line < 1 || line > plus->n_lines) {
412  G_warning(_("Attempt to access feature with invalid id (%" PRId64 ")"),
413  line);
414  return -1;
415  }
416 
417  Line = Map->plus
418  .Line[line]; /* we expect Line to be NULL, so offset is needed */
419  if (Line != NULL) {
420  G_warning(_("Attempt to access alive feature %d"), (int)line);
421  return -1;
422  }
423 
424  if (!(plus->update_cidx)) {
425  plus->cidx_up_to_date = 0;
426  }
427 
428  /* restore feature in 'coor' file */
429  if (0 != V1_restore_line_nat(Map, offset, line))
430  return -1;
431 
432  /* read feature geometry */
433  if (!Points)
434  Points = Vect_new_line_struct();
435  if (!Cats)
436  Cats = Vect_new_cats_struct();
437  type = V1_read_line_nat(Map, Points, Cats, offset);
438  if (type < 0)
439  return -1;
440 
441  /* update topology */
442  return V2__add_line_to_topo_nat(Map, offset, type, Points, Cats, line,
443  NULL) > 0
444  ? 0
445  : -1;
446 }
447 
448 /*** static or internal subroutines below ****/
449 
450 /*!
451  \brief Writes feature at the given offset or at the end of the file
452 
453  Internal use only
454 
455  \param Map pointer to Map_info structure
456  \param offset feature offset
457  \param type feature type (GV_POINT, GV_LINE, ...)
458  \param points feature geometry
459  \param cats feature categories
460 
461  \return feature offset
462  \return -1 on error
463  */
464 off_t V1__write_line_nat(struct Map_info *Map, off_t offset, int type,
465  const struct line_pnts *points,
466  const struct line_cats *cats)
467 {
468  int i, n_points;
469  char rhead, nc;
470  short field;
471  struct gvfile *dig_fp;
472 
473  dig_set_cur_port(&(Map->head.port));
474  dig_fp = &(Map->dig_fp);
475 
476  /* if the given offset is smaller than the coor header size,
477  * append new feature to the end of the coor file,
478  * else overwrite whatever exists at offset */
479 
480  if (offset < Map->head.head_size) {
481  if (dig_fseek(&(Map->dig_fp), 0L, SEEK_END) ==
482  -1) /* set to end of file */
483  return -1;
484 
485  offset = dig_ftell(&(Map->dig_fp));
486  G_debug(3, "V1__rewrite_line_nat(): offset = %" PRId64, offset);
487  if (offset == -1)
488  return -1;
489  }
490  else {
491  if (dig_fseek(dig_fp, offset, 0) == -1)
492  return -1;
493  }
494 
495  /* first byte: 0 bit: 1 - alive, 0 - dead
496  * 1 bit: 1 - categories, 0 - no category
497  * 2-3 bit: store type
498  * 4-5 bit: reserved for store type expansion
499  * 6-7 bit: not used
500  */
501 
502  rhead = (char)dig_type_to_store(type);
503  rhead <<= 2;
504  if (cats->n_cats > 0) {
505  rhead |= 0x02;
506  }
507  rhead |= 0x01; /* written/rewritten is always alive */
508 
509  if (0 >= dig__fwrite_port_C(&rhead, 1, dig_fp)) {
510  return -1;
511  }
512 
513  if (cats->n_cats > 0) {
514  if (Map->head.coor_version.minor == 1) { /* coor format 5.1 */
515  if (0 >= dig__fwrite_port_I(&(cats->n_cats), 1, dig_fp))
516  return -1;
517  }
518  else { /* coor format 5.0 */
519  nc = (char)cats->n_cats;
520  if (0 >= dig__fwrite_port_C(&nc, 1, dig_fp))
521  return -1;
522  }
523 
524  if (cats->n_cats > 0) {
525  if (Map->head.coor_version.minor == 1) { /* coor format 5.1 */
526  if (0 >= dig__fwrite_port_I(cats->field, cats->n_cats, dig_fp))
527  return -1;
528  }
529  else { /* coor format 5.0 */
530  for (i = 0; i < cats->n_cats; i++) {
531  field = (short)cats->field[i];
532  if (0 >= dig__fwrite_port_S(&field, 1, dig_fp))
533  return -1;
534  }
535  }
536  if (0 >= dig__fwrite_port_I(cats->cat, cats->n_cats, dig_fp))
537  return -1;
538  }
539  }
540 
541  if (type & GV_POINTS) {
542  n_points = 1;
543  }
544  else {
545  n_points = points->n_points;
546  if (0 >= dig__fwrite_port_I(&n_points, 1, dig_fp))
547  return -1;
548  }
549 
550  if (0 >= dig__fwrite_port_D(points->x, n_points, dig_fp))
551  return -1;
552  if (0 >= dig__fwrite_port_D(points->y, n_points, dig_fp))
553  return -1;
554 
555  if (Map->head.with_z) {
556  if (0 >= dig__fwrite_port_D(points->z, n_points, dig_fp))
557  return -1;
558  }
559 
560  if (0 != dig_fflush(dig_fp))
561  return -1;
562 
563  return offset;
564 }
565 
566 /*!
567  \brief Deletes area (i.e. centroid) categories from category
568  index (internal use only)
569 
570  Call G_fatal_error() when area do not exits.
571 
572  \param Map pointer to Map_info structure
573  \param area area id
574  */
575 void V2__delete_area_cats_from_cidx_nat(struct Map_info *Map, int area)
576 {
577  int i;
578  struct P_area *Area;
579  static struct line_cats *Cats = NULL;
580 
581  G_debug(3, "V2__delete_area_cats_from_cidx_nat(), area = %d", area);
582 
583  Area = Map->plus.Area[area];
584  if (!Area)
585  G_fatal_error(_("%s: Area %d does not exist"),
586  "delete_area_cats_from_cidx()", area);
587 
588  if (Area->centroid == 0) /* no centroid found */
589  return;
590 
591  if (!Cats)
592  Cats = Vect_new_cats_struct();
593 
594  Vect_read_line(Map, NULL, Cats, Area->centroid);
595 
596  for (i = 0; i < Cats->n_cats; i++) {
597  dig_cidx_del_cat(&(Map->plus), Cats->field[i], Cats->cat[i], area,
598  GV_AREA);
599  }
600 }
601 
602 /*!
603  \brief Adds area (i.e. centroid) categories from category index
604  (internal use only)
605 
606  Call G_fatal_error() when area do not exits.
607 
608  \param Map pointer to Map_info structure
609  \param area area id
610  */
611 void V2__add_area_cats_to_cidx_nat(struct Map_info *Map, int area)
612 {
613  int i;
614  struct P_area *Area;
615  static struct line_cats *Cats = NULL;
616 
617  G_debug(3, "V2__add_area_cats_to_cidx_nat(), area = %d", area);
618 
619  Area = Map->plus.Area[area];
620  if (!Area)
621  G_fatal_error(_("%s: Area %d does not exist"),
622  "add_area_cats_to_cidx():", area);
623 
624  if (Area->centroid == 0) /* no centroid found */
625  return;
626 
627  if (!Cats)
628  Cats = Vect_new_cats_struct();
629 
630  V2_read_line_nat(Map, NULL, Cats, Area->centroid);
631 
632  for (i = 0; i < Cats->n_cats; i++) {
633  dig_cidx_add_cat_sorted(&(Map->plus), Cats->field[i], Cats->cat[i],
634  area, GV_AREA);
635  }
636 }
637 
638 /*!
639  \brief Delete feature from topology (internal use only)
640 
641  Note: This function requires build level >= GV_BUILD_BASE.
642 
643  Also updates category index if requested.
644 
645  Calls G_warning() on error.
646 
647  \param Map pointer to Map_info struct
648  \param line feature id to be removed
649  \param Points feature geometry (pointer to line_pnts struct)
650  \param external_routine external subroutine to execute (used by PostGIS
651  Topology)
652 
653  \return 0 on success
654  \return -1 on failure
655  */
656 int V2__delete_line_from_topo_nat(struct Map_info *Map, int line, int type,
657  const struct line_pnts *points,
658  const struct line_cats *cats)
659 {
660  int i, first = 1;
661  int adjacent[4], n_adjacent;
662 
663  struct bound_box box, abox;
664  struct Plus_head *plus;
665  struct P_line *Line;
666 
667  n_adjacent = 0;
668 
669  plus = &(Map->plus);
670 
671  if (line < 1 || line > plus->n_lines) {
672  G_warning(_("Attempt to access feature with invalid id (%d)"), line);
673  return -1;
674  }
675 
676  Line = Map->plus.Line[line];
677  if (!Line) {
678  G_warning(_("Attempt to access dead feature %d"), line);
679  return -1;
680  }
681 
682  /* delete feature from category index */
683  if (plus->update_cidx && cats) {
684  for (i = 0; i < cats->n_cats; i++) {
685  dig_cidx_del_cat(plus, cats->field[i], cats->cat[i], line, type);
686  }
687  }
688 
689  /* update areas when deleting boundary from topology */
690  if (plus->built >= GV_BUILD_AREAS && Line->type == GV_BOUNDARY) {
691  int next_line;
692 
693  struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
694 
695  /* store adjacent boundaries at nodes (will be used to rebuild
696  * area/isle) */
697  /* adjacent are stored: > 0 - we want right side; < 0 - we want left
698  * side */
699  n_adjacent = 0;
700 
701  next_line =
703  if (next_line != 0 && abs(next_line) != line) {
704  /* N1, to the right -> we want the right side for > 0 and left for
705  * < 0 */
706  adjacent[n_adjacent] = next_line;
707  n_adjacent++;
708  }
709  next_line = dig_angle_next_line(plus, line, GV_LEFT, GV_BOUNDARY, NULL);
710  if (next_line != 0 && abs(next_line) != line) {
711  /* N1, to the left -> we want the left side for > 0 and right for <
712  * 0 */
713  adjacent[n_adjacent] = -next_line;
714  n_adjacent++;
715  }
716  next_line =
718  if (next_line != 0 && abs(next_line) != line) {
719  /* N2, to the right -> we want the right side for > 0 and left for
720  * < 0 */
721  adjacent[n_adjacent] = next_line;
722  n_adjacent++;
723  }
724  next_line =
726  if (next_line != 0 && abs(next_line) != line) {
727  /* N2, to the left -> we want the left side for > 0 and right for <
728  * 0 */
729  adjacent[n_adjacent] = -next_line;
730  n_adjacent++;
731  }
732 
733  /* delete area(s) and islands this line forms */
734  first = 1;
735  if (topo->left > 0) { /* delete area */
736  Vect_get_area_box(Map, topo->left, &box);
737  if (first) {
738  Vect_box_copy(&abox, &box);
739  first = 0;
740  }
741  else
742  Vect_box_extend(&abox, &box);
743 
744  if (plus->update_cidx) {
745  V2__delete_area_cats_from_cidx_nat(Map, topo->left);
746  }
747  dig_del_area(plus, topo->left);
748  }
749  else if (topo->left < 0) { /* delete isle */
750  dig_del_isle(plus, -topo->left);
751  }
752  if (topo->right > 0) { /* delete area */
753  Vect_get_area_box(Map, topo->right, &box);
754  if (first) {
755  Vect_box_copy(&abox, &box);
756  first = 0;
757  }
758  else
759  Vect_box_extend(&abox, &box);
760 
761  if (plus->update_cidx) {
762  V2__delete_area_cats_from_cidx_nat(Map, topo->right);
763  }
764  dig_del_area(plus, topo->right);
765  }
766  else if (topo->right < 0) { /* delete isle */
767  dig_del_isle(plus, -topo->right);
768  }
769  }
770 
771  /* delete reference from area */
772  if (plus->built >= GV_BUILD_CENTROIDS && Line->type == GV_CENTROID) {
773  struct P_area *Area;
774 
775  struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
776 
777  if (topo->area > 0) {
778  G_debug(3, "Remove centroid %d from area %d", (int)line,
779  topo->area);
780  if (plus->update_cidx) {
781  V2__delete_area_cats_from_cidx_nat(Map, topo->area);
782  }
783  Area = Map->plus.Area[topo->area];
784  if (Area)
785  Area->centroid = 0;
786  else
787  G_warning(_("Attempt to access dead area %d"), topo->area);
788  }
789  }
790 
791  /* delete the line from topo */
792  if (0 != dig_del_line(plus, line, points->x[0], points->y[0], points->z[0]))
793  return -1;
794 
795  /* rebuild areas/isles and attach centroids and isles */
796  if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY) {
797  int i, side, area;
798  int new_areas[4], nnew_areas = 0;
799 
800  /* Rebuild areas/isles */
801  for (i = 0; i < n_adjacent; i++) {
802  side = (adjacent[i] > 0 ? GV_RIGHT : GV_LEFT);
803 
804  G_debug(3, "Build area for line = %d, side = %d", adjacent[i],
805  side);
806 
807  area = Vect_build_line_area(Map, abs(adjacent[i]), side);
808  if (area > 0) { /* area */
809  Vect_get_area_box(Map, area, &box);
810  if (first) {
811  Vect_box_copy(&abox, &box);
812  first = 0;
813  }
814  else
815  Vect_box_extend(&abox, &box);
816 
817  new_areas[nnew_areas] = area;
818  nnew_areas++;
819  }
820  else if (area < 0) {
821  /* isle -> must be attached -> add to abox */
822  Vect_get_isle_box(Map, -area, &box);
823  if (first) {
824  Vect_box_copy(&abox, &box);
825  first = 0;
826  }
827  else
828  Vect_box_extend(&abox, &box);
829  }
830  }
831  /* reattach all centroids/isles in deleted areas + new area.
832  * because isles are selected by box it covers also possible new isle
833  * created above */
834  if (!first) { /* i.e. old area/isle was deleted or new one created */
835  /* reattach isles */
836  if (plus->built >= GV_BUILD_ATTACH_ISLES)
837  Vect_attach_isles(Map, &abox);
838 
839  /* reattach centroids */
840  if (plus->built >= GV_BUILD_CENTROIDS)
841  Vect_attach_centroids(Map, &abox);
842  }
843 
844  if (plus->update_cidx) {
845  for (i = 0; i < nnew_areas; i++) {
846  V2__add_area_cats_to_cidx_nat(Map, new_areas[i]);
847  }
848  }
849  }
850 
851  if (plus->uplist.do_uplist) {
852  G_debug(3, "updated lines : %d , updated nodes : %d",
853  plus->uplist.n_uplines, plus->uplist.n_upnodes);
854  }
855 
856  return 0;
857 }
858 
859 /*!
860  \brief Add feature (line) to topology (internal use only)
861 
862  Also updates category index if requested.
863 
864  Update areas. Areas are modified if:
865 
866  1) first or/and last point are existing nodes ->
867  - drop areas/islands whose boundaries are neighbour to this boundary at these
868  nodes
869  - try build areas and islands for this boundary and neighbour boundaries
870  going through these nodes
871 
872  Question: may be by adding line created new area/isle which doesn't go
873  through nodes of this line
874 
875  <pre>
876  old new line
877  +----+----+ +----+----+ +----+----+
878  | A1 | A2 | + / -> | A1 | /| or + \ -> | A1 | A2 | \
879  | | | | | | | | |
880  +----+----+ +----+----+ +----+----+
881  I1 I1 I1 I1
882  </pre>
883 
884  - re-attach all centroids/isles inside new area(s)
885  - attach new isle to area outside
886 
887  2) line is closed ring (node at the end is new, so it is not case above)
888  - build new area/isle
889  - check if it is island or contains island(s)
890  - re-attach all centroids/isles inside new area(s)
891  - attach new isle to area outside
892 
893  Note that 1) and 2) is done by the same code.
894 
895  \param Map pointer to Map_info structure
896  \param offset feature offset to be added
897  \param points pointer to line_pnts structure (feature's geometry)
898  \param cats pointer to line_cats structure (feature's categories)
899  \param restore_line feature id to be restored (>0) or added (<=0)
900  \param external_routine pointer to external routine (used by PostGIS
901  Topology)
902 
903  \return feature id to be added
904  \return 0 nothing to do (build level must be >= GV_BUILD_BASE)
905  \return -1 on error
906  */
907 int V2__add_line_to_topo_nat(struct Map_info *Map, off_t offset, int type,
908  const struct line_pnts *points,
909  const struct line_cats *cats, int restore_line,
910  int (*external_routine)(struct Map_info *, int))
911 {
912  int first, s, n, i, line;
913  int node, next_line, area, side, sel_area, new_area[2];
914 
915  struct Plus_head *plus;
916  struct P_line *Line, *NLine;
917  struct P_node *Node;
918  struct P_area *Area;
919 
920  struct bound_box box, abox;
921 
922  plus = &(Map->plus);
923 
924  G_debug(3,
925  "V2__add_line_to_topo_nat(): offset = %" PRId64
926  " (build level = %d)",
927  offset, plus->built);
928 
929  if (plus->built < GV_BUILD_BASE) /* nothing to build */
930  return 0;
931 
932  /* add line to topology */
933  dig_line_box(points, &box);
934  if (restore_line > 0)
935  line = dig_restore_line(plus, restore_line, type, points, &box, offset);
936  else
937  line = dig_add_line(plus, type, points, &box, offset);
938  G_debug(3, " line added to topo with id = %d", line);
939 
940  Line = plus->Line[line];
941 
942  /* extend map bounding box */
943  if (line == 1)
944  Vect_box_copy(&(plus->box), &box);
945  else
946  Vect_box_extend(&(plus->box), &box);
947 
948  /* build areas on left/right side */
949  if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY) {
950  struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
951 
952  /* delete neighbour areas/isles */
953  first = TRUE;
954  for (s = 0; s < 2; s++) { /* for each node */
955  node = (s == 0 ? topo->N1 : topo->N2);
956  G_debug(3, " delete neighbour areas/isles: %s node = %d",
957  (s == 0 ? "first" : "second"), node);
958  Node = plus->Node[node];
959  n = 0;
960  for (i = 0; i < Node->n_lines; i++) {
961  NLine = plus->Line[abs(Node->lines[i])];
962  if (NLine->type == GV_BOUNDARY)
963  n++;
964  }
965 
966  G_debug(3, " number of boundaries at node = %d", n);
967  if (n > 2) {
968  /* more than 2 boundaries at node ( >= 2 old + 1 new ) */
969  /* Line above (to the right), it is enough to check to
970  the right, because if area/isle exists it is the
971  same to the left */
972  if (!s)
973  next_line = dig_angle_next_line(plus, line, GV_RIGHT,
974  GV_BOUNDARY, NULL);
975  else
976  next_line = dig_angle_next_line(plus, -line, GV_RIGHT,
977  GV_BOUNDARY, NULL);
978 
979  if (next_line != 0) { /* there is a boundary to the right */
980  NLine = plus->Line[abs(next_line)];
981  topo = (struct P_topo_b *)NLine->topo;
982  if (next_line >
983  0) /* the boundary is connected by 1. node */
984  /* we are interested just in this side (close to our
985  * line) */
986  area = topo->right;
987  else if (next_line <
988  0) /* the boundary is connected by 2. node */
989  area = topo->left;
990 
991  G_debug(3, " next_line = %d area = %d", next_line, area);
992  if (area > 0) { /* is area */
993  Vect_get_area_box(Map, area, &box);
994  if (first) {
995  Vect_box_copy(&abox, &box);
996  first = FALSE;
997  }
998  else
999  Vect_box_extend(&abox, &box);
1000 
1001  if (plus->update_cidx) {
1002  V2__delete_area_cats_from_cidx_nat(Map, area);
1003  }
1004  dig_del_area(plus, area);
1005  if (external_routine) /* call external subroutine if
1006  defined */
1007  external_routine(Map, area);
1008  }
1009  else if (area < 0) { /* is isle */
1010  dig_del_isle(plus, -area);
1011  if (external_routine) /* call external subroutine if
1012  defined */
1013  external_routine(Map, area);
1014  }
1015  }
1016  }
1017  }
1018 
1019  /* Build new areas/isles.
1020  * It's true that we deleted also adjacent areas/isles, but
1021  * if they form new one our boundary must participate, so
1022  * we need to build areas/isles just for our boundary */
1023  for (s = 0; s < 2; s++) {
1024  side = (s == 0 ? GV_LEFT : GV_RIGHT);
1025  area = Vect_build_line_area(Map, line, side);
1026 
1027  if (area > 0) { /* area */
1028  Vect_get_area_box(Map, area, &box);
1029  if (first) {
1030  Vect_box_copy(&abox, &box);
1031  first = FALSE;
1032  }
1033  else
1034  Vect_box_extend(&abox, &box);
1035  }
1036  else if (area < 0) {
1037  /* isle -> must be attached -> add to abox */
1038  Vect_get_isle_box(Map, -area, &box);
1039  if (first) {
1040  Vect_box_copy(&abox, &box);
1041  first = FALSE;
1042  }
1043  else
1044  Vect_box_extend(&abox, &box);
1045  }
1046  new_area[s] = area;
1047  }
1048  /* Reattach all centroids/isles in deleted areas + new area.
1049  * Because isles are selected by box it covers also possible
1050  * new isle created above */
1051  if (!first) { /* i.e. old area/isle was deleted or new one created */
1052  /* Reattach isles */
1053  if (plus->built >= GV_BUILD_ATTACH_ISLES)
1054  Vect_attach_isles(Map, &abox);
1055 
1056  /* Reattach centroids */
1057  if (plus->built >= GV_BUILD_CENTROIDS)
1058  Vect_attach_centroids(Map, &abox);
1059  }
1060  /* Add to category index */
1061  if (plus->update_cidx) {
1062  for (s = 0; s < 2; s++) {
1063  if (new_area[s] > 0) {
1064  V2__add_area_cats_to_cidx_nat(Map, new_area[s]);
1065  }
1066  }
1067  }
1068  }
1069 
1070  /* attach centroid */
1071  if (plus->built >= GV_BUILD_CENTROIDS) {
1072  struct P_topo_c *topo;
1073 
1074  if (type == GV_CENTROID) {
1075  sel_area = Vect_find_area(Map, points->x[0], points->y[0]);
1076  G_debug(3, " new centroid %d is in area %d", line, sel_area);
1077  if (sel_area > 0) {
1078  Area = plus->Area[sel_area];
1079  Line = plus->Line[line];
1080  topo = (struct P_topo_c *)Line->topo;
1081  if (Area->centroid == 0) { /* first centroid */
1082  G_debug(3, " first centroid -> attach to area");
1083  Area->centroid = line;
1084  topo->area = sel_area;
1085  if (plus->update_cidx) {
1086  V2__add_area_cats_to_cidx_nat(Map, sel_area);
1087  }
1088  }
1089  else { /* duplicate centroid */
1090  G_debug(3, " duplicate centroid -> do not attach to area");
1091  topo->area = -sel_area;
1092  }
1093  }
1094  }
1095  }
1096 
1097  /* add category index */
1098  if (plus->update_cidx && cats) {
1099  for (i = 0; i < cats->n_cats; i++) {
1100  dig_cidx_add_cat_sorted(plus, cats->field[i], cats->cat[i], line,
1101  type);
1102  }
1103  }
1104 
1105  if (plus->uplist.do_uplist) {
1106  G_debug(3, "updated lines : %d , updated nodes : %d",
1107  plus->uplist.n_uplines, plus->uplist.n_upnodes);
1108  }
1109 
1110  return line;
1111 }
#define NULL
Definition: ccmath.h:32
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
int V2_read_line_nat(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature on topological level (level 2) - native format - internal use only.
Definition: read_nat.c:136
int Vect_attach_centroids(struct Map_info *, const struct bound_box *)
(Re)Attach centroids in given bounding box to areas
Definition: build.c:505
int Vect_build_line_area(struct Map_info *, int, int)
Build area on given side of line (GV_LEFT or GV_RIGHT)
Definition: build.c:79
int Vect_box_extend(struct bound_box *, const struct bound_box *)
Extend box A by box B.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
int Vect_get_area_box(struct Map_info *, int, struct bound_box *)
Get bounding box of area.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition: line.c:45
int Vect_get_isle_box(struct Map_info *, int, struct bound_box *)
Get bounding box of isle.
int Vect_attach_isles(struct Map_info *, const struct bound_box *)
(Re)Attach isles in given bounding box to areas
Definition: build.c:427
int V1_read_line_nat(struct Map_info *, struct line_pnts *, struct line_cats *, off_t)
Read vector feature on non-topological level (level 1) - native format - internal use only.
Definition: read_nat.c:43
struct line_cats * Vect_new_cats_struct(void)
Creates and initializes line_cats structure.
int Vect_find_area(struct Map_info *, double, double)
Find the nearest area.
int Vect_box_copy(struct bound_box *, const struct bound_box *)
Copy box B to box A.
#define GV_CENTROID
Definition: dig_defines.h:186
#define GV_LINES
Definition: dig_defines.h:193
#define GV_BOUNDARY
Definition: dig_defines.h:185
#define GV_BUILD_ATTACH_ISLES
Topology levels - attach islands to areas.
Definition: dig_defines.h:129
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
Definition: dig_defines.h:125
#define GV_BUILD_AREAS
Topology levels - build areas.
Definition: dig_defines.h:127
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
Definition: dig_defines.h:131
#define GV_RIGHT
Definition: dig_defines.h:176
#define GV_POINTS
Definition: dig_defines.h:192
#define GV_AREA
Definition: dig_defines.h:189
#define GV_LEFT
Boundary side indicator left/right.
Definition: dig_defines.h:175
int dig_restore_line(struct Plus_head *, int, int, const struct line_pnts *, const struct bound_box *, off_t)
Restore line in Plus_head structure.
Definition: plus_line.c:189
int dig_angle_next_line(struct Plus_head *, plus_t, int, int, float *)
Find line number of next angle to follow a line.
Definition: plus_area.c:474
int dig__fwrite_port_C(const char *, size_t, struct gvfile *)
Write chars to the Portable Vector Format.
Definition: portable.c:886
int dig_del_line(struct Plus_head *, int, double, double, double)
Delete line from Plus_head structure.
Definition: plus_line.c:216
int dig__fwrite_port_I(const int *, size_t, struct gvfile *)
Write integers to the Portable Vector Format.
Definition: portable.c:758
off_t dig_ftell(struct gvfile *file)
Get struct gvfile position.
Definition: file.c:36
int dig_del_area(struct Plus_head *, int)
Delete area from Plus_head structure.
Definition: plus_area.c:365
int dig_set_cur_port(struct Port_info *)
Set current Port_info structure.
Definition: portable.c:996
int dig__fwrite_port_D(const double *, size_t, struct gvfile *)
Write doubles to the Portable Vector Format.
Definition: portable.c:559
int dig__fread_port_C(char *, size_t, struct gvfile *)
Read chars from the Portable Vector Format.
Definition: portable.c:511
int dig_del_isle(struct Plus_head *, int)
Delete island from Plus_head structure.
Definition: plus_area.c:781
int dig_cidx_del_cat(struct Plus_head *, int, int, int, int)
int dig_fseek(struct gvfile *file, off_t offset, int whence)
Set struct gvfile position.
Definition: file.c:60
int dig_cidx_add_cat_sorted(struct Plus_head *, int, int, int, int)
int dig_fflush(struct gvfile *file)
Flush struct gvfile.
Definition: file.c:104
int dig_add_line(struct Plus_head *, int, const struct line_pnts *, const struct bound_box *, off_t)
Add new line to Plus_head structure.
Definition: plus_line.c:133
int dig_line_box(const struct line_pnts *, struct bound_box *)
int dig__fwrite_port_S(const short *, size_t, struct gvfile *)
Write shorts to the Portable Vector Format.
Definition: portable.c:813
int dig_type_to_store(int)
Convert type to store type.
#define TRUE
Definition: gis.h:79
#define FALSE
Definition: gis.h:83
#define _(str)
Definition: glocale.h:10
if(!(yy_init))
Definition: sqlp.yy.c:775
Vector map info.
Definition: dig_structs.h:1243
struct gvfile dig_fp
GV file pointer (native format only)
Definition: dig_structs.h:1395
struct dig_head head
Header info.
Definition: dig_structs.h:1388
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1270
Area (topology) info.
Definition: dig_structs.h:1583
plus_t centroid
Number of first centroid within area.
Definition: dig_structs.h:1605
Vector geometry.
Definition: dig_structs.h:1553
char type
Line type.
Definition: dig_structs.h:1564
off_t offset
Offset in coor file for line.
Definition: dig_structs.h:1571
void * topo
Topology info.
Definition: dig_structs.h:1577
Topological feature - node.
Definition: dig_structs.h:1433
plus_t n_lines
Number of attached lines (size of lines, angle)
Definition: dig_structs.h:1456
plus_t * lines
List of connected lines.
Definition: dig_structs.h:1463
Boundary topology.
Definition: dig_structs.h:1492
plus_t left
Area number to the left, negative for isle.
Definition: dig_structs.h:1504
plus_t N1
Start node.
Definition: dig_structs.h:1496
plus_t N2
End node.
Definition: dig_structs.h:1500
plus_t right
Area number to the right, negative for isle.
Definition: dig_structs.h:1508
Centroid topology.
Definition: dig_structs.h:1514
plus_t area
Area number, negative for duplicate centroid.
Definition: dig_structs.h:1518
Basic topology-related info.
Definition: dig_structs.h:769
int do_uplist
Indicates if the list of updated features is maintained.
Definition: dig_structs.h:1161
struct P_line ** Line
Array of vector geometries.
Definition: dig_structs.h:871
plus_t n_lines
Current number of lines.
Definition: dig_structs.h:931
int n_upnodes
number of updated nodes
Definition: dig_structs.h:1197
struct P_area ** Area
Array of areas.
Definition: dig_structs.h:875
int cidx_up_to_date
Category index to be updated.
Definition: dig_structs.h:1140
int update_cidx
Update category index if vector is modified.
Definition: dig_structs.h:1120
struct Plus_head::@10 uplist
List of updated lines/nodes.
struct bound_box box
Bounding box of features.
Definition: dig_structs.h:861
int n_uplines
Number of updated lines.
Definition: dig_structs.h:1185
struct P_node ** Node
Array of nodes.
Definition: dig_structs.h:867
int built
Highest level of topology currently available.
Definition: dig_structs.h:857
int minor
Current version (minor)
Definition: dig_structs.h:275
Bounding box.
Definition: dig_structs.h:64
struct Version_info coor_version
Version info for coor file.
Definition: dig_structs.h:331
int with_z
2D/3D vector data
Definition: dig_structs.h:339
struct Port_info port
Portability information.
Definition: dig_structs.h:353
File definition.
Definition: dig_structs.h:94
Feature category info.
Definition: dig_structs.h:1677
int * field
Array of layers (fields)
Definition: dig_structs.h:1681
int * cat
Array of categories.
Definition: dig_structs.h:1685
int n_cats
Number of categories attached to element.
Definition: dig_structs.h:1689
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
int V2__add_line_to_topo_nat(struct Map_info *Map, off_t offset, int type, const struct line_pnts *points, const struct line_cats *cats, int restore_line, int(*external_routine)(struct Map_info *, int))
Add feature (line) to topology (internal use only)
Definition: write_nat.c:907
off_t V2_rewrite_line_nat(struct Map_info *Map, off_t line, int type, const struct line_pnts *points, const struct line_cats *cats)
Rewrites feature to 'coor' file at topological level (internal use only)
Definition: write_nat.c:161
int V1_delete_line_nat(struct Map_info *Map, off_t offset)
Deletes feature at level 1 (internal use only)
Definition: write_nat.c:248
int V2_restore_line_nat(struct Map_info *Map, off_t offset, off_t line)
Restores feature at topological level (internal use only)
Definition: write_nat.c:398
off_t V2_write_line_nat(struct Map_info *Map, int type, const struct line_pnts *points, const struct line_cats *cats)
Writes feature to 'coor' file at topological level (internal use only)
Definition: write_nat.c:67
off_t V1_write_line_nat(struct Map_info *Map, int type, const struct line_pnts *points, const struct line_cats *cats)
Writes feature to 'coor' file at level 1 (internal use only)
Definition: write_nat.c:45
int V2__delete_line_from_topo_nat(struct Map_info *Map, int line, int type, const struct line_pnts *points, const struct line_cats *cats)
Delete feature from topology (internal use only)
Definition: write_nat.c:656
int V2_delete_line_nat(struct Map_info *Map, off_t line)
Deletes feature at topological level (internal use only)
Definition: write_nat.c:290
off_t V1_rewrite_line_nat(struct Map_info *Map, off_t offset, int type, const struct line_pnts *points, const struct line_cats *cats)
Rewrites feature to 'coor' file at level 1 (internal use only)
Definition: write_nat.c:105
int V1_restore_line_nat(struct Map_info *Map, off_t offset, off_t line)
Restores feature at level 1 (internal use only)
Definition: write_nat.c:350