GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-eb16a2cfc9
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 
28 static int build_topo(struct Map_info *, int);
29 static int build_topogeom_stmt(const struct Format_info_pg *, int, int, int,
30  char *);
31 static int save_map_bbox(const struct Format_info_pg *,
32  const struct bound_box *);
33 static int create_topo_grass(const struct Format_info_pg *);
34 static int has_topo_grass(const struct Format_info_pg *);
35 static int write_nodes(const struct Plus_head *, const struct Format_info_pg *);
36 static int write_lines(const struct Plus_head *, const struct Format_info_pg *);
37 static int write_areas(const struct Plus_head *, const struct Format_info_pg *);
38 static int write_isles(const struct Plus_head *, const struct Format_info_pg *);
39 static void build_stmt_id(const void *, int, int, const struct Plus_head *,
40  char **, size_t *);
41 static 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  */
63 int Vect_build_pg(struct Map_info *Map NOPG_UNUSED, int build NOPG_UNUSED)
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 &&
90  build > GV_BUILD_BASE) {
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  */
143 int 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 */
165  Vect__build_downgrade(Map, build);
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)
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 */
216  Vect_build_nat(Map, build);
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..."));
298  nareas = Vect_get_num_areas(Map);
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 
306  if (build < GV_BUILD_CENTROIDS)
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 
376  if (build >= GV_BUILD_ATTACH_ISLES) {
377  /* insert isles as faces with negative face_id */
378  nisles = Vect_get_num_islands(Map);
379  for (isle = 1; isle <= nisles; isle++) {
380  Isle = plus->Isle[isle];
381  Vect__insert_face_pg(Map, -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 
414  Vect__define_topo_relation(pg_info, area, area);
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  }
435  key_val = G_fread_key_value(fp);
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 
448  G_free_key_value(key_val);
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  */
467 int 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  */
508 int 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)",
536  TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX, pg_info->toposchema_id,
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  */
554 int 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'",
564  TOPO_SCHEMA, TOPO_TABLE);
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,
583  TOPO_TABLE, TOPO_ID, TOPO_BBOX);
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,
590  TOPO_TABLE, TOPO_ID);
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",
600  TOPO_SCHEMA, TOPO_TABLE, TOPO_TABLE, TOPO_ID, TOPO_ID);
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  */
617 int 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  */
660 int write_nodes(const struct Plus_head *plus,
661  const struct Format_info_pg *pg_info)
662 {
663  int i, node_id;
664  size_t stmt_lines_size, stmt_angles_size, stmt_size;
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 
683  stmt_lines = stmt_angles = NULL;
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,
693  &stmt_lines_size);
694  /* 'angle' array */
695  build_stmt_id(Node->angles, Node->n_lines, FALSE, NULL, &stmt_angles,
696  &stmt_angles_size);
697 
698  /* build SQL statement to add new node into 'node_grass' */
699  if (stmt_lines_size + stmt_angles_size + 512 > stmt_size) {
700  stmt_size = stmt_lines_size + stmt_angles_size + 512;
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,
707  stmt_angles);
708  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
709  G_warning(_("Unable to write nodes"));
710  return -1;
711  }
712  }
713 
714  G_free(stmt_lines);
715  G_free(stmt_angles);
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  */
736 int 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  */
802 int write_areas(const struct Plus_head *plus,
803  const struct Format_info_pg *pg_info)
804 {
805  int area, centroid;
806  size_t stmt_lines_size, stmt_isles_size, stmt_size;
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 
815  stmt_lines = stmt_isles = NULL;
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,
825  &stmt_lines_size);
826  /* 'isles' array */
827  build_stmt_id(Area->isles, Area->n_isles, TRUE, NULL, &stmt_isles,
828  &stmt_isles_size);
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' */
845  if (stmt_lines_size + stmt_isles_size + 512 > stmt_size) {
846  stmt_size = stmt_lines_size + stmt_isles_size + 512;
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,
853  centroid, stmt_isles);
854  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
855  return -1;
856  }
857  }
858 
859  G_free(stmt_lines);
860  G_free(stmt_isles);
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  */
879 int write_isles(const struct Plus_head *plus,
880  const struct Format_info_pg *pg_info)
881 {
882  int isle;
883  size_t stmt_lines_size, stmt_size;
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 
891  stmt_lines = NULL;
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,
899  &stmt_lines_size);
900 
901  /* build SQL statement to add new node into 'node_grass' */
902  if (stmt_lines_size + 512 > stmt_size) {
903  stmt_size = stmt_lines_size + 512;
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 
916  G_free(stmt_lines);
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  */
932 void 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)) {
950  stmt_id_size = DB_SQL_MAX;
951  stmt_id = (char *)G_malloc(stmt_id_size);
952  }
953  else {
954  stmt_id_size = *stmt_size;
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) {
964  stmt_id_size = strlen(stmt_id) + DB_SQL_MAX;
965  stmt_id = (char *)G_realloc(stmt_id, 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;
990  *stmt_size = stmt_id_size;
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  */
1036 int 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) {
1063  Vect__copy_areas(Map, 1, Map);
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 NOPG_UNUSED, int build NOPG_UNUSED)
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
FILE * G_fopen_old(const char *, const char *, const char *)
Open a database file for reading.
Definition: gis/open.c:251
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:150
#define G_realloc(p, n)
Definition: defs/gis.h:96
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 void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
const char * G_find_key_value(const char *, const struct Key_Value *)
Find given key (case sensitive)
Definition: key_value1.c:85
#define G_malloc(n)
Definition: defs/gis.h:94
void G_free_key_value(struct Key_Value *)
Free allocated Key_Value structure.
Definition: key_value1.c:104
const char * G_mapset(void)
Get current mapset name.
Definition: gis/mapset.c:33
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
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:774
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:700
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_format_info(struct Map_info *)
Get format info as string (relevant only for non-native formats)
Definition: header_finfo.c:110
int Vect_build_partial(struct Map_info *, int)
Build partial topology for vector map.
Definition: build.c:864
const char * Vect_get_finfo_geometry_type(struct Map_info *)
Get geometry type as string (relevant only for non-native formats)
Definition: header_finfo.c:145
int Vect_get_area_centroid(struct Map_info *, int)
Returns centroid id for given area.
#define GV_CENTROID
Definition: dig_defines.h:186
@ SF_POLYGON
Definition: dig_defines.h:244
@ SF_LINESTRING
Definition: dig_defines.h:242
@ SF_GEOMETRY
Definition: dig_defines.h:240
@ SF_POINT
Definition: dig_defines.h:241
#define GV_BUILD_NONE
Topology levels - nothing to build.
Definition: dig_defines.h:123
#define GV_LINE
Definition: dig_defines.h:184
#define GV_POINT
Feature types used in memory on run time (may change)
Definition: dig_defines.h:183
#define GV_BOUNDARY
Definition: dig_defines.h:185
#define GV_BUILD_ATTACH_ISLES
Topology levels - attach islands to areas.
Definition: dig_defines.h:129
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
Definition: dig_defines.h:125
#define GV_BUILD_AREAS
Topology levels - build areas.
Definition: dig_defines.h:127
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
Definition: dig_defines.h:131
#define LEVEL_2
Vector level - with 2D topology.
Definition: dig_defines.h:118
#define GV_MODE_RW
Read-write vector map open mode.
Definition: dig_defines.h:108
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
if(!(yy_init))
Definition: sqlp.yy.c:775
int ctype
Cache type.
Definition: dig_structs.h:496
int lines_num
Number of lines which forms current feature.
Definition: dig_structs.h:478
Data structure used for building pseudo-topology.
Definition: dig_structs.h:388
int * array
Offset list.
Definition: dig_structs.h:436
int array_num
Number of items in offset list.
Definition: dig_structs.h:440
Non-native format info (PostGIS)
Definition: dig_structs.h:590
char * db_name
Database name (derived from conninfo)
Definition: dig_structs.h:598
char * fid_column
FID column.
Definition: dig_structs.h:614
char * schema_name
Schema name.
Definition: dig_structs.h:602
PGconn * conn
PGconn object (generated by PQconnectdb)
Definition: dig_structs.h:650
char * toposchema_name
Topology schema name and id.
Definition: dig_structs.h:686
struct Format_info_cache cache
Lines cache for reading feature.
Definition: dig_structs.h:670
int inTransaction
Start/Finish transaction.
Definition: dig_structs.h:645
char * topogeom_column
TopoGeometry column (feature table)
Definition: dig_structs.h:682
struct Format_info_offset offset
Offset list used for building pseudo-topology (simple features access)
Definition: dig_structs.h:676
char * geom_column
Geometry column (simple feature access)
Definition: dig_structs.h:618
int topo_geo_only
Topology format.
Definition: dig_structs.h:694
char * table_name
Table name.
Definition: dig_structs.h:606
SF_FeatureType feature_type
Feature type (simple feature access)
Definition: dig_structs.h:622
struct Format_info_pg pg
PostGIS info.
Definition: dig_structs.h:712
Definition: gis.h:534
Vector map info.
Definition: dig_structs.h:1243
int level
Topology level.
Definition: dig_structs.h:1297
struct Format_info fInfo
Format info for non-native formats.
Definition: dig_structs.h:1400
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1270
Area (topology) info.
Definition: dig_structs.h:1583
plus_t n_isles
Number of islands inside.
Definition: dig_structs.h:1609
plus_t * isles
1st generation interior islands
Definition: dig_structs.h:1617
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1587
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1598
plus_t centroid
Number of first centroid within area.
Definition: dig_structs.h:1605
Isle (topology) info.
Definition: dig_structs.h:1623
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1638
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1627
plus_t area
Area it exists w/in, if any.
Definition: dig_structs.h:1645
Vector geometry.
Definition: dig_structs.h:1553
char type
Line type.
Definition: dig_structs.h:1564
off_t offset
Offset in coor file for line.
Definition: dig_structs.h:1571
void * topo
Topology info.
Definition: dig_structs.h:1577
Topological feature - node.
Definition: dig_structs.h:1433
plus_t n_lines
Number of attached lines (size of lines, angle)
Definition: dig_structs.h:1456
float * angles
List of angles of connected lines.
Definition: dig_structs.h:1472
plus_t * lines
List of connected lines.
Definition: dig_structs.h:1463
Boundary topology.
Definition: dig_structs.h:1492
plus_t left
Area number to the left, negative for isle.
Definition: dig_structs.h:1504
plus_t right
Area number to the right, negative for isle.
Definition: dig_structs.h:1508
Basic topology-related info.
Definition: dig_structs.h:769
struct P_line ** Line
Array of vector geometries.
Definition: dig_structs.h:871
plus_t n_lines
Current number of lines.
Definition: dig_structs.h:931
int Spidx_new
Build new spatial index.
Definition: dig_structs.h:1043
plus_t n_nodes
Current number of topological features derived from vector geometries.
Definition: dig_structs.h:923
plus_t n_blines
Current number of boundaries.
Definition: dig_structs.h:894
struct P_area ** Area
Array of areas.
Definition: dig_structs.h:875
int update_cidx
Update category index if vector is modified.
Definition: dig_structs.h:1120
plus_t n_isles
Current number of isles.
Definition: dig_structs.h:939
struct bound_box box
Bounding box of features.
Definition: dig_structs.h:861
struct P_isle ** Isle
Array of isles.
Definition: dig_structs.h:879
struct P_node ** Node
Array of nodes.
Definition: dig_structs.h:867
plus_t n_areas
Current number of areas.
Definition: dig_structs.h:935
int built
Highest level of topology currently available.
Definition: dig_structs.h:857
Bounding box.
Definition: dig_structs.h:64
double W
West.
Definition: dig_structs.h:80
double T
Top.
Definition: dig_structs.h:84
double S
South.
Definition: dig_structs.h:72
double N
North.
Definition: dig_structs.h:68
double E
East.
Definition: dig_structs.h:76
double B
Bottom.
Definition: dig_structs.h:88
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