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 */
V1_write_line_nat(struct Map_info * Map,int type,const struct line_pnts * points,const struct line_cats * cats)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 */
V2_write_line_nat(struct Map_info * Map,int type,const struct line_pnts * points,const struct line_cats * cats)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 */
V1_rewrite_line_nat(struct Map_info * Map,off_t offset,int type,const struct line_pnts * points,const struct line_cats * cats)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 */
V2_rewrite_line_nat(struct Map_info * Map,off_t line,int type,const struct line_pnts * points,const struct line_cats * cats)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 */
V1_delete_line_nat(struct Map_info * Map,off_t offset)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 */
V2_delete_line_nat(struct Map_info * Map,off_t line)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 */
V1_restore_line_nat(struct Map_info * Map,off_t offset,off_t line)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 */
V2_restore_line_nat(struct Map_info * Map,off_t offset,off_t line)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 */
V1__write_line_nat(struct Map_info * Map,off_t offset,int type,const struct line_pnts * points,const struct line_cats * cats)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 = %lu", 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 */
V2__delete_area_cats_from_cidx_nat(struct Map_info * Map,int area)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 */
V2__add_area_cats_to_cidx_nat(struct Map_info * Map,int area)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  */
V2__delete_line_from_topo_nat(struct Map_info * Map,int line,int type,const struct line_pnts * points,const struct line_cats * cats)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 */
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))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 = %ld (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 }
1074