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