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