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