GRASS 8 Programmer's Manual 8.6.0dev(2026)-1d1e47ad9d
Loading...
Searching...
No Matches
build_pg.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/build_pg.c
3
4 \brief Vector library - Building topology for PostGIS layers
5
6 Higher level functions for reading/writing/manipulating vectors.
7
8 Line offset (simple features only) is
9 - centroids : FID
10 - other types : index of the first record (which is FID) in offset array.
11
12 (C) 2012-2013 by the GRASS Development Team
13
14 This program is free software under the GNU General Public License
15 (>=v2). Read the file COPYING that comes with GRASS for details.
16
17 \author Martin Landa <landa.martin gmail.com>
18 */
19
20#include <grass/vector.h>
21#include <grass/glocale.h>
22
23#include "local_proto.h"
24
25#ifdef HAVE_POSTGRES
26#include "pg_local_proto.h"
27
28static int build_topo(struct Map_info *, int);
29static int build_topogeom_stmt(const struct Format_info_pg *, int, int, int,
30 char *);
31static int save_map_bbox(const struct Format_info_pg *,
32 const struct bound_box *);
33static int create_topo_grass(const struct Format_info_pg *);
34static int has_topo_grass(const struct Format_info_pg *);
35static int write_nodes(const struct Plus_head *, const struct Format_info_pg *);
36static int write_lines(const struct Plus_head *, const struct Format_info_pg *);
37static int write_areas(const struct Plus_head *, const struct Format_info_pg *);
38static int write_isles(const struct Plus_head *, const struct Format_info_pg *);
39static void build_stmt_id(const void *, int, int, const struct Plus_head *,
40 char **, size_t *);
41static int create_simple_feature_from_topo(struct Map_info *);
42#define NOPG_UNUSED
43#else
44#define NOPG_UNUSED UNUSED
45#endif
46
47/*!
48 \brief Build topology for PostGIS layer
49
50 Build levels:
51 - GV_BUILD_NONE
52 - GV_BUILD_BASE
53 - GV_BUILD_ATTACH_ISLES
54 - GV_BUILD_CENTROIDS
55 - GV_BUILD_ALL
56
57 \param Map pointer to Map_info structure
58 \param build build level
59
60 \return 1 on success
61 \return 0 on error
62 */
64{
65#ifdef HAVE_POSTGRES
66 struct Plus_head *plus;
67 struct Format_info_pg *pg_info;
68
69 plus = &(Map->plus);
70 pg_info = &(Map->fInfo.pg);
71
72 G_debug(1, "Vect_build_pg(): db='%s' table='%s', build=%d",
73 pg_info->db_name, pg_info->table_name, build);
74
75 /* commit transaction block (update mode only) */
76 if (pg_info->inTransaction &&
77 Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
78 return 0;
79 pg_info->inTransaction = FALSE;
80
81 if (pg_info->feature_type == SF_GEOMETRY)
82 return 1;
83
84 if (build == plus->built)
85 return 1; /* do nothing */
86
87 /* TODO move this init to better place (Vect_open_ ?), because in
88 theory build may be reused on level2 */
89 if (!pg_info->toposchema_name && build >= plus->built &&
91 G_free(pg_info->offset.array);
92 G_zero(&(pg_info->offset), sizeof(struct Format_info_offset));
93 }
94
95 if (!pg_info->conn) {
96 G_warning(_("No DB connection"));
97 return 0;
98 }
99
100 if (!pg_info->fid_column && !pg_info->toposchema_name) {
101 G_warning(_("Feature table <%s> has no primary key defined"),
102 pg_info->table_name);
103 G_warning(_("Random read is not supported for this layer. "
104 "Unable to build topology."));
105 return 0;
106 }
107
108 if (build > GV_BUILD_NONE) {
109 G_message(_("Using external data format '%s' (feature type '%s')"),
112 if (!pg_info->toposchema_name)
113 G_message(_("Building pseudo-topology over simple features..."));
114 else
115 G_message(
116 _("Building topology from PostGIS topology schema <%s>..."),
117 pg_info->toposchema_name);
118 }
119
120 if (!pg_info->toposchema_name) /* pseudo-topology for simple features */
121 return Vect__build_sfa(Map, build);
122
123 /* PostGIS Topology */
124 return build_topo(Map, build);
125#else
126 G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
127 return 0;
128#endif
129}
130
131#ifdef HAVE_POSTGRES
132/*!
133 \brief Build from PostGIS topology schema
134
135 \todo Attach isles
136
137 \param Map pointer to Map_info struct
138 \param build build level
139
140 \return 1 on success
141 \return 0 on error
142 */
143int build_topo(struct Map_info *Map, int build)
144{
145 int line, type, s, n_nodes = 0;
146 int area, nareas, isle, nisles;
147 int face[2];
148 char stmt[DB_SQL_MAX];
149 char *def_file;
150
151 struct Plus_head *plus;
152 struct Format_info_pg *pg_info;
153
154 struct P_line *Line;
155 struct P_area *Area;
156 struct P_topo_b *topo_b;
157 struct P_isle *Isle;
158
159 plus = &(Map->plus);
160 pg_info = &(Map->fInfo.pg);
161
162 /* check if upgrade or downgrade */
163 if (build < plus->built) {
164 /* -> downgrade */
166 return 1;
167 }
168 /* -> upgrade */
169
170 if (build < GV_BUILD_BASE)
171 return 1; /* nothing to print */
172
173 /* cache features to speed-up random access (when attaching isles
174 to areas) */
175 if (build >= GV_BUILD_BASE) {
176 /* clean-up GRASS topology tables in DB */
177 if (!pg_info->topo_geo_only)
178 Vect__clean_grass_db_topo(&(Map->fInfo.pg));
179
180 if (Map->mode == GV_MODE_RW && pg_info->cache.lines_num > 0) {
181
182 /* read line cache from scratch when map is open in update
183 * mode, before building native topology read nodes from
184 * PostGIS Topology */
185
186 /* clean-up spatial a category indices */
187 dig_free_plus(&(Map->plus));
188 dig_init_plus(&(Map->plus));
189 plus->Spidx_new = TRUE;
190 plus->update_cidx = TRUE;
191
192 /* reset cache for reading features */
193 Vect__free_cache(&(pg_info->cache));
194 }
195 }
196
197 if (plus->built >= GV_BUILD_BASE && pg_info->cache.lines_num < 1) {
198 /* features are not cached, build from scratch */
200 }
201
202 if (plus->built < GV_BUILD_BASE) {
203 /* force loading nodes from DB to get up-to-date node
204 * offsets, see write_nodes() for details */
205 Vect__free_offset(&(pg_info->offset));
206
207 pg_info->cache.ctype = CACHE_FEATURE; /* do not cache nodes */
208 n_nodes = Map->plus.n_nodes = Vect__load_map_nodes_pg(Map, TRUE);
209 Vect__free_cache(&(pg_info->cache));
210 }
211
212 if (build > GV_BUILD_BASE)
213 pg_info->cache.ctype = CACHE_MAP; /* cache all features */
214
215 /* update TopoGeometry based on GRASS-like topology */
217
218 if (n_nodes != Map->plus.n_nodes)
219 G_warning(
220 _("Inconsistency in topology: number of nodes %d (should be %d)"),
221 Map->plus.n_nodes, n_nodes);
222
223 /* store map bounding box in DB */
224 save_map_bbox(pg_info, &(plus->box));
225
226 /* begin transaction */
227 if (Vect__execute_pg(pg_info->conn, "BEGIN"))
228 return 0;
229
230 Vect__execute_pg(pg_info->conn, "SET CONSTRAINTS ALL DEFERRED");
231
232 /* write full node topo info to DB if requested */
233 if (!pg_info->topo_geo_only) {
234 write_nodes(plus, pg_info);
235 write_lines(plus, pg_info);
236 }
237
238 /* update faces from GRASS Topology */
239 if (build >= GV_BUILD_AREAS) {
240 /* do clean up (1-3)
241 insert new faces (4)
242 update edges (5)
243 */
244
245 G_message(_("Cleaning-up topology schema..."));
246 /* 1) reset centroids to '0' (universal face) */
247 snprintf(stmt, sizeof(stmt),
248 "UPDATE \"%s\".node SET containing_face = 0 WHERE "
249 "containing_face IS NOT NULL",
250 pg_info->toposchema_name);
251 G_debug(2, "SQL: %s", stmt);
252 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
253 Vect__execute_pg(pg_info->conn, "ROLLBACK");
254 return 0;
255 }
256
257 /* 2) reset left|right edges */
258 snprintf(stmt, sizeof(stmt),
259 "UPDATE \"%s\".edge_data SET left_face = 0, right_face = 0",
260 pg_info->toposchema_name);
261 G_debug(2, "SQL: %s", stmt);
262 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
263 Vect__execute_pg(pg_info->conn, "ROLLBACK");
264 return 0;
265 }
266
267 /* 3) delete faces (areas/isles) */
268 snprintf(stmt, sizeof(stmt),
269 "DELETE FROM \"%s\".face WHERE "
270 "face_id != 0",
271 pg_info->toposchema_name);
272 G_debug(2, "SQL: %s", stmt);
273 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
274 Vect__execute_pg(pg_info->conn, "ROLLBACK");
275 return 0;
276 }
277 if (!pg_info->topo_geo_only) {
278 snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".%s",
279 pg_info->toposchema_name, TOPO_TABLE_AREA);
280 G_debug(2, "SQL: %s", stmt);
281 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
282 Vect__execute_pg(pg_info->conn, "ROLLBACK");
283 return 0;
284 }
285
286 snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".%s",
287 pg_info->toposchema_name, TOPO_TABLE_ISLE);
288 G_debug(2, "SQL: %s", stmt);
289 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
290 Vect__execute_pg(pg_info->conn, "ROLLBACK");
291 return 0;
292 }
293 }
294
295 /* 4) insert faces & update nodes (containing_face) based on
296 * GRASS topology */
297 G_message(_("Updating faces..."));
299 for (area = 1; area <= nareas; area++) {
300 G_percent(area, nareas, 5);
301 if (0 == Vect__insert_face_pg(Map, area)) {
302 Vect__execute_pg(pg_info->conn, "ROLLBACK");
303 return 0;
304 }
305
307 continue;
308
309 /* update centroids (node -> containing_face) */
310 Area = plus->Area[area];
311 if (Area->centroid < 1) {
312 G_debug(3, "Area %d without centroid, skipped", area);
313 continue;
314 }
315
316 Line = plus->Line[Area->centroid];
317 snprintf(stmt, sizeof(stmt),
318 "UPDATE \"%s\".node SET "
319 "containing_face = %d WHERE node_id = %d",
320 pg_info->toposchema_name, area, (int)Line->offset);
321 G_debug(2, "SQL: %s", stmt);
322
323 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
324 Vect__execute_pg(pg_info->conn, "ROLLBACK");
325 return 0;
326 }
327 }
328
329 /* 5) update edges (left and right face) */
330 G_message(_("Updating edges..."));
331 for (line = 1; line <= plus->n_lines; line++) {
332 G_percent(line, plus->n_lines, 5);
333 type = Vect_read_line(Map, NULL, NULL, line);
334 if (type != GV_BOUNDARY)
335 continue;
336
337 Line = Map->plus.Line[line];
338 if (!Line) {
339 G_warning(_("Inconsistency in topology detected. "
340 "Dead line found."));
341 return 0;
342 }
343
344 topo_b = (struct P_topo_b *)Line->topo;
345
346 for (s = 0; s < 2; s++) { /* for both sides */
347 face[s] = s == 0 ? topo_b->left : topo_b->right;
348 if (face[s] < 0) {
349 /* isle */
350 Isle = plus->Isle[abs(face[s])];
351 face[s] = Isle->area;
352 }
353 }
354 G_debug(3, "update edge %d: left_face = %d, right_face = %d",
355 (int)Line->offset, face[0], face[1]);
356
357 snprintf(stmt, sizeof(stmt),
358 "UPDATE \"%s\".edge_data SET "
359 "left_face = %d, right_face = %d "
360 "WHERE edge_id = %d",
361 pg_info->toposchema_name, face[0], face[1],
362 (int)Line->offset);
363
364 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
365 Vect__execute_pg(pg_info->conn, "ROLLBACK");
366 return 0;
367 }
368 }
369
370 /* write full area topo info to DB if requested */
371 if (!pg_info->topo_geo_only) {
372 write_areas(plus, pg_info);
373 }
374 } /* build >= GV_BUILD_AREAS */
375
377 /* insert isles as faces with negative face_id */
379 for (isle = 1; isle <= nisles; isle++) {
380 Isle = plus->Isle[isle];
382 }
383
384 /* write full isles topo info to DB if requested */
385 if (!pg_info->topo_geo_only) {
386 write_isles(plus, pg_info);
387 }
388 } /* build >= GV_BUILD_ISLES */
389
390 if (pg_info->feature_type == SF_POLYGON) {
391 int centroid;
392
393 struct P_line *Line;
394
395 G_message(_("Updating TopoGeometry data..."));
396 for (area = 1; area <= plus->n_areas; area++) {
397 G_percent(area, plus->n_areas, 5);
398 centroid = Vect_get_area_centroid(Map, area);
399 if (centroid < 1)
400 continue;
401
402 Line = plus->Line[centroid];
403 if (!Line)
404 continue;
405
406 /* update topogeometry object: centroid -> face */
407 if (build_topogeom_stmt(pg_info, GV_CENTROID, area,
408 (int)Line->offset, stmt) &&
409 Vect__execute_pg(pg_info->conn, stmt) == -1) {
410 Vect__execute_pg(pg_info->conn, "ROLLBACK");
411 return 0;
412 }
413
415 }
416 }
417
418 if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
419 return 0;
420
421 /* check if we want to create simple features from topogeometry
422 data */
423 def_file = getenv("GRASS_VECTOR_PGFILE");
424
425 if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
426 FILE *fp;
427 const char *p;
428
429 struct Key_Value *key_val;
430
431 fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
432 if (!fp) {
433 G_fatal_error(_("Unable to open PG file"));
434 }
436 fclose(fp);
437
438 /* build simple features from topogeometry data */
439 p = G_find_key_value("simple_feature", key_val);
440 if (p && G_strcasecmp(p, "yes") == 0) {
441 if (build > GV_BUILD_BASE)
442 Map->level = LEVEL_2; /* force level to avoid errors */
443
444 if (create_simple_feature_from_topo(Map) != 0)
445 return 0;
446 }
447
449 }
450
451 return 1;
452}
453
454/*!
455 \brief Build UPDATE statement for topo geometry element stored in
456 feature table
457
458 \param pg_info so pointer to Format_info_pg
459 \param type feature type (GV_POINT, ...)
460 \param topo_id topology element id
461 \param fid feature id
462 \param[out] stmt string buffer
463
464 \return 1 on success
465 \return 0 on failure
466 */
467int build_topogeom_stmt(const struct Format_info_pg *pg_info, int type,
468 int topo_id, int fid, char *stmt)
469{
470 int topogeom_type;
471
472 switch (type) {
473 case GV_POINT:
474 topogeom_type = 1;
475 break;
476 case GV_LINE:
477 case GV_BOUNDARY:
478 topogeom_type = 2;
479 break;
480 case GV_CENTROID:
481 topogeom_type = 3;
482 break;
483 default:
484 G_warning(_("Unsupported topo geometry type %d"), type);
485 return 0;
486 }
487
488 snprintf(stmt, DB_SQL_MAX,
489 "UPDATE \"%s\".\"%s\" SET %s = "
490 "'(%d, 1, %d, %d)'::topology.TopoGeometry "
491 "WHERE (%s).id = %d",
492 pg_info->schema_name, pg_info->table_name,
493 pg_info->topogeom_column, pg_info->toposchema_id, topo_id,
494 topogeom_type, pg_info->topogeom_column, fid);
495
496 return 1;
497}
498
499/*!
500 \brief Store map bounding box in DB head table
501
502 \param pg_info pointer to Format_info_pg struct
503 \param box pointer to bounding box
504
505 \return 1 on success
506 \return 0 on failure
507 */
508int save_map_bbox(const struct Format_info_pg *pg_info,
509 const struct bound_box *box)
510{
511 char stmt[DB_SQL_MAX];
512
513 /* create if not exists */
514 if (create_topo_grass(pg_info) == -1) {
515 G_warning(_("Unable to create <%s.%s>"), TOPO_SCHEMA, TOPO_TABLE);
516 return 0;
517 }
518
519 /* update bbox */
520 if (has_topo_grass(pg_info)) {
521 /* -> update */
522 snprintf(
523 stmt, sizeof(stmt),
524 "UPDATE \"%s\".\"%s\" SET %s = "
525 "'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d WHERE %s "
526 "= %d",
527 TOPO_SCHEMA, TOPO_TABLE, TOPO_BBOX, box->W, box->S, box->B, box->E,
528 box->N, box->T, TOPO_ID, pg_info->toposchema_id);
529 }
530 else {
531 /* -> insert */
532 snprintf(
533 stmt, sizeof(stmt),
534 "INSERT INTO \"%s\".\"%s\" (%s, %s) "
535 "VALUES(%d, 'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d)",
537 box->W, box->S, box->B, box->E, box->N, box->T);
538 }
539
540 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
541 return -1;
542 }
543
544 return 1;
545}
546
547/*!
548 \brief Creates 'topology.grass' table if not exists
549
550 \return 0 table already exists
551 \return 1 table successfully added
552 \return -1 on error
553 */
554int create_topo_grass(const struct Format_info_pg *pg_info)
555{
556 char stmt[DB_SQL_MAX];
557
558 PGresult *result;
559
560 /* check if table exists */
561 snprintf(stmt, sizeof(stmt),
562 "SELECT COUNT(*) FROM information_schema.tables "
563 "WHERE table_schema = '%s' AND table_name = '%s'",
565 result = PQexec(pg_info->conn, stmt);
566 if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
567 PQclear(result);
568 return -1;
569 }
570
571 if (atoi(PQgetvalue(result, 0, 0)) == 1) {
572 /* table already exists */
573 PQclear(result);
574 return 1;
575 }
576 PQclear(result);
577
578 G_debug(1, "<%s.%s> created", TOPO_SCHEMA, TOPO_TABLE);
579
580 /* create table */
581 snprintf(stmt, sizeof(stmt),
582 "CREATE TABLE \"%s\".\"%s\" (%s INTEGER, %s box3d)", TOPO_SCHEMA,
584 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
585 return -1;
586 }
587 /* add primary key */
588 snprintf(stmt, sizeof(stmt),
589 "ALTER TABLE \"%s\".\"%s\" ADD PRIMARY KEY (%s)", TOPO_SCHEMA,
591 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
592 return -1;
593 }
594
595 /* add constraint */
596 snprintf(
597 stmt, sizeof(stmt),
598 "ALTER TABLE \"%s\".\"%s\" ADD CONSTRAINT \"%s_%s_fkey\" "
599 "FOREIGN KEY (%s) REFERENCES topology.topology(id) ON DELETE CASCADE",
601 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
602 return -1;
603 }
604
605 return 1;
606}
607
608/*!
609 \brief Check if 'topology_id' exists in 'topology.grass'
610
611 \param pg_info pointer to Format_info_pg struct
612
613 \return TRUE if exists
614 \return FALSE otherwise
615 \return -1 on error
616 */
617int has_topo_grass(const struct Format_info_pg *pg_info)
618{
619 int has_topo;
620 char stmt[DB_SQL_MAX];
621
622 PGresult *result;
623
624 snprintf(stmt, sizeof(stmt),
625 "SELECT COUNT(*) FROM \"%s\".\"%s\" "
626 "WHERE %s = %d",
627 TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, pg_info->toposchema_id);
628 result = PQexec(pg_info->conn, stmt);
629 if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
630 PQclear(result);
631 return -1;
632 }
633
634 has_topo = FALSE;
635 if (atoi(PQgetvalue(result, 0, 0)) == 1) {
636 /* table already exists */
637 has_topo = TRUE;
638 }
639 PQclear(result);
640
641 return has_topo;
642}
643
644/*!
645 \brief Insert node into 'node_grass' table
646
647 Writes (see P_node struct):
648 - lines
649 - angles
650
651 Already stored in Topo-Geo:
652 - x,y,z (geom)
653
654 \param plus pointer to Plus_head struct
655 \param pg_info pointer to Format_info_pg struct
656
657 \return 0 on success
658 \return -1 on error
659 */
660int write_nodes(const struct Plus_head *plus,
661 const struct Format_info_pg *pg_info)
662{
663 int i, node_id;
665 char *stmt_lines, *stmt_angles, *stmt;
666
667 const struct P_node *Node;
668 const struct Format_info_offset *offset;
669
670 offset = &(pg_info->offset);
671
672 if (offset->array_num < 1) /* nothing to write */
673 return 0;
674
675 if (plus->n_nodes != offset->array_num) {
676 G_warning(_("Unable to write nodes, offset array mismatch"));
677 return -1;
678 }
679
680 stmt_size = 2 * DB_SQL_MAX + 512;
681 stmt = (char *)G_malloc(stmt_size);
682
684 for (i = 1; i <= plus->n_nodes; i++) {
685 Node = plus->Node[i];
686 if (!Node)
687 continue; /* should not happen */
688
689 node_id = offset->array[i - 1];
690
691 /* 'lines' array */
692 build_stmt_id(Node->lines, Node->n_lines, TRUE, plus, &stmt_lines,
694 /* 'angle' array */
695 build_stmt_id(Node->angles, Node->n_lines, FALSE, NULL, &stmt_angles,
697
698 /* build SQL statement to add new node into 'node_grass' */
701 stmt = (char *)G_realloc(stmt, stmt_size);
702 }
703 snprintf(stmt, stmt_size,
704 "INSERT INTO \"%s\".%s VALUES ("
705 "%d, '{%s}', '{%s}')",
706 pg_info->toposchema_name, TOPO_TABLE_NODE, node_id, stmt_lines,
708 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
709 G_warning(_("Unable to write nodes"));
710 return -1;
711 }
712 }
713
716 G_free(stmt);
717
718 return 0;
719}
720
721/*!
722 \brief Insert lines into 'line_grass' table
723
724 Writes (see P_line struct) - only for boundaries:
725 - left, right area
726
727 Already stored in Topo-Geo:
728 - edge_id, left_face, right_face
729
730 \param plus pointer to Plus_head struct
731 \param pg_info pointer to Format_info_pg struct
732
733 \return 0 on success
734 \return -1 on error
735 */
736int write_lines(const struct Plus_head *plus,
737 const struct Format_info_pg *pg_info)
738{
739 int i, row, offset;
740 char stmt[DB_SQL_MAX];
741
742 const struct P_line *Line;
743 const struct P_topo_b *topo;
744
745 PGresult *res;
746
747 snprintf(stmt, sizeof(stmt),
748 "SELECT edge_id FROM \"%s\".edge_data WHERE "
749 "left_face != 0 OR right_face != 0 ORDER BY edge_id",
750 pg_info->toposchema_name);
751 G_debug(2, "SQL: %s", stmt);
752 res = PQexec(pg_info->conn, stmt);
753 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
754 (PQntuples(res) > 0 && PQntuples(res) != plus->n_blines)) {
755 G_warning(_("Inconsistency in topology: number of "
756 "boundaries %d (should be %d)"),
757 PQntuples(res), plus->n_blines);
758 if (res)
759 PQclear(res);
760 return -1;
761 }
762
763 for (row = 0, i = 1; i <= plus->n_lines; i++) {
764 Line = plus->Line[i];
765 if (!Line || Line->type != GV_BOUNDARY)
766 continue;
767
768 if (Line->offset == 0L)
769 offset = atoi(PQgetvalue(res, row++, 0));
770 else
771 offset = (int)Line->offset;
772
773 topo = (struct P_topo_b *)Line->topo;
774 snprintf(stmt, sizeof(stmt),
775 "INSERT INTO \"%s\".%s VALUES ("
776 "%d, %d, %d)",
777 pg_info->toposchema_name, TOPO_TABLE_LINE, offset, topo->left,
778 topo->right);
779 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
780 G_warning(_("Unable to write lines"));
781 return -1;
782 }
783 }
784
785 return 0;
786}
787
788/*!
789 \brief Insert area into 'area_grass' table
790
791 Writes (see P_area struct):
792 - lines
793 - centroid
794 - isles
795
796 \param plus pointer to Plus_head struct
797 \param pg_info pointer to Format_info_pg struct
798
799 \return 0 on success
800 \return -1 on error
801 */
802int write_areas(const struct Plus_head *plus,
803 const struct Format_info_pg *pg_info)
804{
805 int area, centroid;
807 char *stmt_lines, *stmt_isles, *stmt;
808
809 const struct P_line *Line;
810 const struct P_area *Area;
811
812 stmt_size = 2 * DB_SQL_MAX + 512;
813 stmt = (char *)G_malloc(stmt_size);
814
816 for (area = 1; area <= plus->n_areas; area++) {
817 Area = plus->Area[area];
818 if (!Area) {
819 G_debug(3, "Area %d skipped (dead)", area);
820 continue; /* should not happen */
821 }
822
823 /* 'lines' array */
824 build_stmt_id(Area->lines, Area->n_lines, TRUE, NULL, &stmt_lines,
826 /* 'isles' array */
827 build_stmt_id(Area->isles, Area->n_isles, TRUE, NULL, &stmt_isles,
829
830 if (Area->centroid != 0) {
831 Line = plus->Line[Area->centroid];
832 if (!Line) {
833 G_warning(_("Topology for centroid %d not available. Area %d "
834 "skipped"),
835 Area->centroid, area);
836 continue;
837 }
838 centroid = (int)Line->offset;
839 }
840 else {
841 centroid = 0;
842 }
843
844 /* build SQL statement to add new node into 'node_grass' */
847 stmt = (char *)G_realloc(stmt, stmt_size);
848 }
849 snprintf(stmt, stmt_size,
850 "INSERT INTO \"%s\".%s VALUES ("
851 "%d, '{%s}', %d, '{%s}')",
852 pg_info->toposchema_name, TOPO_TABLE_AREA, area, stmt_lines,
854 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
855 return -1;
856 }
857 }
858
861 G_free(stmt);
862
863 return 0;
864}
865
866/*!
867 \brief Insert isle into 'isle_grass' table
868
869 Writes (see P_isle struct):
870 - lines
871 - area
872
873 \param plus pointer to Plus_head struct
874 \param pg_info pointer to Format_info_pg struct
875
876 \return 0 on success
877 \return -1 on error
878 */
879int write_isles(const struct Plus_head *plus,
880 const struct Format_info_pg *pg_info)
881{
882 int isle;
884 char *stmt_lines, *stmt;
885
886 const struct P_isle *Isle;
887
888 stmt_size = DB_SQL_MAX + 512;
889 stmt = (char *)G_malloc(stmt_size);
890
892 for (isle = 1; isle <= plus->n_isles; isle++) {
893 Isle = plus->Isle[isle];
894 if (!Isle)
895 continue; /* should not happen */
896
897 /* 'lines' array */
898 build_stmt_id(Isle->lines, Isle->n_lines, TRUE, NULL, &stmt_lines,
900
901 /* build SQL statement to add new node into 'node_grass' */
902 if (stmt_lines_size + 512 > stmt_size) {
904 stmt = (char *)G_realloc(stmt, stmt_size);
905 }
906 snprintf(stmt, stmt_size,
907 "INSERT INTO \"%s\".%s VALUES ("
908 "%d, '{%s}', %d)",
909 pg_info->toposchema_name, TOPO_TABLE_ISLE, isle, stmt_lines,
910 Isle->area);
911 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
912 return -1;
913 }
914 }
915
917 G_free(stmt);
918
919 return 0;
920}
921
922/*!
923 \brief Create PG-like array for int/float array
924
925 \param array array of items
926 \param nitems number of items in the array
927 \param is_int TRUE for array of integers otherwise floats
928 \param plus pointer to Plus_head struct
929 \param[in,out] output buffer (re-used)
930 \param[in,out] buffer size
931 */
932void build_stmt_id(const void *array, int nitems, int is_int,
933 const struct Plus_head *plus, char **stmt, size_t *stmt_size)
934{
935 int i, ivalue;
936 int *iarray;
937 float *farray;
938
939 size_t stmt_id_size;
940 char *stmt_id, buf_id[128];
941
942 struct P_line *Line;
943
944 if (is_int)
945 iarray = (int *)array;
946 else
947 farray = (float *)array;
948
949 if (!(*stmt)) {
951 stmt_id = (char *)G_malloc(stmt_id_size);
952 }
953 else {
955 stmt_id = *stmt;
956 }
957
958 /* reset array */
959 stmt_id[0] = '\0';
960
961 for (i = 0; i < nitems; i++) {
962 /* realloc array if needed */
963 if (strlen(stmt_id) + 100 > stmt_id_size) {
966 }
967
968 if (is_int) {
969 if (plus) {
970 Line = plus->Line[abs(iarray[i])];
971 ivalue = (int)Line->offset;
972 if (iarray[i] < 0)
973 ivalue *= -1;
974 }
975 else {
976 ivalue = iarray[i];
977 }
978 snprintf(buf_id, sizeof(buf_id), "%d", ivalue);
979 }
980 else {
981 snprintf(buf_id, sizeof(buf_id), "%f", farray[i]);
982 }
983
984 if (i > 0)
985 strcat(stmt_id, ",");
986 strcat(stmt_id, buf_id);
987 }
988
989 *stmt = stmt_id;
991}
992
993/*!
994 \brief Clean-up GRASS Topology tables
995
996 \param pg_info pointer to Format_info_pg pg_info
997
998 \return 0 on success
999 \return -1 on error
1000 */
1002{
1003 char stmt[DB_SQL_MAX];
1004
1005 snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".\"%s\"",
1006 pg_info->toposchema_name, TOPO_TABLE_NODE);
1007 if (-1 == Vect__execute_pg(pg_info->conn, stmt))
1008 return -1;
1009
1010 snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".\"%s\"",
1011 pg_info->toposchema_name, TOPO_TABLE_LINE);
1012 if (-1 == Vect__execute_pg(pg_info->conn, stmt))
1013 return -1;
1014
1015 snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".\"%s\"",
1016 pg_info->toposchema_name, TOPO_TABLE_AREA);
1017 if (-1 == Vect__execute_pg(pg_info->conn, stmt))
1018 return -1;
1019
1020 snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".\"%s\"",
1021 pg_info->toposchema_name, TOPO_TABLE_ISLE);
1022 if (-1 == Vect__execute_pg(pg_info->conn, stmt))
1023 return -1;
1024
1025 return 0;
1026}
1027
1028/*!
1029 \brief Create simple features geometry from topogeometry data
1030
1031 \param Map pointer to Map_info struct
1032
1033 \return 0 on success
1034 \return -1 on error
1035 */
1036int create_simple_feature_from_topo(struct Map_info *Map)
1037{
1038 char stmt[DB_SQL_MAX];
1039
1040 struct Format_info_pg *pg_info;
1041
1042 pg_info = &(Map->fInfo.pg);
1043
1044 G_debug(1, "build_simple_feature_from_topo(): %d", pg_info->feature_type);
1045
1046 G_message(_("Create simple features topology from topogeometry data..."));
1047 Vect__execute_pg(pg_info->conn, "BEGIN");
1048 if (pg_info->feature_type == SF_POINT ||
1049 pg_info->feature_type == SF_LINESTRING) {
1050 snprintf(stmt, sizeof(stmt),
1051 "UPDATE \"%s\".\"%s\" SET %s = (SELECT geom FROM \"%s\".node "
1052 "WHERE node_id = (%s).id)",
1053 pg_info->schema_name, pg_info->table_name,
1054 pg_info->geom_column, pg_info->toposchema_name,
1055 pg_info->topogeom_column);
1056
1057 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1058 Vect__execute_pg(pg_info->conn, "ROLLBACK");
1059 return -1;
1060 }
1061 }
1062 else if (pg_info->feature_type == SF_POLYGON) {
1064 }
1065 else {
1066 G_warning(_("Unable to build simple features from topogeometry data. "
1067 "Unsupported type %d."),
1068 pg_info->feature_type);
1069 }
1070
1071 Vect__execute_pg(pg_info->conn, "COMMIT");
1072
1073 return 0;
1074}
1075#endif
#define NOPG_UNUSED
Definition build_pg.c:42
int Vect__clean_grass_db_topo(struct Format_info_pg *pg_info)
Clean-up GRASS Topology tables.
Definition build_pg.c:1001
int Vect_build_pg(struct Map_info *Map, int build)
Build topology for PostGIS layer.
Definition build_pg.c:63
#define NULL
Definition ccmath.h:32
#define DB_SQL_MAX
Definition dbmi.h:142
void G_percent(long, long, int)
Print percent complete messages.
Definition percent.c:61
void G_zero(void *, int)
Zero out a buffer, buf, of length i.
Definition gis/zero.c:23
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
#define G_realloc(p, n)
Definition defs/gis.h:141
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:139
FILE * G_fopen_old(const char *, const char *, const char *)
Open a database file for reading.
Definition gis/open.c:253
const char * G_find_file2(const char *, const char *, const char *)
Searches for a file from the mapset search list or in a specified mapset. (look but don't touch)
Definition find_file.c:234
void G_free_key_value(struct Key_Value *)
Free allocated Key_Value structure.
Definition key_value1.c:104
const char * G_find_key_value(const char *, const struct Key_Value *)
Find given key (case sensitive)
Definition key_value1.c:85
struct Key_Value * G_fread_key_value(FILE *)
Read key/values pairs from file.
Definition key_value2.c:49
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition strings.c:47
void G_message(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
const char * G_mapset(void)
Get current mapset name.
Definition gis/mapset.c:33
plus_t Vect_get_num_islands(struct Map_info *)
Get number of islands in vector map.
Definition level_two.c:135
plus_t Vect_get_num_areas(struct Map_info *)
Get number of areas in vector map.
Definition level_two.c:87
void Vect__build_downgrade(struct Map_info *, int)
Downgrade build level (for internal use only)
Definition build.c:767
int Vect_build_nat(struct Map_info *, int)
Build topology.
Definition build_nat.c:34
int Vect__build_sfa(struct Map_info *, int)
Build pseudo-topology (for simple features) - internal use only.
Definition build_sfa.c:696
const char * Vect_get_finfo_format_info(struct Map_info *)
Get format info as string (relevant only for non-native formats)
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
const char * Vect_get_finfo_geometry_type(struct Map_info *)
Get geometry type as string (relevant only for non-native formats)
int Vect_build_partial(struct Map_info *, int)
Build partial topology for vector map.
Definition build.c:857
int Vect_get_area_centroid(struct Map_info *, int)
Returns centroid id for given area.
#define GV_CENTROID
@ SF_POLYGON
@ SF_LINESTRING
@ SF_GEOMETRY
@ SF_POINT
#define GV_BUILD_NONE
Topology levels - nothing to build.
#define GV_LINE
#define GV_POINT
Feature types used in memory on run time (may change)
#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 LEVEL_2
Vector level - with 2D topology.
#define GV_MODE_RW
Read-write vector map open mode.
void dig_free_plus(struct Plus_head *)
Free Plus structure.
Definition plus.c:173
int dig_init_plus(struct Plus_head *)
Initialize Plus_head structure.
Definition plus.c:30
#define TRUE
Definition gis.h:78
#define FALSE
Definition gis.h:82
#define _(str)
Definition glocale.h:10
int Vect__load_map_nodes_pg(struct Map_info *Map, int geom_only)
Read nodes from DB.
Definition open_pg.c:1492
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
Definition read_pg.c:1565
Data structure used for building pseudo-topology.
int * array
Offset list.
int array_num
Number of items in offset list.
Non-native format info (PostGIS)
Vector map info.
Area (topology) info.
plus_t n_isles
Number of islands inside.
plus_t * isles
1st generation interior islands
plus_t n_lines
Number of boundary lines.
plus_t * lines
List of boundary lines.
plus_t centroid
Number of first centroid within area.
Isle (topology) info.
plus_t * lines
List of boundary lines.
plus_t n_lines
Number of boundary lines.
plus_t area
Area it exists w/in, if any.
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)
float * angles
List of angles of connected lines.
plus_t * lines
List of connected lines.
Boundary topology.
plus_t left
Area number to the left, negative for isle.
plus_t right
Area number to the right, negative for isle.
Basic topology-related info.
struct P_line ** Line
Array of vector geometries.
plus_t n_lines
Current number of lines.
int Spidx_new
Build new spatial index.
plus_t n_nodes
Current number of topological features derived from vector geometries.
plus_t n_blines
Current number of boundaries.
struct P_area ** Area
Array of areas.
int update_cidx
Update category index if vector is modified.
plus_t n_isles
Current number of isles.
struct bound_box box
Bounding box of features.
struct P_isle ** Isle
Array of isles.
struct P_node ** Node
Array of nodes.
plus_t n_areas
Current number of areas.
int built
Highest level of topology currently available.
Bounding box.
Definition dig_structs.h:62
double W
West.
Definition dig_structs.h:78
double T
Top.
Definition dig_structs.h:82
double S
South.
Definition dig_structs.h:70
double N
North.
Definition dig_structs.h:66
double E
East.
Definition dig_structs.h:74
double B
Bottom.
Definition dig_structs.h:86
void Vect__free_offset(struct Format_info_offset *offset)
void Vect__free_cache(struct Format_info_cache *cache)
int Vect__copy_areas(struct Map_info *In, int field, struct Map_info *Out)
Copy areas as polygons (OGR/PostGIS simple features access only)
int Vect__define_topo_relation(const struct Format_info_pg *pg_info, int topo_id, int element_id)
Definition write_pg.c:2430
int Vect__insert_face_pg(struct Map_info *Map, int area)
Insert new face to the 'face' table (topo only)
Definition write_pg.c:2578