GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-a277d8547c
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
write_pg.c
Go to the documentation of this file.
1 /*!
2  \file lib/vector/Vlib/write_pg.c
3 
4  \brief Vector library - write vector feature (PostGIS format)
5 
6  Higher level functions for reading/writing/manipulating vectors.
7 
8  Write subroutine inspired by OGR PostgreSQL driver.
9 
10  \todo PostGIS version of V2__delete_area_cats_from_cidx_nat()
11  \todo function to delete corresponding entry in fidx
12  \todo PostGIS version of V2__add_area_cats_to_cidx_nat
13 
14  (C) 2012-2014 by Martin Landa, and the GRASS Development Team
15 
16  This program is free software under the GNU General Public License
17  (>=v2). Read the file COPYING that comes with GRASS for details.
18 
19  \author Martin Landa <landa.martin gmail.com>
20  */
21 
22 #include <inttypes.h>
23 #include <string.h>
24 
25 #include <grass/vector.h>
26 #include <grass/glocale.h>
27 
28 #include "local_proto.h"
29 
30 #ifdef HAVE_POSTGRES
31 #include "pg_local_proto.h"
32 
33 #define WKBSRIDFLAG 0x20000000
34 
35 #define TOPOGEOM_COLUMN "topo"
36 
37 /*! Use SQL statements from PostGIS Topology extension (this options
38  is quite slow. By default are used simple SQL statements (INSERT, UPDATE)
39  */
40 #define USE_TOPO_STMT 0
41 
42 static int create_table(struct Format_info_pg *);
43 static int check_schema(const struct Format_info_pg *);
44 static int create_topo_schema(struct Format_info_pg *, int);
45 static int create_pg_layer(struct Map_info *, int);
46 static char *get_sftype(SF_FeatureType);
47 static off_t write_line_sf(struct Map_info *, int, const struct line_pnts **,
48  int, const struct line_cats *);
49 static off_t write_line_tp(struct Map_info *, int, int,
50  const struct line_pnts *, const struct line_cats *);
51 static char *binary_to_hex(int, const unsigned char *);
52 static unsigned char *point_to_wkb(int, const struct line_pnts *, int, int *);
53 static unsigned char *linestring_to_wkb(int, const struct line_pnts *, int,
54  int *);
55 static unsigned char *polygon_to_wkb(int, const struct line_pnts **, int, int,
56  int *);
57 static char *line_to_wkb(struct Format_info_pg *, const struct line_pnts **,
58  int, int, int);
59 static int write_feature(struct Map_info *, int, int, const struct line_pnts **,
60  int, int);
61 static char *build_insert_stmt(const struct Format_info_pg *, const char *, int,
62  int);
63 static int insert_topo_element(struct Map_info *, int, int, const char *);
64 static int type_to_topogeom(const struct Format_info_pg *);
65 static int update_next_edge(struct Map_info *, int, int);
66 
67 #if 0 /* unused */
68 static int delete_face(struct Map_info *, int);
69 static int update_topo_edge(struct Map_info *, int);
70 #endif
71 static int update_topo_face(struct Map_info *, int);
72 static int add_line_to_topo_pg(struct Map_info *, off_t, int,
73  const struct line_pnts *);
74 static int delete_line_from_topo_pg(struct Map_info *, int, int,
75  const struct line_pnts *);
76 static int set_constraint_to_deferrable(struct Format_info_pg *, const char *,
77  const char *, const char *,
78  const char *, const char *);
79 static dbDriver *open_db(struct Format_info_pg *);
80 
81 static struct line_pnts *Points;
82 
83 #define NOPG_UNUSED
84 #else
85 #define NOPG_UNUSED UNUSED
86 #endif
87 
88 /*!
89  \brief Writes feature on level 1 (PostGIS interface)
90 
91  Notes for simple feature access:
92  - centroids are not supported in PostGIS, pseudotopo holds virtual
93  centroids
94  - boundaries are not supported in PostGIS, pseudotopo treats polygons
95  as boundaries
96 
97  Notes for PostGIS Topology access:
98  - centroids are stored as isolated nodes
99  - boundaries are stored as edges
100 
101  \param Map pointer to Map_info structure
102  \param type feature type (GV_POINT, GV_LINE, ...)
103  \param points pointer to line_pnts structure (feature geometry)
104  \param cats pointer to line_cats structure (feature categories)
105 
106  \return feature offset into file
107  \return -1 on error
108  */
109 off_t V1_write_line_pg(struct Map_info *Map NOPG_UNUSED, int type NOPG_UNUSED,
110  const struct line_pnts *points NOPG_UNUSED,
111  const struct line_cats *cats NOPG_UNUSED)
112 {
113 #ifdef HAVE_POSTGRES
114  struct Format_info_pg *pg_info;
115 
116  pg_info = &(Map->fInfo.pg);
117 
118  if (pg_info->feature_type == SF_GEOMETRY) {
119  /* create PostGIS table if doesn't exist */
120  if (create_pg_layer(Map, type) < 0)
121  return -1;
122  }
123 
124  if (!points)
125  return 0;
126 
127  if (!pg_info->toposchema_name) { /* simple features access */
128  return write_line_sf(Map, type, &points, 1, cats);
129  }
130 
131  /* PostGIS Topology access */
132  return write_line_tp(Map, type, FALSE, points, cats);
133 #else
134  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
135  return -1;
136 #endif
137 }
138 
139 /*!
140  \brief Writes feature on topological level (PostGIS interface)
141 
142  Calls V2_write_line_sfa() for simple features access.
143 
144  \param Map pointer to Map_info structure
145  \param type feature type (GV_POINT, GV_LINE, ...)
146  \param points pointer to line_pnts structure (feature geometry)
147  \param cats pointer to line_cats structure (feature categories)
148 
149  \return feature offset into file
150  \return -1 on error
151  */
152 off_t V2_write_line_pg(struct Map_info *Map NOPG_UNUSED, int type NOPG_UNUSED,
153  const struct line_pnts *points NOPG_UNUSED,
154  const struct line_cats *cats NOPG_UNUSED)
155 {
156 #ifdef HAVE_POSTGRES
157  struct Format_info_pg *pg_info;
158 
159  pg_info = &(Map->fInfo.pg);
160 
161  if (!pg_info->toposchema_name) { /* pseudo-topology */
162  return V2_write_line_sfa(Map, type, points, cats);
163  }
164 
165  /* PostGIS Topology */
166  return write_line_tp(Map, type, FALSE, points, cats);
167 #else
168  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
169  return -1;
170 #endif
171 }
172 
173 /*!
174  \brief Rewrites feature at the given offset (level 1) (PostGIS interface,
175  internal use only)
176 
177  Only for simple feature access. PostGIS Topology requires level 2.
178 
179  \todo Use UPDATE statement ?
180 
181  \param Map pointer to Map_info structure
182  \param offset feature offset
183  \param type feature type (GV_POINT, GV_LINE, ...)
184  \param points feature geometry
185  \param cats feature categories
186 
187  \return feature offset (rewritten feature)
188  \return -1 on error
189  */
191  int type, const struct line_pnts *points NOPG_UNUSED,
192  const struct line_cats *cats NOPG_UNUSED)
193 {
194  G_debug(3, "V1_rewrite_line_pg(): type=%d offset=%" PRId64, type, offset);
195 #ifdef HAVE_POSTGRES
196  if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
197  G_warning(_("Unable to rewrite feature (incompatible feature types)"));
198  return -1;
199  }
200 
201  /* delete old */
203 
204  return V1_write_line_pg(Map, type, points, cats);
205 #else
206  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
207  return -1;
208 #endif
209 }
210 
211 /*!
212  \brief Rewrites feature at topological level (PostGIS interface, internal use
213  only)
214 
215  Note: Topology must be built at level >= GV_BUILD_BASE
216 
217  \todo Handle also categories
218  \todo Store original geometry in tmp table for restore
219 
220  \param Map pointer to Map_info structure
221  \param line feature id
222  \param type feature type (GV_POINT, GV_LINE, ...)
223  \param points feature geometry
224  \param cats feature categories (unused)
225 
226  \return offset where feature was rewritten
227  \return -1 on error
228  */
229 off_t V2_rewrite_line_pg(struct Map_info *Map NOPG_UNUSED, off_t line, int type,
230  const struct line_pnts *points NOPG_UNUSED,
231  const struct line_cats *cats UNUSED)
232 {
233  G_debug(3, "V2_rewrite_line_pg(): line=%d type=%d", (int)line, type);
234 #ifdef HAVE_POSTGRES
235  const char *schema_name, *table_name, *keycolumn;
236  char *stmt, *geom_data;
237 
238  struct Format_info_pg *pg_info;
239  struct P_line *Line;
240  off_t offset;
241 
242  geom_data = NULL;
243  stmt = NULL;
244  pg_info = &(Map->fInfo.pg);
245 
246  if (line < 1 || line > Map->plus.n_lines) {
247  G_warning(_("Attempt to access feature with invalid id (%d)"),
248  (int)line);
249  return -1;
250  }
251 
252  Line = Map->plus.Line[line];
253  if (Line == NULL) {
254  G_warning(_("Attempt to access dead feature %d"), (int)line);
255  return -1;
256  }
257  offset = Line->offset;
258 
259  if (!(Map->plus.update_cidx)) {
260  Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
261  }
262 
263  if (!Points)
264  Points = Vect_new_line_struct();
265 
266  if (type != V2_read_line_pg(Map, Points, NULL, line)) {
267  G_warning(_("Unable to rewrite feature (incompatible feature types)"));
268  return -1;
269  }
270 
271  /* remove line from topology */
272  if (0 != delete_line_from_topo_pg(Map, line, type, Points))
273  return -1;
274 
275  if (pg_info->toposchema_name) { /* PostGIS Topology */
276  schema_name = pg_info->toposchema_name;
277  if (type & GV_POINTS) {
278  table_name = keycolumn = "node";
279  }
280  else {
281  table_name = "edge_data";
282  keycolumn = "edge";
283  }
284  }
285  else { /* simple features access */
286  schema_name = pg_info->schema_name;
287  table_name = pg_info->table_name;
288  keycolumn = pg_info->fid_column;
289  }
290 
291  geom_data = line_to_wkb(pg_info, &points, 1, type, Map->head.with_z);
292  G_asprintf(&stmt,
293  "UPDATE \"%s\".\"%s\" SET geom = '%s'::GEOMETRY WHERE %s_id = "
294  "%" PRId64,
295  schema_name, table_name, geom_data, keycolumn, line);
296  G_free(geom_data);
297 
298  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
299  G_warning(_("Unable to rewrite feature %d"), (int)line);
300  Vect__execute_pg(pg_info->conn, "ROLLBACK");
301  return -1;
302  }
303 
304  /* update topology
305  note: offset is not changed */
306  return add_line_to_topo_pg(Map, offset, type, points);
307 #else
308  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
309  return -1;
310 #endif
311 }
312 
313 /*!
314  \brief Deletes feature at the given offset (level 1)
315 
316  Only for simple feature access. PostGIS Topology requires level 2.
317 
318  \param Map pointer Map_info structure
319  \param offset feature offset
320 
321  \return 0 on success
322  \return -1 on error
323  */
325  off_t offset NOPG_UNUSED)
326 {
327 #ifdef HAVE_POSTGRES
328  long fid;
329  char stmt[DB_SQL_MAX];
330 
331  struct Format_info_pg *pg_info;
332 
333  pg_info = &(Map->fInfo.pg);
334 
335  if (!pg_info->conn || !pg_info->table_name) {
336  G_warning(_("No connection defined"));
337  return -1;
338  }
339 
340  if (offset >= pg_info->offset.array_num) {
341  G_warning(_("Invalid offset (%" PRId64 ")"), offset);
342  return -1;
343  }
344 
345  fid = pg_info->offset.array[offset];
346 
347  G_debug(3, "V1_delete_line_pg(): offset = %lu -> fid = %ld",
348  (unsigned long)offset, fid);
349 
350  if (!pg_info->inTransaction) {
351  /* start transaction */
352  pg_info->inTransaction = TRUE;
353  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
354  return -1;
355  }
356 
357  snprintf(stmt, sizeof(stmt), "DELETE FROM %s WHERE %s = %ld",
358  pg_info->table_name, pg_info->fid_column, fid);
359  G_debug(3, "SQL: %s", stmt);
360 
361  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
362  G_warning(_("Unable to delete feature"));
363  Vect__execute_pg(pg_info->conn, "ROLLBACK");
364  return -1;
365  }
366 
367  return 0;
368 #else
369  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
370  return -1;
371 #endif
372 }
373 
374 /*!
375  \brief Deletes feature on topological level (PostGIS interface)
376 
377  Note: Topology must be built at level >= GV_BUILD_BASE
378 
379  Calls V2_delete_line_sfa() for simple feature access.
380 
381  \param Map pointer to Map_info structure
382  \param line feature id to be deleted
383 
384  \return 0 on success
385  \return -1 on error
386  */
387 int V2_delete_line_pg(struct Map_info *Map NOPG_UNUSED, off_t line NOPG_UNUSED)
388 {
389 #ifdef HAVE_POSTGRES
390  int ret;
391  struct Format_info_pg *pg_info;
392 
393  pg_info = &(Map->fInfo.pg);
394 
395  if (line < 1 || line > Map->plus.n_lines) {
396  G_warning(_("Attempt to access feature with invalid id (%d)"),
397  (int)line);
398  return -1;
399  }
400 
401  if (!pg_info->toposchema_name) { /* pseudo-topology */
402  return V2_delete_line_sfa(Map, line);
403  }
404  else { /* PostGIS topology */
405  int type;
406  char stmt[DB_SQL_MAX];
407  const char *table_name, *keycolumn;
408 
409  struct P_line *Line;
410 
411  if (line < 1 || line > Map->plus.n_lines) {
412  G_warning(_("Attempt to access feature with invalid id (%d)"),
413  (int)line);
414  return -1;
415  }
416 
417  Line = Map->plus.Line[line];
418  if (!Line) {
419  G_warning(_("Attempt to access dead feature %d"), (int)line);
420  return -1;
421  }
422 
423  if (!(Map->plus.update_cidx)) {
424  Map->plus.cidx_up_to_date =
425  FALSE; /* category index will be outdated */
426  }
427 
428  Vect__execute_pg(pg_info->conn, "BEGIN");
429 
430  if (Line->type & GV_POINTS) {
431  table_name = keycolumn = "node";
432  }
433  else {
434  table_name = "edge_data";
435  keycolumn = "edge";
436 
437  /* first remove references to this edge */
438  /* (1) left next edge */
439  snprintf(stmt, sizeof(stmt),
440  "UPDATE \"%s\".\"%s\" SET abs_next_left_edge = edge_id, "
441  "next_left_edge = -edge_id WHERE abs_next_left_edge = %d",
442  pg_info->toposchema_name, table_name, (int)Line->offset);
443  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
444  Vect__execute_pg(pg_info->conn, "ROLLBACK");
445  return -1;
446  }
447 
448  /* (2) right next edge */
449  snprintf(stmt, sizeof(stmt),
450  "UPDATE \"%s\".\"%s\" SET abs_next_right_edge = edge_id, "
451  "next_right_edge = edge_id WHERE abs_next_right_edge = %d",
452  pg_info->toposchema_name, table_name, (int)Line->offset);
453  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
454  Vect__execute_pg(pg_info->conn, "ROLLBACK");
455  return -1;
456  }
457  }
458 
459  /* read the line */
460  if (!Points)
461  Points = Vect_new_line_struct();
462 
463  type = V2_read_line_pg(Map, Points, NULL, line);
464  if (type < 0)
465  return -1;
466 
467  /* delete record from topology table */
468  snprintf(
469  stmt, sizeof(stmt), "DELETE FROM \"%s\".\"%s\" WHERE %s_id = %d",
470  pg_info->toposchema_name, table_name, keycolumn, (int)Line->offset);
471  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
472  G_warning(_("Unable to delete feature (%s) %d"), keycolumn,
473  (int)line);
474  Vect__execute_pg(pg_info->conn, "ROLLBACK");
475  return -1;
476  }
477 
478  if (pg_info->cache.ctype == CACHE_MAP) {
479  /* delete from cache */
480 
481  Vect_destroy_line_struct(pg_info->cache.lines[line - 1]);
482  pg_info->cache.lines[line - 1] = NULL;
483  pg_info->cache.lines_types[line - 1] = 0;
484  pg_info->cache.lines_cats[line - 1] = 0;
485  }
486 
487  /* update topology */
488  ret = delete_line_from_topo_pg(Map, line, type, Points);
489 
490  if (ret == 0)
491  Vect__execute_pg(pg_info->conn, "COMMIT");
492 
493  return ret;
494  }
495 #else
496  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
497  return -1;
498 #endif
499 }
500 
501 #ifdef HAVE_POSTGRES
502 /*!
503  \brief Writes node on topological level (PostGIS Topology
504  interface, internal use only)
505 
506  The vector map must be open on level 2 at least with
507  GV_BUILD_BASE. PostGIS Topology schema must be defined.
508 
509  \param Map pointer to Map_info structure
510  \param node node id (starts at 1)
511  \param points pointer to line_pnts structure
512 
513  \return 0 on success
514  \return -1 on error
515  */
516 off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
517 {
518  struct Format_info_pg *pg_info;
519 
520  pg_info = &(Map->fInfo.pg);
521 
522  if (!pg_info->toposchema_name)
523  return -1; /* PostGIS Topology required */
524 
525  return write_line_tp(Map, GV_POINT, TRUE, points, NULL);
526 }
527 
528 /*!
529  \brief Writes area on topological level (PostGIS Simple Features
530  interface, internal use only)
531 
532  \param Map pointer to Map_info structure
533  \param points feature geometry (exterior + interior rings)
534  \param nparts number of parts including exterior ring
535  \param cats feature categories
536 
537  \return feature offset
538  \return -1 on error
539  */
540 off_t V2__write_area_pg(struct Map_info *Map, const struct line_pnts **points,
541  int nparts, const struct line_cats *cats)
542 {
543  return write_line_sf(Map, GV_BOUNDARY, points, nparts, cats);
544 }
545 
546 /*!
547  \brief Updates simple features geometry from GRASS-like topo
548 
549  \param Map pointer to Map_info structure
550  \param points feature geometry (exterior + interior rings)
551  \param nparts number of parts including exterior ring
552  \param cat area category
553 
554  \return 0 on success
555  \return -1 on error
556  */
557 int V2__update_area_pg(struct Map_info *Map, const struct line_pnts **points,
558  int nparts, int cat)
559 {
560  int part, npoints;
561  char *stmt, *geom_data;
562 
563  struct Format_info_pg *pg_info;
564 
565  pg_info = &(Map->fInfo.pg);
566 
567  for (part = 0; part < nparts; part++) {
568  npoints = points[part]->n_points - 1;
569  if (points[part]->x[0] != points[part]->x[npoints] ||
570  points[part]->y[0] != points[part]->y[npoints] ||
571  points[part]->z[0] != points[part]->z[npoints]) {
572  G_warning(_("Boundary is not closed. Skipping."));
573  return -1;
574  }
575  }
576 
577  geom_data = line_to_wkb(pg_info, points, nparts, GV_AREA, Vect_is_3d(Map));
578  if (!geom_data)
579  return -1;
580 
581  stmt = NULL;
582  G_asprintf(&stmt,
583  "UPDATE \"%s\".\"%s\" SET %s = '%s'::GEOMETRY WHERE %s = %d",
584  pg_info->schema_name, pg_info->table_name, pg_info->geom_column,
585  geom_data, pg_info->fid_column, cat);
586  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
587  /* rollback transaction */
588  Vect__execute_pg(pg_info->conn, "ROLLBACK");
589  G_free(geom_data);
590  G_free(stmt);
591  return -1;
592  }
593 
594  G_free(geom_data);
595  G_free(stmt);
596 
597  return 0;
598 }
599 
600 /*!
601  \brief Create new feature table
602 
603  \param pg_info pointer to Format_info_pg
604 
605  \return -1 on error
606  \return 0 on success
607  */
608 int create_table(struct Format_info_pg *pg_info)
609 {
610  int spatial_index, primary_key;
611  char stmt[DB_SQL_MAX];
612  char *geom_type, *def_file;
613 
614  struct field_info *Fi;
615 
616  PGresult *result;
617 
618  def_file = getenv("GRASS_VECTOR_PGFILE");
619 
620  /* by default create spatial index & add primary key */
621  spatial_index = primary_key = TRUE;
622  if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
623  FILE *fp;
624  const char *p;
625 
626  struct Key_Value *key_val;
627 
628  fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
629  if (!fp) {
630  G_warning(_("Unable to open PG file"));
631  }
632  else {
633  key_val = G_fread_key_value(fp);
634  fclose(fp);
635 
636  /* disable spatial index ? */
637  p = G_find_key_value("spatial_index", key_val);
638  if (p && G_strcasecmp(p, "no") == 0)
640 
641  /* disable primary key ? */
642  p = G_find_key_value("primary_key", key_val);
643  if (p && G_strcasecmp(p, "no") == 0)
644  primary_key = FALSE;
645 
646  G_free_key_value(key_val);
647  }
648  }
649 
650  /* create schema if not exists */
651  if (G_strcasecmp(pg_info->schema_name, "public") != 0) {
652  if (check_schema(pg_info) != 0)
653  return -1;
654  }
655 
656  /* prepare CREATE TABLE statement */
657  snprintf(stmt, sizeof(stmt),
658  "CREATE TABLE \"%s\".\"%s\" (%s SERIAL%s, %s INTEGER",
659  pg_info->schema_name, pg_info->table_name, pg_info->fid_column,
660  primary_key ? " PRIMARY KEY" : "", GV_KEY_COLUMN);
661 
662  Fi = pg_info->fi;
663 
664  if (Fi) {
665  /* append attributes */
666  int col, ncols, sqltype, length;
667  char stmt_col[DB_SQL_MAX];
668  const char *colname;
669 
670  dbString dbtable_name;
671 
672  dbDriver *driver;
673  dbTable *table;
674  dbColumn *column;
675 
676  db_init_string(&dbtable_name);
677 
678  driver = open_db(pg_info);
679  if (driver == NULL)
680  return -1;
681 
682  /* describe table */
683  db_set_string(&dbtable_name, Fi->table);
684  if (db_describe_table(driver, &dbtable_name, &table) != DB_OK) {
685  G_warning(_("Unable to describe table <%s>"), Fi->table);
687  pg_info->dbdriver = NULL;
688  return -1;
689  }
690  ncols = db_get_table_number_of_columns(table);
691 
692  G_debug(3,
693  "copying attributes: driver = %s database = %s table = %s cols "
694  "= %d",
695  Fi->driver, Fi->database, Fi->table, ncols);
696 
697  for (col = 0; col < ncols; col++) {
698  column = db_get_table_column(table, col);
699  colname = db_get_column_name(column);
700  sqltype = db_get_column_sqltype(column);
701  length = db_get_column_length(column);
702 
703  G_debug(3, "\tcolumn = %d name = %s type = %d length = %d", col,
704  colname, sqltype, length);
705 
706  if (G_strcasecmp(pg_info->fid_column, colname) == 0 ||
707  G_strcasecmp(GV_KEY_COLUMN, colname) == 0) {
708  /* skip fid column if exists */
709  G_debug(3, "\t%s skipped", colname);
710  continue;
711  }
712 
713  /* append column */
714  snprintf(stmt_col, sizeof(stmt_col), ",\"%s\" %s", colname,
715  db_sqltype_name(sqltype));
716  strcat(stmt, stmt_col);
717  if (sqltype == DB_SQL_TYPE_CHARACTER) {
718  /* length only for string columns */
719  snprintf(stmt_col, sizeof(stmt_col), "(%d)", length);
720  strcat(stmt, stmt_col);
721  }
722  }
723 
724  db_free_string(&dbtable_name);
725  }
726  strcat(stmt, ")"); /* close CREATE TABLE statement */
727 
728  /* begin transaction (create table) */
729  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
730  return -1;
731  }
732 
733  /* create table */
734  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
735  Vect__execute_pg(pg_info->conn, "ROLLBACK");
736  return -1;
737  }
738 
739  /* determine geometry type (string) */
740  switch (pg_info->feature_type) {
741  case (SF_POINT):
742  geom_type = "POINT";
743  break;
744  case (SF_LINESTRING):
745  geom_type = "LINESTRING";
746  break;
747  case (SF_POLYGON):
748  geom_type = "POLYGON";
749  break;
750  case (SF_POLYGON25D):
751  geom_type = "POLYGONZ";
752  break;
753  case (SF_GEOMETRY):
754  geom_type = "GEOMETRY";
755  break;
756  default:
757  G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
758  Vect__execute_pg(pg_info->conn, "ROLLBACK");
759  return -1;
760  }
761 
762  /* add geometry column */
763  snprintf(stmt, sizeof(stmt),
764  "SELECT AddGeometryColumn('%s', '%s', "
765  "'%s', %d, '%s', %d)",
766  pg_info->schema_name, pg_info->table_name, pg_info->geom_column,
767  pg_info->srid, geom_type, pg_info->coor_dim);
768  G_debug(2, "SQL: %s", stmt);
769  result = PQexec(pg_info->conn, stmt);
770 
771  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
772  G_warning("%s", PQresultErrorMessage(result));
773  PQclear(result);
774  Vect__execute_pg(pg_info->conn, "ROLLBACK");
775  return -1;
776  }
777 
778  /* create indices
779  - GV_KEY_COLUMN
780  - geometry column
781  */
782  snprintf(stmt, sizeof(stmt), "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" (%s)",
783  pg_info->table_name, GV_KEY_COLUMN, pg_info->schema_name,
784  pg_info->table_name, GV_KEY_COLUMN);
785  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
786  Vect__execute_pg(pg_info->conn, "ROLLBACK");
787  return -1;
788  }
789 
790  if (spatial_index) {
791  G_verbose_message(_("Building spatial index on <%s>..."),
792  pg_info->geom_column);
793  snprintf(stmt, sizeof(stmt),
794  "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" USING GIST (%s)",
795  pg_info->table_name, pg_info->geom_column,
796  pg_info->schema_name, pg_info->table_name,
797  pg_info->geom_column);
798 
799  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
800  Vect__execute_pg(pg_info->conn, "ROLLBACK");
801  return -1;
802  }
803  }
804 
805  /* close transaction (create table) */
806  if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
807  return -1;
808  }
809 
810  return 0;
811 }
812 
813 /*!
814  \brief Creates new schema for feature table if not exists
815 
816  \param pg_info pointer to Format_info_pg
817 
818  \return -1 on error
819  \return 0 on success
820  */
821 int check_schema(const struct Format_info_pg *pg_info)
822 {
823  int i, found, nschema;
824  char stmt[DB_SQL_MAX];
825 
826  PGresult *result;
827 
828  if (!pg_info->conn || !pg_info->table_name) {
829  G_warning(_("No connection defined"));
830  return -1;
831  }
832 
833  /* add geometry column */
834  snprintf(stmt, sizeof(stmt), "SELECT nspname FROM pg_namespace");
835  G_debug(2, "SQL: %s", stmt);
836  result = PQexec(pg_info->conn, stmt);
837 
838  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
839  PQclear(result);
840  Vect__execute_pg(pg_info->conn, "ROLLBACK");
841  return -1;
842  }
843 
844  found = FALSE;
845  nschema = PQntuples(result);
846  for (i = 0; i < nschema && !found; i++) {
847  if (strcmp(pg_info->schema_name, PQgetvalue(result, i, 0)) == 0)
848  found = TRUE;
849  }
850 
851  PQclear(result);
852 
853  if (!found) {
854  snprintf(stmt, sizeof(stmt), "CREATE SCHEMA %s", pg_info->schema_name);
855  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
856  Vect__execute_pg(pg_info->conn, "ROLLBACK");
857  return -1;
858  }
859  G_warning(_("Schema <%s> doesn't exist, created"),
860  pg_info->schema_name);
861  }
862 
863  return 0;
864 }
865 
866 /*!
867  \brief Create new PostGIS topology schema
868 
869  - create topology schema
870  - add topology column to the feature table
871 
872  \todo Add constraints for grass-like tables
873 
874  \param pg_info pointer to Format_info_pg
875 
876  \return 0 on success
877  \return 1 topology disable, nothing to do
878  \return -1 on failure
879  */
880 int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
881 {
882  double tolerance;
883  char stmt[DB_SQL_MAX];
884  char *def_file;
885 
886  def_file = getenv("GRASS_VECTOR_PGFILE");
887 
888  /* read default values from PG file */
889  tolerance = 0.;
890  if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
891  FILE *fp;
892  const char *p;
893 
894  struct Key_Value *key_val;
895 
896  fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
897  if (!fp) {
898  G_fatal_error(_("Unable to open PG file"));
899  }
900  key_val = G_fread_key_value(fp);
901  fclose(fp);
902 
903  /* tolerance */
904  p = G_find_key_value("topo_tolerance", key_val);
905  if (p)
906  tolerance = atof(p);
907  G_debug(1, "PG: tolerance: %f", tolerance);
908 
909  /* topogeom column */
910  p = G_find_key_value("topogeom_name", key_val);
911  if (p)
912  pg_info->topogeom_column = G_store(p);
913  else
915  G_debug(1, "PG: topogeom_column :%s", pg_info->topogeom_column);
916 
917  /* topo-geo only (default: no) */
918  p = G_find_key_value("topo_geo_only", key_val);
919  if (p && G_strcasecmp(p, "yes") == 0)
920  pg_info->topo_geo_only = TRUE;
921  G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
922 
923  /* build simple features from topogeometry data */
924  p = G_find_key_value("simple_feature", key_val);
925  if (p && G_strcasecmp(p, "yes") == 0)
926  pg_info->topo_geo_only = TRUE;
927  G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
928 
929  G_free_key_value(key_val);
930  }
931 
932  /* begin transaction (create topo schema) */
933  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
934  return -1;
935  }
936 
937  /* create topology schema */
938  G_verbose_message(_("Creating topology schema <%s>..."),
939  pg_info->toposchema_name);
940  snprintf(stmt, sizeof(stmt),
941  "SELECT topology.createtopology('%s', "
942  "find_srid('%s', '%s', '%s'), %f, '%s')",
943  pg_info->toposchema_name, pg_info->schema_name,
944  pg_info->table_name, pg_info->geom_column, tolerance,
945  with_z == WITH_Z ? "t" : "f");
946  pg_info->toposchema_id = Vect__execute_get_value_pg(pg_info->conn, stmt);
947  if (pg_info->toposchema_id == -1) {
948  Vect__execute_pg(pg_info->conn, "ROLLBACK");
949  return -1;
950  }
951 
952  /* add topo column to the feature table */
953  G_verbose_message(_("Adding new topology column <%s>..."),
954  pg_info->topogeom_column);
955  snprintf(stmt, sizeof(stmt),
956  "SELECT topology.AddTopoGeometryColumn('%s', '%s', '%s', "
957  "'%s', '%s')",
958  pg_info->toposchema_name, pg_info->schema_name,
959  pg_info->table_name, pg_info->topogeom_column,
960  get_sftype(pg_info->feature_type));
961  if (-1 == Vect__execute_get_value_pg(pg_info->conn, stmt)) {
962  Vect__execute_pg(pg_info->conn, "ROLLBACK");
963  return -1;
964  }
965 
966  /* create index on topo column */
967  snprintf(stmt, sizeof(stmt),
968  "CREATE INDEX \"%s_%s_%s_idx\" ON \"%s\".\"%s\" (((%s).id))",
969  pg_info->schema_name, pg_info->table_name,
970  pg_info->topogeom_column, pg_info->schema_name,
971  pg_info->table_name, pg_info->topogeom_column);
972  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
973  Vect__execute_pg(pg_info->conn, "ROLLBACK");
974  return -1;
975  }
976 
977  /* change constraints to deferrable initially deferred */
978  if (!pg_info->topo_geo_only) {
979  if (-1 == set_constraint_to_deferrable(pg_info, "node", "face_exists",
980  "containing_face", "face",
981  "face_id") ||
982  -1 == set_constraint_to_deferrable(pg_info, "edge_data",
983  "end_node_exists", "end_node",
984  "node", "node_id") ||
985  -1 == set_constraint_to_deferrable(pg_info, "edge_data",
986  "left_face_exists", "left_face",
987  "face", "face_id") ||
988  -1 == set_constraint_to_deferrable(
989  pg_info, "edge_data", "right_face_exists", "right_face",
990  "face", "face_id") ||
991  -1 == set_constraint_to_deferrable(pg_info, "edge_data",
992  "start_node_exists",
993  "start_node", "node", "node_id"))
994  return -1;
995  }
996 
997  /* create additional tables in topological schema to store
998  GRASS topology in DB */
999  if (!pg_info->topo_geo_only) {
1000  /* (1) create 'node_grass' (see P_node struct)
1001 
1002  todo: add constraints for lines and angles
1003  */
1004  snprintf(stmt, sizeof(stmt),
1005  "CREATE TABLE \"%s\".%s (node_id SERIAL PRIMARY KEY, "
1006  "lines integer[], angles float[])",
1007  pg_info->toposchema_name, TOPO_TABLE_NODE);
1008  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1009  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1010  return -1;
1011  }
1012 
1013  snprintf(stmt, sizeof(stmt),
1014  "ALTER TABLE \"%s\".%s ADD CONSTRAINT node_exists "
1015  "FOREIGN KEY (node_id) REFERENCES \"%s\".node (node_id) "
1016  "DEFERRABLE INITIALLY DEFERRED",
1017  pg_info->toposchema_name, TOPO_TABLE_NODE,
1018  pg_info->toposchema_name);
1019  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1020  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1021  return -1;
1022  }
1023 
1024  /* (2) create 'line_grass' (see P_line struct)
1025 
1026  */
1027  snprintf(stmt, sizeof(stmt),
1028  "CREATE TABLE \"%s\".%s (line_id SERIAL PRIMARY KEY, "
1029  "left_area integer, right_area integer)",
1030  pg_info->toposchema_name, TOPO_TABLE_LINE);
1031  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1032  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1033  return -1;
1034  }
1035 
1036  snprintf(stmt, sizeof(stmt),
1037  "ALTER TABLE \"%s\".%s ADD CONSTRAINT line_exists "
1038  "FOREIGN KEY (line_id) REFERENCES \"%s\".edge_data (edge_id) "
1039  "DEFERRABLE INITIALLY DEFERRED",
1040  pg_info->toposchema_name, TOPO_TABLE_LINE,
1041  pg_info->toposchema_name);
1042  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1043  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1044  return -1;
1045  }
1046 
1047  /* (3) create 'area_grass' (see P_area struct)
1048 
1049  todo: add constraints for lines, centtroid and isles
1050  */
1051  snprintf(stmt, sizeof(stmt),
1052  "CREATE TABLE \"%s\".%s (area_id SERIAL PRIMARY KEY, "
1053  "lines integer[], centroid integer, isles integer[])",
1054  pg_info->toposchema_name, TOPO_TABLE_AREA);
1055  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1056  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1057  return -1;
1058  }
1059 
1060  /* (4) create 'isle_grass' (see P_isle struct)
1061 
1062  todo: add constraints for lines and area
1063  */
1064  snprintf(stmt, sizeof(stmt),
1065  "CREATE TABLE \"%s\".%s (isle_id SERIAL PRIMARY KEY, "
1066  "lines integer[], area integer)",
1067  pg_info->toposchema_name, TOPO_TABLE_ISLE);
1068  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1069  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1070  return -1;
1071  }
1072  }
1073 
1074  /* close transaction (create topo schema) */
1075  if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
1076  return -1;
1077  }
1078 
1079  return 0;
1080 }
1081 
1082 /*!
1083  \brief Create new PostGIS layer in given database (internal use only)
1084 
1085  V1_open_new_pg() must be called before this function.
1086 
1087  List of currently supported types:
1088  - GV_POINT (SF_POINT)
1089  - GV_LINE (SF_LINESTRING)
1090  - GV_BOUNDARY (SF_POLYGON)
1091 
1092  When PostGIS Topology the map level is updated to topological level
1093  and build level set to GV_BUILD_BASE.
1094 
1095  \param[in,out] Map pointer to Map_info structure
1096  \param type feature type (GV_POINT, GV_LINE, ...)
1097 
1098  \return 0 success
1099  \return -1 error
1100  */
1101 int create_pg_layer(struct Map_info *Map, int type)
1102 {
1103  int ndblinks;
1104 
1105  struct Format_info_pg *pg_info;
1106 
1107  pg_info = &(Map->fInfo.pg);
1108  if (!pg_info->conninfo) {
1109  G_warning(_("Connection string not defined"));
1110  return -1;
1111  }
1112 
1113  if (!pg_info->table_name) {
1114  G_warning(_("PostGIS feature table not defined"));
1115  return -1;
1116  }
1117 
1118  G_debug(1, "Vect__open_new_pg(): conninfo='%s' table='%s' -> type = %d",
1119  pg_info->conninfo, pg_info->table_name, type);
1120 
1121  /* determine geometry type */
1122 
1123  switch (type) {
1124  case GV_POINT:
1125  case GV_CENTROID:
1126  pg_info->feature_type = SF_POINT;
1127  break;
1128  case GV_LINE:
1129  case GV_BOUNDARY:
1130  pg_info->feature_type = SF_LINESTRING;
1131  break;
1132  case GV_AREA:
1133  pg_info->feature_type = SF_POLYGON;
1134  break;
1135  case GV_FACE:
1136  pg_info->feature_type = SF_POLYGON25D;
1137  break;
1138  case -2:
1139  pg_info->feature_type = SF_GEOMETRY;
1140  break;
1141  default:
1142  G_warning(_("Unsupported geometry type (%d)"), type);
1143  return -1;
1144  }
1145 
1146  /* coordinate dimension */
1147  pg_info->coor_dim = Vect_is_3d(Map) ? 3 : 2;
1148 
1149  /* create new PostGIS table */
1150  ndblinks = Vect_get_num_dblinks(Map);
1151  if (ndblinks > 0) {
1152  pg_info->fi = Vect_get_dblink(Map, 0); /* TODO: support more layers */
1153  if (pg_info->fi) {
1154  if (ndblinks > 1)
1155  G_warning(_("More layers defined, using driver <%s> and "
1156  "database <%s>"),
1157  pg_info->fi->driver, pg_info->fi->database);
1158  }
1159  else {
1160  G_warning(_("Database connection not defined. "
1161  "Unable to write attributes."));
1162  }
1163  }
1164 
1165  /* create new feature table */
1166  if (create_table(pg_info) == -1) {
1167  G_warning(_("Unable to create new PostGIS feature table"));
1168  return -1;
1169  }
1170 
1171  /* create new topology schema (if PostGIS topology support is enabled) */
1172  if (pg_info->toposchema_name) {
1173  /* force topological level */
1174  Map->level = LEVEL_2;
1175  Map->plus.built = GV_BUILD_BASE;
1176 
1177  /* track updated features, used in V2__add_line_to_topo_nat() */
1178  Vect_set_updated(Map, TRUE);
1179 
1180  if (create_topo_schema(pg_info, Vect_is_3d(Map)) == -1) {
1181  G_warning(_("Unable to create new PostGIS topology schema"));
1182  return -1;
1183  }
1184  }
1185 
1186  return 0;
1187 }
1188 
1189 /*!
1190  \brief Get simple feature type as a string
1191 
1192  Used for AddTopoGeometryColumn().
1193 
1194  Valid types:
1195  - SF_POINT
1196  - SF_LINESTRING
1197  - SF_POLYGON
1198 
1199  \return string with feature type
1200  \return empty string
1201  */
1202 char *get_sftype(SF_FeatureType sftype)
1203 {
1204  if (sftype == SF_POINT)
1205  return "POINT";
1206  else if (sftype == SF_LINESTRING)
1207  return "LINE";
1208  else if (sftype == SF_POLYGON)
1209  return "POLYGON";
1210  else if (sftype == SF_GEOMETRY || sftype == SF_GEOMETRYCOLLECTION)
1211  return "COLLECTION";
1212  else
1213  G_warning(_("Unsupported feature type %d"), sftype);
1214 
1215  return "";
1216 }
1217 
1218 /*!
1219  \brief Write vector features as PostGIS simple feature element
1220 
1221  \param Map pointer to Map_info structure
1222  \param type feature type (GV_POINT, GV_LINE, ...)
1223  \param points feature geometry (exterior + interior rings for polygonsx)
1224  \param nparts number of parts
1225  \param cats feature categories
1226 
1227  \return feature offset
1228  \return -1 on error
1229  */
1230 off_t write_line_sf(struct Map_info *Map, int type,
1231  const struct line_pnts **points, int nparts,
1232  const struct line_cats *cats)
1233 {
1234  int cat;
1235  off_t offset;
1236 
1237  SF_FeatureType sf_type;
1238 
1239  struct Format_info_pg *pg_info;
1240  struct Format_info_offset *offset_info;
1241 
1242  pg_info = &(Map->fInfo.pg);
1243  offset_info = &(pg_info->offset);
1244 
1245  if (nparts < 1)
1246  return -1;
1247 
1248  /* check required PG settings */
1249  if (!pg_info->conn) {
1250  G_warning(_("No connection defined"));
1251  return -1;
1252  }
1253  if (!pg_info->table_name) {
1254  G_warning(_("PostGIS feature table not defined"));
1255  return -1;
1256  }
1257 
1258  /* create PostGIS table if doesn't exist */
1259  if (pg_info->feature_type == SF_GEOMETRY) {
1260  if (create_pg_layer(Map, type) < 0)
1261  return -1;
1262  }
1263 
1264  /* get category & check for attributes */
1265  cat = -1;
1266  if (cats->n_cats > 0) {
1267  int field;
1268 
1269  if (pg_info->fi)
1270  field = pg_info->fi->number;
1271  else
1272  field = 1;
1273 
1274  if (!Vect_cat_get(cats, field, &cat))
1275  G_warning(_("No category defined for layer %d"), field);
1276  if (cats->n_cats > 1) {
1277  G_warning(_("Feature has more categories, using "
1278  "category %d (from layer %d)"),
1279  cat, field);
1280  }
1281  }
1282 
1283  sf_type = pg_info->feature_type;
1284 
1285  /* determine matching PostGIS feature geometry type */
1286  if (type & (GV_POINT | GV_KERNEL)) {
1287  if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
1288  G_warning(_("Point skipped (output feature type: %s)"),
1290  return 0;
1291  }
1292  }
1293  else if (type & GV_LINE) {
1294  if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
1295  G_warning(_("Line skipped (output feature type: %s)"),
1297  return 0;
1298  }
1299  }
1300  else if (type & GV_CENTROID) {
1301  if (sf_type != SF_POLYGON && sf_type != SF_POINT) {
1302  G_warning(_("Centroid skipped (output feature type: %s)"),
1304  return 0;
1305  }
1306  }
1307  else if (type & GV_BOUNDARY) {
1308  if (sf_type != SF_POLYGON && sf_type != SF_LINESTRING) {
1309  G_warning(_("Boundary skipped (output feature type: %s)"),
1311  return 0;
1312  }
1313  }
1314  else if (type & GV_FACE) {
1315  if (sf_type != SF_POLYGON25D) {
1316  G_warning(_("Face skipped (output feature type: %s)"),
1318  return 0;
1319  }
1320  }
1321  else {
1322  G_warning(_("Unsupported feature type %d"), type);
1323  return -1;
1324  }
1325 
1326  G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d", type,
1327  points[0]->n_points, cat);
1328 
1329  if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
1330  /* skip this check when writing PostGIS topology */
1331  int part, npoints;
1332 
1333  for (part = 0; part < nparts; part++) {
1334  npoints = points[part]->n_points - 1;
1335  if (points[part]->x[0] != points[part]->x[npoints] ||
1336  points[part]->y[0] != points[part]->y[npoints] ||
1337  points[part]->z[0] != points[part]->z[npoints]) {
1338  G_warning(_("Boundary is not closed. Skipping."));
1339  return -1;
1340  }
1341  }
1342  }
1343 
1344  /* write feature's geometry and fid */
1345  if (-1 == write_feature(Map, -1, type, points, nparts, cat)) {
1346  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1347  return -1;
1348  }
1349 
1350  /* update offset array */
1351  if (offset_info->array_num >= offset_info->array_alloc) {
1352  offset_info->array_alloc += 1000;
1353  offset_info->array = (int *)G_realloc(
1354  offset_info->array, offset_info->array_alloc * sizeof(int));
1355  }
1356  offset = offset_info->array_num;
1357 
1358  offset_info->array[offset_info->array_num++] = cat;
1359  if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
1360  /* register first part in offset array */
1361  offset_info->array[offset_info->array_num++] = 0;
1362  }
1363  G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
1364  (unsigned long)offset, offset_info->array_num, cat);
1365 
1366  return offset;
1367 }
1368 
1369 /*!
1370  \brief Write vector feature in PostGIS topology schema and
1371  updates internal topology structures
1372 
1373  \param Map vector map
1374  \param type feature type to be written
1375  \param points feature geometry
1376  \param is_node TRUE for nodes (written as points)
1377 
1378  \return feature id (build level >= GV_BUILD_BASE otherwise 0)
1379  \return 0 for nodes
1380  \return -1 on error
1381  */
1382 off_t write_line_tp(struct Map_info *Map, int type, int is_node,
1383  const struct line_pnts *points,
1384  const struct line_cats *cats)
1385 {
1386  int line, cat, line_id;
1387 
1388  struct Format_info_pg *pg_info;
1389  struct Plus_head *plus;
1390  struct field_info *Fi;
1391 
1392  pg_info = &(Map->fInfo.pg);
1393  plus = &(Map->plus);
1394 
1395  if (!(plus->update_cidx)) {
1396  plus->cidx_up_to_date = FALSE; /* category index will be outdated */
1397  }
1398 
1399  /* check type for nodes */
1400  if (is_node && type != GV_POINT) {
1401  G_warning(_("Invalid feature type (%d) for nodes"), type);
1402  return -1;
1403  }
1404 
1405  /* check required PG settings */
1406  if (!pg_info->conn) {
1407  G_warning(_("No connection defined"));
1408  return -1;
1409  }
1410  if (!pg_info->table_name) {
1411  G_warning(_("PostGIS feature table not defined"));
1412  return -1;
1413  }
1414  if (!pg_info->toposchema_name) {
1415  G_warning(_("PostGIS topology schema not defined"));
1416  return -1;
1417  }
1418 
1419  /* create PostGIS table if doesn't exist */
1420  if (pg_info->feature_type == SF_GEOMETRY) {
1421  if (create_pg_layer(Map, type) < 0)
1422  return -1;
1423  }
1424 
1425  if (!points)
1426  return 0;
1427 
1428  G_debug(3, "write_line_pg(): type = %d n_points = %d", type,
1429  points->n_points);
1430 
1431  Fi = pg_info->fi;
1432 
1433  cat = -1;
1434  if (cats && cats->n_cats > 0) {
1435  if (Fi) {
1436  if (!pg_info->dbdriver)
1437  open_db(pg_info);
1438  if (!Vect_cat_get(cats, Fi->number, &cat))
1439  G_warning(_("No category defined for layer %d"), Fi->number);
1440  if (cats->n_cats > 1) {
1441  G_warning(_("Feature has more categories, using "
1442  "category %d (from layer %d)"),
1443  cat, cats->field[0]);
1444  }
1445  }
1446  /* assume layer=1 */
1447  Vect_cat_get(cats, 1, &cat);
1448  }
1449 
1450  /* update GRASS topology before writing PostGIS feature */
1451  line = 0;
1452  if (plus->built >= GV_BUILD_BASE) {
1453  if (is_node) {
1454  /* nodes are given with negative id */
1455  line = -1 *
1456  dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
1457  }
1458  else {
1459  off_t offset;
1460 
1461  /* better is probably to check nextval directly */
1462  if (type & GV_POINTS) {
1463  offset = Vect_get_num_primitives(Map, GV_POINTS) + 1; /* next */
1464  offset += Vect_get_num_nodes(
1465  Map); /* nodes are also stored in 'node' table */
1466  }
1467  else { /* LINES */
1468  offset = Vect_get_num_primitives(Map, GV_LINES) + 1; /* next */
1469  }
1470 
1471  line = add_line_to_topo_pg(Map, offset, type, points);
1472  }
1473  }
1474 
1475  /* write new feature to PostGIS
1476  - feature table for simple features
1477  - feature table and topo schema for topological access
1478  */
1479  line_id = write_feature(Map, line, type, &points, 1, cat);
1480  if (line_id < 0) {
1481  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1482  return -1;
1483  }
1484 
1485  if (pg_info->cache.ctype == CACHE_MAP) {
1486  /* add line to the cache */
1487  Vect__reallocate_cache(&(pg_info->cache), 1, TRUE);
1488  pg_info->cache.lines[line - 1] = Vect_new_line_struct();
1489  pg_info->cache.lines_types[line - 1] = type;
1490  pg_info->cache.lines_cats[line - 1] = cat;
1491  }
1492 
1493  /* update offset array for nodes */
1494  if (is_node) {
1495  int node;
1496 
1497  struct Format_info_offset *offset;
1498 
1499  offset = &(pg_info->offset);
1500 
1501  node = abs(line);
1502  if (node > offset->array_alloc) {
1503  offset->array_alloc += 1000;
1504  offset->array = (int *)G_realloc(offset->array,
1505  offset->array_alloc * sizeof(int));
1506  }
1507 
1508  offset->array_num = node;
1509  offset->array[node - 1] = (int)line_id; /* node id starts at 1 */
1510  }
1511 
1512  /* update PostGIS-line topo */
1513  if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
1514  update_topo_face(Map, line); /* TODO: avoid extra statements */
1515 
1516  return !is_node ? line : 0;
1517 }
1518 
1519 /*!
1520  \brief Binary data to HEX
1521 
1522  Allocated buffer should be freed by G_free().
1523 
1524  \param nbytes number of bytes to allocate
1525  \param wkb_data WKB data
1526 
1527  \return allocated buffer with HEX data
1528  */
1529 char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
1530 {
1531  char *hex_data;
1532  int i, nlow, nhigh;
1533  static const char ach_hex[] = "0123456789ABCDEF";
1534 
1535  hex_data = (char *)G_malloc(nbytes * 2 + 1);
1536  hex_data[nbytes * 2] = '\0';
1537 
1538  for (i = 0; i < nbytes; i++) {
1539  nlow = wkb_data[i] & 0x0f;
1540  nhigh = (wkb_data[i] & 0xf0) >> 4;
1541 
1542  hex_data[i * 2] = ach_hex[nhigh];
1543  hex_data[i * 2 + 1] = ach_hex[nlow];
1544  }
1545 
1546  return hex_data;
1547 }
1548 
1549 /*!
1550  \brief Write point into WKB buffer
1551 
1552  See OGRPoint::exportToWkb from GDAL/OGR library
1553 
1554  \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
1555  \param points feature geometry
1556  \param with_z WITH_Z for 3D data
1557  \param[out] nsize buffer size
1558 
1559  \return allocated WKB buffer
1560  \return NULL on error
1561  */
1562 unsigned char *point_to_wkb(int byte_order, const struct line_pnts *points,
1563  int with_z, int *nsize)
1564 {
1565  unsigned char *wkb_data;
1566  unsigned int sf_type;
1567 
1568  if (points->n_points != 1)
1569  return NULL;
1570 
1571  /* allocate buffer */
1572  *nsize = with_z ? 29 : 21;
1573  wkb_data = G_malloc(*nsize);
1574  G_zero(wkb_data, *nsize);
1575 
1576  G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
1577 
1578  /* set the byte order */
1579  if (byte_order == ENDIAN_LITTLE)
1580  wkb_data[0] = '\001';
1581  else
1582  wkb_data[0] = '\000';
1583 
1584  /* set the geometry feature type */
1585  sf_type = with_z ? SF_POINT25D : SF_POINT;
1586 
1587  if (byte_order == ENDIAN_LITTLE)
1588  sf_type = LSBWORD32(sf_type);
1589  else
1590  sf_type = MSBWORD32(sf_type);
1591  memcpy(wkb_data + 1, &sf_type, 4);
1592 
1593  /* copy in the raw data */
1594  memcpy(wkb_data + 5, &(points->x[0]), 8);
1595  memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
1596 
1597  if (with_z) {
1598  memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
1599  }
1600 
1601  /* swap if needed */
1602  if (byte_order == ENDIAN_BIG) {
1603  SWAPDOUBLE(wkb_data + 5);
1604  SWAPDOUBLE(wkb_data + 5 + 8);
1605 
1606  if (with_z)
1607  SWAPDOUBLE(wkb_data + 5 + 16);
1608  }
1609 
1610  return wkb_data;
1611 }
1612 
1613 /*!
1614  \bried Write linestring into WKB buffer
1615 
1616  See OGRLineString::exportToWkb from GDAL/OGR library
1617 
1618  \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
1619  \param points feature geometry
1620  \param with_z WITH_Z for 3D data
1621  \param[out] nsize buffer size
1622 
1623  \return allocated WKB buffer
1624  \return NULL on error
1625  */
1626 unsigned char *linestring_to_wkb(int byte_order, const struct line_pnts *points,
1627  int with_z, int *nsize)
1628 {
1629  int i, point_size;
1630  unsigned char *wkb_data;
1631  unsigned int sf_type;
1632 
1633  if (points->n_points < 1)
1634  return NULL;
1635 
1636  /* allocate buffer */
1637  point_size = 8 * (with_z ? 3 : 2);
1638  *nsize = 5 + 4 + points->n_points * point_size;
1639  wkb_data = G_malloc(*nsize);
1640  G_zero(wkb_data, *nsize);
1641 
1642  G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
1643 
1644  /* set the byte order */
1645  if (byte_order == ENDIAN_LITTLE)
1646  wkb_data[0] = '\001';
1647  else
1648  wkb_data[0] = '\000';
1649 
1650  /* set the geometry feature type */
1651  sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
1652 
1653  if (byte_order == ENDIAN_LITTLE)
1654  sf_type = LSBWORD32(sf_type);
1655  else
1656  sf_type = MSBWORD32(sf_type);
1657  memcpy(wkb_data + 1, &sf_type, 4);
1658 
1659  /* copy in the data count */
1660  memcpy(wkb_data + 5, &(points->n_points), 4);
1661 
1662  /* copy in the raw data */
1663  for (i = 0; i < points->n_points; i++) {
1664  memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
1665  memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
1666 
1667  if (with_z) {
1668  memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
1669  }
1670  }
1671 
1672  /* swap if needed */
1673  if (byte_order == ENDIAN_BIG) {
1674  int npoints, nitems;
1675 
1676  npoints = SWAP32(points->n_points);
1677  memcpy(wkb_data + 5, &npoints, 4);
1678 
1679  nitems = (with_z ? 3 : 2) * points->n_points;
1680  for (i = 0; i < nitems; i++) {
1681  SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
1682  }
1683  }
1684 
1685  return wkb_data;
1686 }
1687 
1688 /*!
1689  \bried Write polygon into WKB buffer
1690 
1691  See OGRPolygon::exportToWkb from GDAL/OGR library
1692 
1693  \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
1694  \param ipoints list of ring geometries (first is outer ring)
1695  \param nrings number of rings
1696  \param with_z WITH_Z for 3D data
1697  \param[out] nsize buffer size
1698 
1699  \return allocated WKB buffer
1700  \return NULL on error
1701  */
1702 unsigned char *polygon_to_wkb(int byte_order, const struct line_pnts **points,
1703  int nrings, int with_z, int *nsize)
1704 {
1705  int i, ring, point_size, offset;
1706  unsigned char *wkb_data;
1707  unsigned int sf_type;
1708 
1709  /* check data validity */
1710  if (nrings < 1)
1711  return NULL;
1712  for (ring = 0; ring < nrings; ring++) {
1713  if (points[ring]->n_points < 3)
1714  return NULL;
1715  }
1716 
1717  /* allocate buffer */
1718  point_size = 8 * (with_z ? 3 : 2);
1719  *nsize = 9;
1720  for (ring = 0; ring < nrings; ring++)
1721  *nsize += 4 + point_size * points[ring]->n_points;
1722  wkb_data = G_malloc(*nsize);
1723  G_zero(wkb_data, *nsize);
1724 
1725  G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
1726 
1727  /* set the byte order */
1728  if (byte_order == ENDIAN_LITTLE)
1729  wkb_data[0] = '\001';
1730  else
1731  wkb_data[0] = '\000';
1732 
1733  /* set the geometry feature type */
1734  sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
1735 
1736  if (byte_order == ENDIAN_LITTLE)
1737  sf_type = LSBWORD32(sf_type);
1738  else
1739  sf_type = MSBWORD32(sf_type);
1740  memcpy(wkb_data + 1, &sf_type, 4);
1741 
1742  /* copy in the raw data */
1743  if (byte_order == ENDIAN_BIG) {
1744  int ncount;
1745 
1746  ncount = SWAP32(nrings);
1747  memcpy(wkb_data + 5, &ncount, 4);
1748  }
1749  else {
1750  memcpy(wkb_data + 5, &nrings, 4);
1751  }
1752 
1753  /* serialize rings */
1754  offset = 9;
1755  for (ring = 0; ring < nrings; ring++) {
1756  memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
1757  for (i = 0; i < points[ring]->n_points; i++) {
1758  memcpy(wkb_data + offset + 4 + point_size * i,
1759  &(points[ring]->x[i]), 8);
1760  memcpy(wkb_data + offset + 4 + 8 + point_size * i,
1761  &(points[ring]->y[i]), 8);
1762 
1763  if (with_z) {
1764  memcpy(wkb_data + offset + 4 + 16 + point_size * i,
1765  &(points[ring]->z[i]), 8);
1766  }
1767  }
1768 
1769  offset += 4 + point_size * points[ring]->n_points;
1770 
1771  /* swap if needed */
1772  if (byte_order == ENDIAN_BIG) {
1773  int npoints, nitems;
1774 
1775  npoints = SWAP32(points[ring]->n_points);
1776  memcpy(wkb_data + 5, &npoints, 4);
1777 
1778  nitems = (with_z ? 3 : 2) * points[ring]->n_points;
1779  for (i = 0; i < nitems; i++) {
1780  SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
1781  }
1782  }
1783  }
1784 
1785  return wkb_data;
1786 }
1787 
1788 /*!
1789  \brief Write feature to WKB buffer
1790 
1791  Allocated string buffer should be freed by G_free().
1792 
1793  \param pg_info pointer to Format_info_pg struct
1794  \param points array of geometries which form feature
1795  \param nparts number of geometries in array
1796  \param type feature type (GV_POINT, GV_LINE, ...)
1797  \param with_z WITH_Z for 3D data
1798 
1799  \return allocated string buffer
1800  \return NULL on error
1801  */
1802 char *line_to_wkb(struct Format_info_pg *pg_info,
1803  const struct line_pnts **points, int nparts, int type,
1804  int with_z)
1805 {
1806  int byte_order, nbytes, nsize;
1807  unsigned int sf_type;
1808 
1809  unsigned char *wkb_data;
1810  char *text_data, *text_data_p, *hex_data;
1811 
1812  byte_order = dig__byte_order_out();
1813 
1814  /* get wkb data */
1815  nbytes = -1;
1816  wkb_data = NULL;
1817  if (type & GV_POINTS) /* point or centroid */
1818  wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
1819  else if (type == GV_LINE ||
1820  (type == GV_BOUNDARY && pg_info->feature_type == SF_LINESTRING))
1821  wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
1822  else if (type & (GV_BOUNDARY | GV_FACE | GV_AREA)) {
1823  if (!pg_info->toposchema_name || type == GV_AREA) {
1824  /* PostGIS simple feature access */
1825  wkb_data =
1826  polygon_to_wkb(byte_order, points, nparts, with_z, &nbytes);
1827  }
1828  else {
1829  /* PostGIS topology access */
1830  wkb_data =
1831  linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
1832  }
1833  }
1834 
1835  if (!wkb_data || nbytes < 1) {
1836  G_warning(_("Unsupported feature type %d"), type);
1837  return NULL;
1838  }
1839 
1840  /* When converting to hex, each byte takes 2 hex characters. In
1841  addition we add in 8 characters to represent the SRID integer
1842  in hex, and one for a null terminator */
1843  nsize = nbytes * 2 + 8 + 1;
1844  text_data = text_data_p = (char *)G_malloc(nsize);
1845 
1846  /* convert the 1st byte, which is the endianness flag, to hex */
1847  hex_data = binary_to_hex(1, wkb_data);
1848  strcpy(text_data_p, hex_data);
1849  G_free(hex_data);
1850  text_data_p += 2;
1851 
1852  /* get the geom type which is bytes 2 through 5 */
1853  memcpy(&sf_type, wkb_data + 1, 4);
1854 
1855  /* add the SRID flag if an SRID is provided */
1856  if (pg_info->srid > 0) {
1857  unsigned int srs_flag;
1858 
1859  /* change the flag to little endianness */
1860  srs_flag = LSBWORD32(WKBSRIDFLAG);
1861  /* apply the flag */
1862  sf_type = sf_type | srs_flag;
1863  }
1864 
1865  /* write the geom type which is 4 bytes */
1866  hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
1867  strcpy(text_data_p, hex_data);
1868  G_free(hex_data);
1869  text_data_p += 8;
1870 
1871  /* include SRID if provided */
1872  if (pg_info->srid > 0) {
1873  unsigned int srs_id;
1874 
1875  /* force the srsid to little endianness */
1876  srs_id = LSBWORD32(pg_info->srid);
1877  hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
1878  strcpy(text_data_p, hex_data);
1879  G_free(hex_data);
1880  text_data_p += 8;
1881  }
1882 
1883  /* copy the rest of the data over - subtract 5 since we already
1884  copied 5 bytes above */
1885  hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
1886  strcpy(text_data_p, hex_data);
1887  G_free(hex_data);
1888 
1889  return text_data;
1890 }
1891 
1892 /*!
1893  \brief Insert feature into table
1894 
1895  \param Map pointer to Map_info structure
1896  \param line feature id (topo access only)
1897  \param type feature type (GV_POINT, GV_LINE, ...)
1898  \param points pointer to line_pnts struct
1899  \param nparts number of parts (rings for polygon)
1900  \param cat category number (-1 for no category)
1901 
1902  \return topo_id for PostGIS Topology
1903  \return 0 for simple features access
1904  \return -1 on error
1905  */
1906 int write_feature(struct Map_info *Map, int line, int type,
1907  const struct line_pnts **points, int nparts, int cat)
1908 {
1909  int with_z, topo_id;
1910  char *stmt, *geom_data;
1911 
1912  struct Format_info_pg *pg_info;
1913 
1914  pg_info = &(Map->fInfo.pg);
1915  with_z = Map->head.with_z;
1916 
1917  if (with_z && pg_info->coor_dim != 3) {
1918  G_warning(_("Trying to insert 3D data into feature table "
1919  "which store 2D data only"));
1920  return -1;
1921  }
1922  if (!with_z && pg_info->coor_dim != 2) {
1923  G_warning(_("Trying to insert 2D data into feature table "
1924  "which store 3D data only"));
1925  return -1;
1926  }
1927 
1928  /* build WKB geometry from line_pnts structures */
1929  geom_data = line_to_wkb(pg_info, points, nparts, type, with_z);
1930  if (!geom_data)
1931  return -1;
1932 
1933  /* start transaction */
1934  if (!pg_info->inTransaction) {
1935  pg_info->inTransaction = TRUE;
1936  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
1937  G_free(geom_data);
1938  return -1;
1939  }
1940  }
1941 
1942  /* write feature in PostGIS topology schema if enabled */
1943  topo_id = -1;
1944  if (pg_info->toposchema_name) {
1945  /* insert feature into topology schema (node or edge) */
1946  topo_id = insert_topo_element(Map, line, type, geom_data);
1947  if (topo_id < 0) {
1948  G_warning(_("Unable to insert topological element into PostGIS "
1949  "Topology schema"));
1950  G_free(geom_data);
1951 
1952  return -1;
1953  }
1954 
1955  if (pg_info->feature_type != SF_POLYGON) {
1956  /* define relation */
1957  Vect__define_topo_relation(pg_info, topo_id, topo_id);
1958  }
1959  }
1960 
1961  /* build INSERT statement
1962  simple feature geometry + attributes
1963  */
1964  stmt = build_insert_stmt(pg_info, geom_data, topo_id, cat);
1965 
1966  /* stmt can NULL when writing PostGIS topology with no attributes
1967  * attached */
1968  if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
1969  /* rollback transaction */
1970  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1971  G_free(geom_data);
1972  G_free(stmt);
1973 
1974  return -1;
1975  }
1976  G_free(geom_data);
1977  G_free(stmt);
1978 
1979  return pg_info->toposchema_name ? topo_id : 0;
1980 }
1981 
1982 /*!
1983  \brief Build INSERT statement to add new feature to the feature
1984  table
1985 
1986  Note: Allocated string should be freed.
1987 
1988  \param pg_info pointer to Format_info_pg structure
1989  \param geom_data geometry data
1990  \param type feature type (GV_POINT, GV_LINE, ...) - (only for PostGIS
1991  Topology) \param id topology element id (only for PostGIS Topology) \param
1992  cat category number (or -1 for no category) \param Fi pointer to field_info
1993  structure (NULL for no attributes)
1994 
1995  \return allocated string with INSERT statement
1996  */
1997 char *build_insert_stmt(const struct Format_info_pg *pg_info,
1998  const char *geom_data, int topo_id, int cat)
1999 {
2000  int topogeom_type;
2001 
2002  char *stmt, buf[DB_SQL_MAX];
2003 
2004  struct field_info *Fi;
2005 
2006  topogeom_type = -1;
2007  if (pg_info->toposchema_name) {
2008  topogeom_type = type_to_topogeom(pg_info);
2009  if (topogeom_type < 0)
2010  return NULL;
2011  }
2012 
2013  Fi = pg_info->fi;
2014 
2015  stmt = NULL;
2016  if (Fi && cat > -1) {
2017  /* write attributes (simple features and topology elements) */
2018  int col, ncol, more;
2019  int sqltype, ctype, is_fid;
2020  char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
2021 
2022  const char *colname;
2023 
2024  dbString dbstmt;
2025  dbCursor cursor;
2026  dbTable *table;
2027  dbColumn *column;
2028  dbValue *value;
2029 
2030  db_init_string(&dbstmt);
2031  buf_val[0] = '\0';
2032 
2033  /* read & set attributes */
2034  snprintf(buf, sizeof(buf), "SELECT * FROM %s WHERE %s = %d", Fi->table,
2035  Fi->key, cat);
2036  G_debug(4, "SQL: %s", buf);
2037  db_set_string(&dbstmt, buf);
2038 
2039  /* prepare INSERT statement */
2040  snprintf(buf, sizeof(buf), "INSERT INTO \"%s\".\"%s\" (",
2041  pg_info->schema_name, pg_info->table_name);
2042 
2043  /* select data */
2044  if (db_open_select_cursor(pg_info->dbdriver, &dbstmt, &cursor,
2045  DB_SEQUENTIAL) != DB_OK) {
2046  G_warning(_("Unable to select attributes for category %d"), cat);
2047  }
2048  else {
2049  if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
2050  G_warning(_("Unable to fetch data from table <%s>"), Fi->table);
2051  }
2052 
2053  if (!more) {
2054  G_warning(_("No database record for category %d, "
2055  "no attributes will be written"),
2056  cat);
2057  }
2058  else {
2059  table = db_get_cursor_table(&cursor);
2061 
2062  for (col = 0; col < ncol; col++) {
2063  column = db_get_table_column(table, col);
2064  colname = db_get_column_name(column);
2065 
2066  /* -> values */
2067  value = db_get_column_value(column);
2068  /* for debug only */
2069  db_convert_column_value_to_string(column, &dbstmt);
2070  G_debug(3, "col %d : val = %s", col,
2071  db_get_string(&dbstmt));
2072 
2073  sqltype = db_get_column_sqltype(column);
2074  ctype = db_sqltype_to_Ctype(sqltype);
2075 
2076  is_fid = strcmp(pg_info->fid_column, colname) == 0;
2077 
2078  /* check fid column (must be integer) */
2079  if (is_fid == TRUE && ctype != DB_C_TYPE_INT) {
2080  G_warning(_("FID column must be integer, column <%s> "
2081  "ignored!"),
2082  colname);
2083  continue;
2084  }
2085 
2086  /* -> columns */
2087  snprintf(buf_tmp, sizeof(buf_tmp), "\"%s\"", colname);
2088  strcat(buf, buf_tmp);
2089  if (col < ncol - 1)
2090  strcat(buf, ",");
2091 
2092  /* prevent writing NULL values */
2093  if (!db_test_value_isnull(value)) {
2094  switch (ctype) {
2095  case DB_C_TYPE_INT:
2096  snprintf(buf_tmp, sizeof(buf_tmp), "%d",
2097  db_get_value_int(value));
2098  break;
2099  case DB_C_TYPE_DOUBLE:
2100  snprintf(buf_tmp, sizeof(buf_tmp), "%.14f",
2101  db_get_value_double(value));
2102  break;
2103  case DB_C_TYPE_STRING: {
2104  char *value_tmp;
2105 
2106  value_tmp = G_str_replace(
2107  db_get_value_string(value), "'", "''");
2108  snprintf(buf_tmp, sizeof(buf_tmp), "'%s'",
2109  value_tmp);
2110  G_free(value_tmp);
2111  break;
2112  }
2113  case DB_C_TYPE_DATETIME:
2114  db_convert_column_value_to_string(column, &dbstmt);
2115  snprintf(buf_tmp, sizeof(buf_tmp), "%s",
2116  db_get_string(&dbstmt));
2117  break;
2118  default:
2119  G_warning(_("Unsupported column type %d"), ctype);
2120  snprintf(buf_tmp, sizeof(buf_tmp), "NULL");
2121  break;
2122  }
2123  }
2124  else {
2125  if (is_fid == TRUE)
2126  G_warning(_("Invalid value for FID column: NULL"));
2127  snprintf(buf_tmp, sizeof(buf_tmp), "NULL");
2128  }
2129  strcat(buf_val, buf_tmp);
2130  if (col < ncol - 1)
2131  strcat(buf_val, ",");
2132  }
2133 
2134  if (!pg_info->toposchema_name) {
2135  /* simple feature access */
2136  G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)", buf,
2137  pg_info->geom_column, buf_val, geom_data);
2138  }
2139  else {
2140  /* PostGIS topology access, write geometry in
2141  * topology schema, skip geometry at this point */
2142  if (buf[strlen(buf) - 1] == ',') { /* last column skipped */
2143  buf[strlen(buf) - 1] = '\0';
2144  buf_val[strlen(buf_val) - 1] = '\0';
2145  }
2146  G_asprintf(&stmt,
2147  "%s, %s) VALUES (%s, '(%d, 1, %d, "
2148  "%d)'::topology.TopoGeometry)",
2149  buf, pg_info->topogeom_column, buf_val,
2150  pg_info->toposchema_id, topo_id, topogeom_type);
2151  }
2152  }
2153  }
2154  }
2155  else {
2156  /* no attributes */
2157  if (!pg_info->toposchema_name) {
2158  /* no attributes (simple features access) */
2159  if (cat > 0) {
2160  /* cetegory defined */
2161  G_asprintf(&stmt,
2162  "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
2163  "(%d, '%s'::GEOMETRY)",
2164  pg_info->schema_name, pg_info->table_name,
2165  GV_KEY_COLUMN, pg_info->geom_column, cat, geom_data);
2166  }
2167  else {
2168  /* no category */
2169  G_asprintf(&stmt,
2170  "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
2171  "('%s'::GEOMETRY)",
2172  pg_info->schema_name, pg_info->table_name,
2173  pg_info->geom_column, geom_data);
2174  }
2175  }
2176  else {
2177  if (cat > 0) {
2178  /* no attributes (topology elements) */
2179  G_asprintf(&stmt,
2180  "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
2181  "(%d, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
2182  pg_info->schema_name, pg_info->table_name,
2183  GV_KEY_COLUMN, pg_info->topogeom_column, cat,
2184  pg_info->toposchema_id, topo_id, topogeom_type);
2185  }
2186  }
2187  }
2188 
2189  return stmt;
2190 }
2191 
2192 /*!
2193  \brief Insert topological element into 'node' or 'edge' table
2194 
2195  Negative id for nodes, 0 for next value.
2196 
2197  \param Map pointer to Map_info struct
2198  \param id feature id (0 for next val)
2199  \param type feature type (GV_POINT, GV_LINE, ...)
2200  \param geom_data geometry in wkb
2201 
2202  \return new topo id
2203  \return -1 on error
2204  */
2205 int insert_topo_element(struct Map_info *Map, int id, int type,
2206  const char *geom_data)
2207 {
2208  int ret, topo_id;
2209  char *stmt, stmt_id[DB_SQL_MAX];
2210  struct Format_info_pg *pg_info;
2211  struct Plus_head *plus;
2212  struct P_line *Line;
2213 
2214  pg_info = &(Map->fInfo.pg);
2215  plus = &(Map->plus);
2216 
2217  Line = NULL;
2218  if (plus->built >= GV_BUILD_BASE) {
2219  if (id > 0) { /* -> feature */
2220  topo_id = id;
2221  if (topo_id > Map->plus.n_lines) {
2222  G_warning(_("Invalid feature %d (max: %d)"), topo_id,
2223  Map->plus.n_lines);
2224  return -1;
2225  }
2226  Line = Map->plus.Line[topo_id];
2227 
2228  if (Line->type & GV_POINTS) {
2229  /* set topo_id for points */
2230  topo_id = Vect_get_num_primitives(Map, GV_POINTS) +
2231  Vect_get_num_nodes(Map);
2232  }
2233  }
2234  else if (id < 0) { /* node */
2235  topo_id = abs(id);
2236  if (type != GV_POINT) {
2237  G_warning(_("Invalid feature type (%d) for node"), type);
2238  return -1;
2239  }
2240  if (topo_id > Map->plus.n_nodes) {
2241  G_warning(_("Invalid node %d (%d)"), topo_id,
2242  Map->plus.n_nodes);
2243  return -1;
2244  }
2245 
2246  /* increment topo_id - also points and centroids are
2247  * stored in 'node' table */
2248  topo_id += Vect_get_num_primitives(Map, GV_POINTS);
2249  }
2250  }
2251 
2252  stmt = NULL;
2253  switch (type) {
2254  case GV_POINT: {
2255  /* insert new node */
2256 #if USE_TOPO_STMT
2257  G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
2258  pg_info->toposchema_name, geom_data);
2259 #else
2260  if (id == 0) {
2261  /* get node_id */
2262  snprintf(stmt_id, sizeof(stmt_id),
2263  "SELECT nextval('\"%s\".node_node_id_seq')",
2264  pg_info->toposchema_name);
2265  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2266  }
2267 
2268  /* build insert statement */
2269  G_asprintf(&stmt,
2270  "INSERT INTO \"%s\".node (node_id, geom) VALUES "
2271  "(%d, '%s'::GEOMETRY)",
2272  pg_info->toposchema_name, topo_id, geom_data);
2273 #endif
2274  break;
2275  }
2276  case GV_LINE:
2277  case GV_BOUNDARY: {
2278  /* insert new edge */
2279 #if USE_TOPO_STMT
2280  G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
2281  pg_info->toposchema_name, geom_data);
2282 #else
2283  int n1, n2, nle, nre;
2284 
2285  struct Format_info_offset *offset;
2286 
2287  offset = &(pg_info->offset);
2288 
2289  if (id == 0) {
2290  /* get edge_id */
2291  snprintf(stmt_id, sizeof(stmt_id),
2292  "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
2293  pg_info->toposchema_name);
2294  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2295  }
2296 
2297  nle = -topo_id; /* assuming isolated lines */
2298  nre = topo_id;
2299 
2300  if (Line) {
2301  int i, n, next_edge;
2302  struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
2303 
2304  topo_id = (int)Line->offset;
2305  /* start & end node */
2306  n1 = topo->N1;
2307  n2 = topo->N2;
2308 
2309  /* next left & right edge */
2310  for (i = 0; i < 2; i++) {
2311  n = Vect_get_node_n_lines(Map, i == 0 ? n1 : n2);
2312  if (n < 2) /* no connection */
2313  continue;
2314 
2315  next_edge =
2316  update_next_edge(Map, n, i == 0 ? topo_id : -topo_id);
2317  if (next_edge != 0) {
2318  if (i == 0)
2319  nre = next_edge; /* update next right edge for start
2320  node */
2321  else
2322  nle =
2323  next_edge; /* update next left edge for end node */
2324  }
2325  else {
2326  G_warning(_("Unable to determine next left/right edge for "
2327  "edge %d"),
2328  topo_id);
2329  }
2330  }
2331  }
2332  else {
2333  G_warning(_("Unable to insert new edge. Topology not available."));
2334  return -1;
2335  }
2336 
2337  G_debug(3, "new edge: id=%d next_left_edge=%d next_right_edge=%d",
2338  topo_id, nle, nre);
2339 
2340  if (n1 > offset->array_num ||
2341  n2 > offset->array_num) /* node id starts at 1 */
2342  return -1;
2343 
2344  /* build insert statement */
2345  G_asprintf(
2346  &stmt,
2347  "INSERT INTO \"%s\".edge_data (edge_id, start_node, end_node, "
2348  "next_left_edge, abs_next_left_edge, next_right_edge, "
2349  "abs_next_right_edge, "
2350  "left_face, right_face, geom) VALUES "
2351  "(%d, %d, %d, %d, %d, %d, %d, 0, 0, '%s'::GEOMETRY)",
2352  pg_info->toposchema_name, topo_id, offset->array[n1 - 1],
2353  offset->array[n2 - 1], nle, abs(nle), nre, abs(nre), geom_data);
2354 #endif
2355  break;
2356  }
2357  case GV_CENTROID: {
2358  /* insert new node (with containing_face) */
2359 #if USE_TOPO_STMT
2360  G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
2361  pg_info->toposchema_name, geom_data);
2362 #else
2363  int area;
2364 
2365  if (id == 0) {
2366  /* get node_id */
2367  snprintf(stmt_id, sizeof(stmt_id),
2368  "SELECT nextval('\"%s\".node_node_id_seq')",
2369  pg_info->toposchema_name);
2370  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2371  }
2372 
2373  if (Line) {
2374  struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
2375 
2376  area = topo->area;
2377  }
2378  else {
2379  area = 0;
2380  }
2381 
2382  G_asprintf(
2383  &stmt,
2384  "INSERT INTO \"%s\".node (node_id, containing_face, geom) VALUES "
2385  "(%d, %d, '%s'::GEOMETRY)",
2386  pg_info->toposchema_name, topo_id, area, geom_data);
2387 #endif
2388  break;
2389  }
2390  default:
2391  G_warning(_("Unsupported feature type %d"), type);
2392  break;
2393  }
2394 
2395  /* execute insert statement */
2396  ret = Vect__execute_pg(pg_info->conn, stmt);
2397  G_free(stmt);
2398 
2399  if (ret == -1) {
2400  /* rollback transaction */
2401  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2402  return -1;
2403  }
2404 
2405  return topo_id;
2406 }
2407 
2408 int type_to_topogeom(const struct Format_info_pg *pg_info)
2409 {
2410  int topogeom_type;
2411 
2412  topogeom_type = -1;
2413  switch (pg_info->feature_type) {
2414  case SF_POINT:
2415  topogeom_type = 1;
2416  break;
2417  case SF_LINESTRING:
2418  topogeom_type = 2;
2419  break;
2420  case SF_POLYGON:
2421  topogeom_type = 3;
2422  break;
2423  default:
2424  G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
2425  }
2426 
2427  return topogeom_type;
2428 }
2429 
2430 int Vect__define_topo_relation(const struct Format_info_pg *pg_info,
2431  int topo_id, int element_id)
2432 {
2433  int topogeom_type;
2434  char stmt[DB_SQL_MAX];
2435 
2436  topogeom_type = type_to_topogeom(pg_info);
2437  if (topogeom_type < 0)
2438  return -1;
2439 
2440  snprintf(stmt, sizeof(stmt),
2441  "INSERT into \"%s\".relation VALUES(%d, 1, %d, %d)",
2442  pg_info->toposchema_name, topo_id, element_id, topogeom_type);
2443  G_debug(3, "SQL: %s", stmt);
2444 
2445  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2446  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2447  return -1;
2448  }
2449 
2450  return 0;
2451 }
2452 
2453 /*!
2454  \brief Find next line (topo only)
2455 
2456  \param Map pointer to Map_info struct
2457  \param nlines number of lines
2458  \param line current line
2459  \param[out] left left line
2460  \param[out] right right line
2461 
2462  \return left (line < 0) or right (line > 0) next edge
2463  \return 0 on failure
2464  */
2465 int update_next_edge(struct Map_info *Map, int nlines, int line)
2466 {
2467  int ret, next_line, edge;
2468  char stmt[DB_SQL_MAX];
2469 
2470  const struct Format_info_pg *pg_info;
2471  struct P_line *Line_next, *Line;
2472 
2473  Line = Line_next = NULL;
2474 
2475  pg_info = &(Map->fInfo.pg);
2476 
2477  /* find next line
2478  start node -> next on the left
2479  end node -> next on the right
2480  */
2481  next_line =
2482  dig_angle_next_line(&(Map->plus), line, GV_LEFT, GV_LINES, NULL);
2483  G_debug(3, "line=%d next_line=%d", line, next_line);
2484  if (next_line == 0) {
2485  G_warning(_("Invalid topology"));
2486  return 0;
2487  }
2488 
2489  Line = Map->plus.Line[abs(line)];
2490  Line_next = Map->plus.Line[abs(next_line)];
2491  if (!Line || !Line_next) {
2492  G_warning(_("Invalid topology"));
2493  return 0;
2494  }
2495 
2496  if (line > 0) {
2497  edge = Line->offset;
2498  ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
2499  }
2500  else {
2501  edge = -Line->offset;
2502  ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
2503  }
2504 
2505  if (next_line < 0) {
2506  snprintf(stmt, sizeof(stmt),
2507  "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
2508  "abs_next_left_edge = %d WHERE edge_id = %d AND "
2509  "abs_next_left_edge = %d",
2510  pg_info->toposchema_name, edge, abs(edge),
2511  (int)Line_next->offset, (int)Line_next->offset);
2512  G_debug(3, "update edge=%d next_left_edge=%d (?)",
2513  (int)Line_next->offset, edge);
2514  }
2515  else {
2516  snprintf(stmt, sizeof(stmt),
2517  "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
2518  "abs_next_right_edge = %d WHERE edge_id = %d AND "
2519  "abs_next_right_edge = %d",
2520  pg_info->toposchema_name, edge, abs(edge),
2521  (int)Line_next->offset, (int)Line_next->offset);
2522  G_debug(3, "update edge=%d next_right_edge=%d (?)",
2523  (int)Line_next->offset, edge);
2524  }
2525 
2526  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2527  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2528  return 0;
2529  }
2530 
2531  if (nlines > 2) {
2532  /* more lines connected to the node
2533 
2534  start node -> next on the right
2535  end node -> next on the left
2536  */
2537  next_line =
2538  dig_angle_next_line(&(Map->plus), line, GV_RIGHT, GV_LINES, NULL);
2539  Line_next = Map->plus.Line[abs(next_line)];
2540 
2541  if (next_line < 0) {
2542  snprintf(stmt, sizeof(stmt),
2543  "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
2544  "abs_next_left_edge = %d WHERE edge_id = %d",
2545  pg_info->toposchema_name, edge, abs(edge),
2546  (int)Line_next->offset);
2547  G_debug(3, "update edge=%d next_left_edge=%d",
2548  (int)Line_next->offset, edge);
2549  }
2550  else {
2551  snprintf(stmt, sizeof(stmt),
2552  "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
2553  "abs_next_right_edge = %d WHERE edge_id = %d",
2554  pg_info->toposchema_name, edge, abs(edge),
2555  (int)Line_next->offset);
2556  G_debug(3, "update edge=%d next_right_edge=%d",
2557  (int)Line_next->offset, edge);
2558  }
2559 
2560  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2561  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2562  return 0;
2563  }
2564  }
2565 
2566  return ret;
2567 }
2568 
2569 /*!
2570  \brief Insert new face to the 'face' table (topo only)
2571 
2572  \param Map pointer to Map_info struct
2573  \param area area id (negative id for isles)
2574 
2575  \return 0 on error
2576  \return area id on success (>0)
2577  */
2578 int Vect__insert_face_pg(struct Map_info *Map, int area)
2579 {
2580  char *stmt;
2581 
2582  struct Format_info_pg *pg_info;
2583  struct bound_box box;
2584 
2585  if (area == 0)
2586  return 0; /* universal face has id '0' in PostGIS Topology */
2587 
2588  stmt = NULL;
2589  pg_info = &(Map->fInfo.pg);
2590 
2591  /* check if face exists */
2592 
2593  /* get mbr of the area */
2594  if (area > 0)
2595  Vect_get_area_box(Map, area, &box);
2596  else
2597  Vect_get_isle_box(Map, abs(area), &box);
2598 
2599  /* insert face if not exists */
2600  G_asprintf(&stmt,
2601  "INSERT INTO \"%s\".face (face_id, mbr) VALUES "
2602  "(%d, ST_GeomFromText('POLYGON((%.12f %.12f, %.12f %.12f, %.12f "
2603  "%.12f, %.12f %.12f, "
2604  "%.12f %.12f))', %d))",
2605  pg_info->toposchema_name, area, box.W, box.S, box.W, box.N,
2606  box.E, box.N, box.E, box.S, box.W, box.S, pg_info->srid);
2607  G_debug(3, "new face id=%d", area);
2608  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2609  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2610  return 0;
2611  }
2612  G_free(stmt);
2613 
2614  return area;
2615 }
2616 
2617 #if 0 /* unused */
2618 /*!
2619  \brief Delete existing face (currently unused)
2620 
2621  \todo Set foreign keys as DEFERRABLE INITIALLY DEFERRED and use SET
2622  CONSTRAINTS ALL DEFERRED
2623 
2624  \param Map pointer to Map_info struct
2625  \param area area id to delete
2626 
2627  \return 0 on success
2628  \return -1 on error
2629  */
2630 int delete_face(struct Map_info *Map, int area)
2631 {
2632  char stmt[DB_SQL_MAX];
2633 
2634  const struct Format_info_pg *pg_info;
2635 
2636  pg_info = &(Map->fInfo.pg);
2637 
2638  /* update centroids first */
2639  snprintf(stmt, sizeof(stmt), "UPDATE \"%s\".node SET containing_face = 0 "
2640  "WHERE containing_face = %d", pg_info->toposchema_name, area);
2641  G_debug(3, "SQL: %s", stmt);
2642  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2643  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2644  return -1;
2645  }
2646 
2647  /* update also edges (left face) */
2648  snprintf(stmt, sizeof(stmt), "UPDATE \"%s\".edge_data SET left_face = 0 "
2649  "WHERE left_face = %d", pg_info->toposchema_name, area);
2650  G_debug(3, "SQL: %s", stmt);
2651  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2652  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2653  return -1;
2654  }
2655 
2656  /* update also edges (left face) */
2657  snprintf(stmt, sizeof(stmt), "UPDATE \"%s\".edge_data SET right_face = 0 "
2658  "WHERE right_face = %d", pg_info->toposchema_name, area);
2659  G_debug(3, "SQL: %s", stmt);
2660  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2661  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2662  return -1;
2663  }
2664 
2665  /* delete face */
2666  snprintf(stmt, sizeof(stmt), "DELETE FROM \"%s\".face WHERE face_id = %d",
2667  pg_info->toposchema_name, area);
2668  G_debug(3, "delete face id=%d", area);
2669  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2670  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2671  return -1;
2672  }
2673 
2674  return 0;
2675 }
2676 
2677 /*!
2678  \brief Update lines (next left and right edges)
2679 
2680  - isolated edges
2681  next left edge: -edge
2682  next right edge: edge
2683 
2684  - connected edges
2685  next left edge: next edge or -edge
2686  next right edge: next edge or edge
2687 
2688  \param Map pointer to Map_info struct
2689  \param line feature id
2690 
2691  \return 0 on success
2692  \return -1 on error
2693  */
2694 int update_topo_edge(struct Map_info *Map, int line)
2695 {
2696  int i, n;
2697  int nle, nre, next_edge;
2698  char stmt[DB_SQL_MAX];
2699 
2700  struct Format_info_pg *pg_info;
2701  struct P_line *Line;
2702 
2703  pg_info = &(Map->fInfo.pg);
2704 
2705  if (line < 1 || line > Map->plus.n_lines) {
2706  G_warning(_("Attempt to access non-existing feature %d"), line);
2707  return -1;
2708  }
2709  Line = Map->plus.Line[line];
2710  if (!Line) {
2711  G_warning(_("Attempt to access dead feature %d"), line);
2712  return -1;
2713  }
2714 
2715  struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
2716 
2717  nre = nle = 0; /* edge = 0 is an illegal value */
2718 
2719  /* check for line connection */
2720  for (i = 0; i < 2; i++) {
2721  /* first check start node then end node */
2722  n = i == 0 ? Vect_get_node_n_lines(Map, topo->N1)
2723  : Vect_get_node_n_lines(Map, topo->N2);
2724 
2725  if (n < 2) /* no connection */
2726  continue;
2727 
2728  next_edge = update_next_edge(Map, n, i == 0 ? line : -line);
2729  if (next_edge != 0) {
2730  if (i == 0)
2731  nre = next_edge; /* update next right edge for start node */
2732  else
2733  nle = next_edge; /* update next left edge for end node */
2734  }
2735  else {
2736  G_warning(_("Unable to determine next left/right edge"));
2737  return -1;
2738  }
2739  }
2740 
2741  if (nle == 0 && nre == 0) /* nothing changed */
2742  return 0;
2743 
2744  if (nle != 0 && nre != 0) {
2745  /* update both next left and right edge */
2746  snprintf(stmt, sizeof(stmt), "UPDATE \"%s\".edge_data SET "
2747  "next_left_edge = %d, abs_next_left_edge = %d, "
2748  "next_right_edge = %d, abs_next_right_edge = %d "
2749  "WHERE edge_id = %d", pg_info->toposchema_name,
2750  nle, abs(nle), nre, abs(nre), (int)Line->offset);
2751  }
2752  else if (nle != 0) {
2753  /* update next left edge only */
2754  snprintf(stmt, sizeof(stmt), "UPDATE \"%s\".edge_data SET "
2755  "next_left_edge = %d, abs_next_left_edge = %d "
2756  "WHERE edge_id = %d", pg_info->toposchema_name,
2757  nle, abs(nle), (int)Line->offset);
2758  }
2759  else {
2760  /* update next right edge only */
2761  snprintf(stmt, sizeof(stmt), "UPDATE \"%s\".edge_data SET "
2762  "next_right_edge = %d, abs_next_right_edge = %d "
2763  "WHERE edge_id = %d", pg_info->toposchema_name,
2764  nre, abs(nre), (int)Line->offset);
2765  }
2766  G_debug(3, "update edge=%d next_left_edge=%d next_right_edge=%d",
2767  (int)Line->offset, nle, nre);
2768 
2769  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2770  /* rollback transaction */
2771  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2772  return -1;
2773  }
2774 
2775  return 0;
2776 }
2777 #endif
2778 
2779 /*!
2780  \brief Update lines (left and right faces)
2781 
2782  TODO: handle isles
2783 
2784  \param Map pointer to Map_info struct
2785  \param line feature id
2786 
2787  \return 0 on success
2788  \return -1 on error
2789  */
2790 int update_topo_face(struct Map_info *Map, int line)
2791 {
2792  int i, s, area, face[2];
2793  char stmt[DB_SQL_MAX];
2794 
2795  struct Format_info_pg *pg_info;
2796  struct P_line *Line, *Line_i;
2797  struct P_area *Area;
2798  struct P_topo_b *topo, *topo_i;
2799 
2800  pg_info = &(Map->fInfo.pg);
2801 
2802  if (line < 1 || line > Map->plus.n_lines) {
2803  G_warning(_("Attempt to access non-existing feature %d"), line);
2804  return -1;
2805  }
2806  Line = Map->plus.Line[line];
2807  if (!Line) {
2808  G_warning(_("Attempt to access dead feature %d"), line);
2809  return -1;
2810  }
2811 
2812  topo = (struct P_topo_b *)Line->topo;
2813 
2814  /* for both side on the current boundary (line) */
2815  /* create new faces */
2816  for (s = 0; s < 2; s++) { /* for each side */
2817  area = s == 0 ? topo->left : topo->right;
2818  if (area <= 0) /* no area - skip */
2819  continue;
2820 
2821  face[s] = Vect__insert_face_pg(Map, area);
2822  if (face[s] < 1) {
2823  G_warning(_("Unable to create new face"));
2824  return -1;
2825  }
2826  }
2827 
2828  /* update edges forming faces */
2829  for (s = 0; s < 2; s++) { /* for each side */
2830  area = s == 0 ? topo->left : topo->right;
2831  if (area <= 0) /* no area - skip */
2832  continue;
2833 
2834  Area = Map->plus.Area[area];
2835  for (i = 0; i < Area->n_lines; i++) {
2836  Line_i = Map->plus.Line[abs(Area->lines[i])];
2837  topo_i = (struct P_topo_b *)Line_i->topo;
2838 
2839  snprintf(
2840  stmt, sizeof(stmt),
2841  "UPDATE \"%s\".edge_data SET "
2842  "left_face = %d, right_face = %d "
2843  "WHERE edge_id = %d",
2844  pg_info->toposchema_name, topo_i->left > 0 ? topo_i->left : 0,
2845  topo_i->right > 0 ? topo_i->right : 0, (int)Line_i->offset);
2846  G_debug(2, "SQL: %s", stmt);
2847 
2848  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2849  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2850  return -1;
2851  }
2852  }
2853 
2854  /* update also centroids (stored as nodes) */
2855  if (Area->centroid > 0) {
2856  Line_i = Map->plus.Line[Area->centroid];
2857  snprintf(stmt, sizeof(stmt),
2858  "UPDATE \"%s\".node SET containing_face = %d "
2859  "WHERE node_id = %d",
2860  pg_info->toposchema_name, face[s], (int)Line_i->offset);
2861  G_debug(2, "SQL: %s", stmt);
2862 
2863  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2864  /* rollback transaction */
2865  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2866  return -1;
2867  }
2868  }
2869  }
2870 
2871  return 0;
2872 }
2873 
2874 /*!
2875  \brief Add line to native and PostGIS topology
2876 
2877  \param Map vector map
2878  \param offset ???
2879  \param type feature type
2880  \param Points feature vertices
2881 
2882  \return feature id
2883  \return -1 on error
2884  */
2885 int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
2886  const struct line_pnts *points)
2887 {
2888  int line, n_nodes;
2889 
2890  struct Plus_head *plus;
2891 
2892  plus = &(Map->plus);
2893 
2894  Vect_reset_updated(Map);
2895  line = V2__add_line_to_topo_nat(Map, offset, type, points, NULL, -1, NULL);
2896 
2897  /* insert new nodes into 'node' table */
2899  if (n_nodes > 0) {
2900  int i, node;
2901  double x, y, z;
2902 
2903  if (!Points)
2904  Points = Vect_new_line_struct();
2905 
2906  for (i = 0; i < n_nodes; i++) {
2907  node = Vect_get_updated_node(Map, i);
2908  /* skip updated and deleted nodes */
2909  if (node > 0 || plus->Node[abs(node)] == NULL)
2910  continue;
2911 
2912  G_debug(3, " new node: %d", node);
2913 
2914  Vect_get_node_coor(Map, abs(node), &x, &y, &z);
2915  Vect_reset_line(Points);
2916  Vect_append_point(Points, x, y, z);
2917 
2918  write_feature(Map, node, GV_POINT,
2919  (const struct line_pnts **)&Points, 1, -1);
2920  }
2921  }
2922 
2923  return line;
2924 }
2925 
2926 /*!
2927  \brief Delete line from native and PostGIS topology
2928 
2929  \param Map vector map
2930  \param line feature id to remove from topo
2931  \param type feature type
2932  \param Points feature vertices
2933 
2934  \return 0 on success
2935  \return -1 on error
2936  */
2937 int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
2938  const struct line_pnts *Points)
2939 {
2940  int N1, N2, node_id;
2941  char stmt[DB_SQL_MAX];
2942 
2943  struct Format_info_pg *pg_info;
2944  struct P_node *Node;
2945 
2946  pg_info = &(Map->fInfo.pg);
2947 
2948  Vect_reset_updated(Map);
2949 
2950  if (!(type & GV_LINES))
2951  return 0;
2952 
2953  Vect_get_line_nodes(Map, line, &N1, &N2);
2954  if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
2955  return -1;
2956 
2957  Node = Map->plus.Node[N1];
2958  if (!Node || Node->n_lines == 0) {
2959  node_id = pg_info->offset.array[N1 - 1];
2960  snprintf(stmt, sizeof(stmt),
2961  "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
2962  pg_info->toposchema_name, node_id);
2963  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2964  G_warning(_("Unable to delete node %d"), node_id);
2965  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2966  return -1;
2967  }
2968  }
2969 
2970  Node = Map->plus.Node[N2];
2971  if (!Node || Node->n_lines == 0) {
2972  node_id = pg_info->offset.array[N2 - 1];
2973  snprintf(stmt, sizeof(stmt),
2974  "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
2975  pg_info->toposchema_name, node_id);
2976  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2977  G_warning(_("Unable to delete node %d"), node_id);
2978  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2979  return -1;
2980  }
2981  }
2982 
2983  return 0;
2984 }
2985 
2986 int set_constraint_to_deferrable(struct Format_info_pg *pg_info,
2987  const char *table, const char *constraint,
2988  const char *column, const char *ref_table,
2989  const char *ref_column)
2990 {
2991  char stmt[DB_SQL_MAX];
2992 
2993  snprintf(stmt, sizeof(stmt), "ALTER TABLE \"%s\".%s DROP CONSTRAINT %s",
2994  pg_info->toposchema_name, table, constraint);
2995  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
2996  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2997  return -1;
2998  }
2999 
3000  snprintf(stmt, sizeof(stmt),
3001  "ALTER TABLE \"%s\".%s ADD CONSTRAINT %s "
3002  "FOREIGN KEY (%s) REFERENCES \"%s\".%s (%s) "
3003  "DEFERRABLE INITIALLY DEFERRED",
3004  pg_info->toposchema_name, table, constraint, column,
3005  pg_info->toposchema_name, ref_table, ref_column);
3006  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
3007  Vect__execute_pg(pg_info->conn, "ROLLBACK");
3008  return -1;
3009  }
3010 
3011  return 0;
3012 }
3013 
3014 /*!
3015  \brief Open database connection with attribute table
3016 
3017  \param pg_info pointer to Format_info_pg struct
3018 
3019  \return pointer to dbDriver on success
3020  \return NULL on failure
3021  */
3022 dbDriver *open_db(struct Format_info_pg *pg_info)
3023 {
3024  dbDriver *driver;
3025  dbHandle handle;
3026 
3027  struct field_info *Fi;
3028 
3029  db_init_handle(&handle);
3030 
3031  Fi = pg_info->fi;
3032 
3033  pg_info->dbdriver = driver = db_start_driver(Fi->driver);
3034  if (!driver) {
3035  G_warning(_("Unable to start driver <%s>"), Fi->driver);
3036  return NULL;
3037  }
3038  db_set_handle(&handle, Fi->database, NULL);
3039  if (db_open_database(driver, &handle) != DB_OK) {
3040  G_warning(_("Unable to open database <%s> by driver <%s>"),
3041  Fi->database, Fi->driver);
3043  pg_info->dbdriver = NULL;
3044  return NULL;
3045  }
3046 
3047  return pg_info->dbdriver;
3048 }
3049 #endif
#define NULL
Definition: ccmath.h:32
#define DB_SQL_MAX
Definition: dbmi.h:142
#define DB_C_TYPE_INT
Definition: dbmi.h:108
#define DB_SEQUENTIAL
Definition: dbmi.h:123
#define DB_C_TYPE_STRING
Definition: dbmi.h:107
#define DB_C_TYPE_DOUBLE
Definition: dbmi.h:109
#define DB_OK
Definition: dbmi.h:71
#define DB_C_TYPE_DATETIME
Definition: dbmi.h:110
#define DB_SQL_TYPE_CHARACTER
Definition: dbmi.h:81
#define DB_NEXT
Definition: dbmi.h:114
const char * db_sqltype_name(int)
Get SQL data type description.
Definition: sqltype.c:25
int db_test_value_isnull(dbValue *)
Check of value is null.
Definition: value.c:26
const char * db_get_value_string(dbValue *)
Get string value.
Definition: value.c:92
int db_describe_table(dbDriver *, dbString *, dbTable **)
Describe table.
Definition: c_desc_table.c:28
dbValue * db_get_column_value(dbColumn *)
Returns column value for given column structure.
dbColumn * db_get_table_column(dbTable *, int)
Returns column structure for given table and column number.
int db_get_column_length(dbColumn *)
Get column's length.
double db_get_value_double(dbValue *)
Get double precision value.
Definition: value.c:50
int db_sqltype_to_Ctype(int)
Get C data type based on given SQL data type.
Definition: sqlCtype.c:24
dbDriver * db_start_driver(const char *)
Initialize a new dbDriver for db transaction.
Definition: start.c:51
int db_get_column_sqltype(dbColumn *)
Returns column sqltype for column.
int db_open_database(dbDriver *, dbHandle *)
Open database connection.
Definition: c_opendb.c:27
dbTable * db_get_cursor_table(dbCursor *)
Get table allocated by cursor.
Definition: cursor.c:67
int db_close_database_shutdown_driver(dbDriver *)
Close driver/database connection.
Definition: db.c:61
void db_free_string(dbString *)
Free allocated space for dbString.
Definition: string.c:150
int db_set_string(dbString *, const char *)
Inserts string to dbString (enlarge string)
Definition: string.c:41
int db_set_handle(dbHandle *, const char *, const char *)
Set handle (database and schema name)
Definition: handle.c:39
int db_get_value_int(dbValue *)
Get integer value.
Definition: value.c:38
void db_init_handle(dbHandle *)
Initialize handle (i.e database/schema)
Definition: handle.c:23
void db_init_string(dbString *)
Initialize dbString.
Definition: string.c:25
int db_open_select_cursor(dbDriver *, dbString *, dbCursor *, int)
Open select cursor.
Definition: c_openselect.c:37
const char * db_get_column_name(dbColumn *)
Returns column name for given column.
int db_convert_column_value_to_string(dbColumn *, dbString *)
?
Definition: columnfmt.c:62
char * db_get_string(const dbString *)
Get string.
Definition: string.c:140
int db_fetch(dbCursor *, int, int *)
Fetch data from open cursor.
Definition: c_fetch.c:28
int db_get_table_number_of_columns(dbTable *)
Return the number of columns of the table.
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
char * G_str_replace(const char *, const char *, const char *)
Replace all occurrences of old_str in buffer with new_str.
Definition: strings.c:189
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 void G_verbose_message(const char *,...) __attribute__((format(printf
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 G_asprintf(char **, const char *,...) __attribute__((format(printf
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition: strings.c:47
int G_debug(int, const char *,...) __attribute__((format(printf
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition: line.c:77
int Vect_get_line_nodes(struct Map_info *, int, int *, int *)
Get line nodes.
Definition: level_two.c:303
off_t V2_write_line_sfa(struct Map_info *, int, const struct line_pnts *, const struct line_cats *)
Writes feature on level 2 (OGR/PostGIS interface, pseudo-topological level)
Definition: write_sfa.c:52
int Vect_get_node_coor(struct Map_info *, int, double *, double *, double *)
Get node coordinates.
Definition: level_two.c:274
int Vect_get_updated_node(struct Map_info *, int)
Get updated (modified) node by index.
Definition: level_two.c:241
int V2_read_line_pg(struct Map_info *, struct line_pnts *, struct line_cats *, int)
int Vect_cat_get(const struct line_cats *, int, int *)
Get first found category of given field.
plus_t Vect_get_num_primitives(struct Map_info *, int)
Get number of primitives in vector map.
Definition: level_two.c:47
void Vect_reset_updated(struct Map_info *)
Reset list of updated lines/nodes.
Definition: level_two.c:469
int Vect_get_area_box(struct Map_info *, int, struct bound_box *)
Get bounding box of area.
int Vect_get_num_dblinks(struct Map_info *)
Get number of defined dblinks.
Definition: level_two.c:159
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition: line.c:45
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
struct field_info * Vect_get_dblink(struct Map_info *, int)
Get information about link to database.
Definition: field.c:475
int Vect_get_num_updated_nodes(struct Map_info *)
Get number of updated nodes.
Definition: level_two.c:219
int Vect_get_isle_box(struct Map_info *, int, struct bound_box *)
Get bounding box of isle.
int Vect_get_node_n_lines(struct Map_info *, int)
Get number of lines for node.
Definition: level_two.c:380
plus_t Vect_get_num_nodes(struct Map_info *)
Get number of nodes in vector map.
Definition: level_two.c:34
void Vect_reset_line(struct line_pnts *)
Reset line.
Definition: line.c:129
int V1_read_line_pg(struct Map_info *, struct line_pnts *, struct line_cats *, off_t)
int Vect_is_3d(struct Map_info *)
Check if vector map is 3D.
void Vect_set_updated(struct Map_info *, int)
Enable/disable maintenance of list of updated lines/nodes.
Definition: level_two.c:454
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition: line.c:148
int V2_delete_line_sfa(struct Map_info *, off_t)
Deletes feature on level 2 (OGR/PostGIS interface)
Definition: write_sfa.c:193
#define GV_CENTROID
Definition: dig_defines.h:186
SF_FeatureType
Simple feature types.
Definition: dig_defines.h:239
@ SF_POLYGON
Definition: dig_defines.h:244
@ SF_LINESTRING
Definition: dig_defines.h:242
@ SF_POLYGON25D
Definition: dig_defines.h:256
@ SF_GEOMETRY
Definition: dig_defines.h:240
@ SF_GEOMETRYCOLLECTION
Definition: dig_defines.h:250
@ SF_POINT25D
Definition: dig_defines.h:254
@ SF_POINT
Definition: dig_defines.h:241
@ SF_LINESTRING25D
Definition: dig_defines.h:255
#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_LINES
Definition: dig_defines.h:193
#define GV_BOUNDARY
Definition: dig_defines.h:185
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
Definition: dig_defines.h:125
#define WITH_Z
Definition: dig_defines.h:172
#define GV_BUILD_AREAS
Topology levels - build areas.
Definition: dig_defines.h:127
#define GV_FACE
Definition: dig_defines.h:187
#define LEVEL_2
Vector level - with 2D topology.
Definition: dig_defines.h:118
#define GV_RIGHT
Definition: dig_defines.h:176
#define GV_POINTS
Definition: dig_defines.h:192
#define GV_AREA
Definition: dig_defines.h:189
#define GV_LEFT
Boundary side indicator left/right.
Definition: dig_defines.h:175
#define GV_KERNEL
Definition: dig_defines.h:188
int dig_angle_next_line(struct Plus_head *, plus_t, int, int, float *)
Find line number of next angle to follow a line.
Definition: plus_area.c:474
int dig__byte_order_out(void)
Get byte order.
Definition: portable.c:1008
int dig_add_node(struct Plus_head *, double, double, double)
Add new node to plus structure.
Definition: plus_node.c:101
const struct driver * driver
Definition: driver/init.c:25
#define GV_KEY_COLUMN
Name of default key column.
Definition: gis.h:417
#define ENDIAN_LITTLE
Endian check.
Definition: gis.h:409
#define TRUE
Definition: gis.h:78
#define FALSE
Definition: gis.h:82
#define ENDIAN_BIG
Definition: gis.h:410
#define UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition: gis.h:46
#define _(str)
Definition: glocale.h:10
#define strcpy
Definition: parson.c:62
int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
Execute SQL statement and get value.
Definition: read_pg.c:1599
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
Definition: read_pg.c:1565
void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
Reallocate lines cache.
Definition: read_pg.c:1626
int ctype
Cache type.
Definition: dig_structs.h:496
int * lines_types
List of line types (GV_POINT, GV_LINE, ...)
Definition: dig_structs.h:466
struct line_pnts ** lines
Lines array.
Definition: dig_structs.h:462
int * lines_cats
List of line cats (used only for PostGIS Topology access)
Definition: dig_structs.h:470
Data structure used for building pseudo-topology.
Definition: dig_structs.h:388
int * array
Offset list.
Definition: dig_structs.h:436
int array_alloc
Space allocated for offset list.
Definition: dig_structs.h:444
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 * fid_column
FID column.
Definition: dig_structs.h:614
char * schema_name
Schema name.
Definition: dig_structs.h:602
dbDriver * dbdriver
Open DB driver when writing attributes.
Definition: dig_structs.h:639
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
int coor_dim
Coordinates dimension (2D or 3D)
Definition: dig_structs.h:626
char * conninfo
Connection string.
Definition: dig_structs.h:594
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
int srid
Spatial reference system id (see spatial_ref_sys table)
Definition: dig_structs.h:631
struct Format_info_offset offset
Offset list used for building pseudo-topology (simple features access)
Definition: dig_structs.h:676
struct field_info * fi
Definition: dig_structs.h:640
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:528
Vector map info.
Definition: dig_structs.h:1243
struct dig_head head
Header info.
Definition: dig_structs.h:1388
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_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
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
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
Centroid topology.
Definition: dig_structs.h:1514
plus_t area
Area number, negative for duplicate centroid.
Definition: dig_structs.h:1518
Line topology.
Definition: dig_structs.h:1478
plus_t N1
Start node.
Definition: dig_structs.h:1482
plus_t N2
End node.
Definition: dig_structs.h:1486
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
plus_t n_nodes
Current number of topological features derived from vector geometries.
Definition: dig_structs.h:923
struct P_area ** Area
Array of areas.
Definition: dig_structs.h:875
int cidx_up_to_date
Category index to be updated.
Definition: dig_structs.h:1140
int update_cidx
Update category index if vector is modified.
Definition: dig_structs.h:1120
struct P_node ** Node
Array of nodes.
Definition: dig_structs.h:867
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 S
South.
Definition: dig_structs.h:72
double N
North.
Definition: dig_structs.h:68
double E
East.
Definition: dig_structs.h:76
int with_z
2D/3D vector data
Definition: dig_structs.h:339
Definition: driver.h:27
Layer (old: field) information.
Definition: dig_structs.h:131
char * table
Name of DB table.
Definition: dig_structs.h:151
char * driver
Name of DB driver ('sqlite', 'dbf', ...)
Definition: dig_structs.h:143
char * database
Definition: dig_structs.h:147
char * key
Name of key column (usually 'cat')
Definition: dig_structs.h:155
int number
Layer number.
Definition: dig_structs.h:135
Feature category info.
Definition: dig_structs.h:1677
int * field
Array of layers (fields)
Definition: dig_structs.h:1681
int n_cats
Number of categories attached to element.
Definition: dig_structs.h:1689
Feature geometry info - coordinates.
Definition: dig_structs.h:1651
double * y
Array of Y coordinates.
Definition: dig_structs.h:1659
double * x
Array of X coordinates.
Definition: dig_structs.h:1655
int n_points
Number of points.
Definition: dig_structs.h:1667
double * z
Array of Z coordinates.
Definition: dig_structs.h:1663
Spatial index info.
Definition: dig_structs.h:1770
int V2__add_line_to_topo_nat(struct Map_info *Map, off_t offset, int type, const struct line_pnts *points, const struct line_cats *cats, int restore_line, int(*external_routine)(struct Map_info *, int))
Add feature (line) to topology (internal use only)
Definition: write_nat.c:907
int V2__delete_line_from_topo_nat(struct Map_info *Map, int line, int type, const struct line_pnts *points, const struct line_cats *cats)
Delete feature from topology (internal use only)
Definition: write_nat.c:656
off_t V1_rewrite_line_pg(struct Map_info *Map NOPG_UNUSED, off_t offset, int type, const struct line_pnts *points NOPG_UNUSED, const struct line_cats *cats NOPG_UNUSED)
Rewrites feature at the given offset (level 1) (PostGIS interface, internal use only)
Definition: write_pg.c:190
#define NOPG_UNUSED
Definition: write_pg.c:83
int Vect__define_topo_relation(const struct Format_info_pg *pg_info, int topo_id, int element_id)
Definition: write_pg.c:2430
off_t V2_rewrite_line_pg(struct Map_info *Map NOPG_UNUSED, off_t line, int type, const struct line_pnts *points NOPG_UNUSED, const struct line_cats *cats UNUSED)
Rewrites feature at topological level (PostGIS interface, internal use only)
Definition: write_pg.c:229
int V2_delete_line_pg(struct Map_info *Map NOPG_UNUSED, off_t line NOPG_UNUSED)
Deletes feature on topological level (PostGIS interface)
Definition: write_pg.c:387
int V1_delete_line_pg(struct Map_info *Map NOPG_UNUSED, off_t offset NOPG_UNUSED)
Deletes feature at the given offset (level 1)
Definition: write_pg.c:324
#define WKBSRIDFLAG
Definition: write_pg.c:33
off_t V1_write_line_pg(struct Map_info *Map NOPG_UNUSED, int type NOPG_UNUSED, const struct line_pnts *points NOPG_UNUSED, const struct line_cats *cats NOPG_UNUSED)
Writes feature on level 1 (PostGIS interface)
Definition: write_pg.c:109
off_t V2__write_area_pg(struct Map_info *Map, const struct line_pnts **points, int nparts, const struct line_cats *cats)
Writes area on topological level (PostGIS Simple Features interface, internal use only)
Definition: write_pg.c:540
int V2__update_area_pg(struct Map_info *Map, const struct line_pnts **points, int nparts, int cat)
Updates simple features geometry from GRASS-like topo.
Definition: write_pg.c:557
off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
Writes node on topological level (PostGIS Topology interface, internal use only)
Definition: write_pg.c:516
off_t V2_write_line_pg(struct Map_info *Map NOPG_UNUSED, int type NOPG_UNUSED, const struct line_pnts *points NOPG_UNUSED, const struct line_cats *cats NOPG_UNUSED)
Writes feature on topological level (PostGIS interface)
Definition: write_pg.c:152
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
#define TOPOGEOM_COLUMN
Definition: write_pg.c:35
#define x