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