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