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