GRASS GIS 8 Programmer's Manual  8.5.0dev(2024)-602118adcc
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  sprintf(stmt, "DELETE FROM %s WHERE %s = %ld", pg_info->table_name,
358  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  sprintf(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  sprintf(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  sprintf(stmt, "DELETE FROM \"%s\".\"%s\" WHERE %s_id = %d",
469  pg_info->toposchema_name, table_name, keycolumn,
470  (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  sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL%s, %s INTEGER",
658  pg_info->schema_name, pg_info->table_name, pg_info->fid_column,
659  primary_key ? " PRIMARY KEY" : "", GV_KEY_COLUMN);
660 
661  Fi = pg_info->fi;
662 
663  if (Fi) {
664  /* append attributes */
665  int col, ncols, sqltype, length;
666  char stmt_col[DB_SQL_MAX];
667  const char *colname;
668 
669  dbString dbtable_name;
670 
671  dbDriver *driver;
672  dbTable *table;
673  dbColumn *column;
674 
675  db_init_string(&dbtable_name);
676 
677  driver = open_db(pg_info);
678  if (driver == NULL)
679  return -1;
680 
681  /* describe table */
682  db_set_string(&dbtable_name, Fi->table);
683  if (db_describe_table(driver, &dbtable_name, &table) != DB_OK) {
684  G_warning(_("Unable to describe table <%s>"), Fi->table);
686  pg_info->dbdriver = NULL;
687  return -1;
688  }
689  ncols = db_get_table_number_of_columns(table);
690 
691  G_debug(3,
692  "copying attributes: driver = %s database = %s table = %s cols "
693  "= %d",
694  Fi->driver, Fi->database, Fi->table, ncols);
695 
696  for (col = 0; col < ncols; col++) {
697  column = db_get_table_column(table, col);
698  colname = db_get_column_name(column);
699  sqltype = db_get_column_sqltype(column);
700  length = db_get_column_length(column);
701 
702  G_debug(3, "\tcolumn = %d name = %s type = %d length = %d", col,
703  colname, sqltype, length);
704 
705  if (G_strcasecmp(pg_info->fid_column, colname) == 0 ||
706  G_strcasecmp(GV_KEY_COLUMN, colname) == 0) {
707  /* skip fid column if exists */
708  G_debug(3, "\t%s skipped", colname);
709  continue;
710  }
711 
712  /* append column */
713  sprintf(stmt_col, ",\"%s\" %s", colname, db_sqltype_name(sqltype));
714  strcat(stmt, stmt_col);
715  if (sqltype == DB_SQL_TYPE_CHARACTER) {
716  /* length only for string columns */
717  sprintf(stmt_col, "(%d)", length);
718  strcat(stmt, stmt_col);
719  }
720  }
721 
722  db_free_string(&dbtable_name);
723  }
724  strcat(stmt, ")"); /* close CREATE TABLE statement */
725 
726  /* begin transaction (create table) */
727  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
728  return -1;
729  }
730 
731  /* create table */
732  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
733  Vect__execute_pg(pg_info->conn, "ROLLBACK");
734  return -1;
735  }
736 
737  /* determine geometry type (string) */
738  switch (pg_info->feature_type) {
739  case (SF_POINT):
740  geom_type = "POINT";
741  break;
742  case (SF_LINESTRING):
743  geom_type = "LINESTRING";
744  break;
745  case (SF_POLYGON):
746  geom_type = "POLYGON";
747  break;
748  case (SF_POLYGON25D):
749  geom_type = "POLYGONZ";
750  break;
751  case (SF_GEOMETRY):
752  geom_type = "GEOMETRY";
753  break;
754  default:
755  G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
756  Vect__execute_pg(pg_info->conn, "ROLLBACK");
757  return -1;
758  }
759 
760  /* add geometry column */
761  sprintf(stmt,
762  "SELECT AddGeometryColumn('%s', '%s', "
763  "'%s', %d, '%s', %d)",
764  pg_info->schema_name, pg_info->table_name, pg_info->geom_column,
765  pg_info->srid, geom_type, pg_info->coor_dim);
766  G_debug(2, "SQL: %s", stmt);
767  result = PQexec(pg_info->conn, stmt);
768 
769  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
770  G_warning("%s", PQresultErrorMessage(result));
771  PQclear(result);
772  Vect__execute_pg(pg_info->conn, "ROLLBACK");
773  return -1;
774  }
775 
776  /* create indices
777  - GV_KEY_COLUMN
778  - geometry column
779  */
780  sprintf(stmt, "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" (%s)",
781  pg_info->table_name, GV_KEY_COLUMN, pg_info->schema_name,
782  pg_info->table_name, GV_KEY_COLUMN);
783  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
784  Vect__execute_pg(pg_info->conn, "ROLLBACK");
785  return -1;
786  }
787 
788  if (spatial_index) {
789  G_verbose_message(_("Building spatial index on <%s>..."),
790  pg_info->geom_column);
791  sprintf(stmt, "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" USING GIST (%s)",
792  pg_info->table_name, pg_info->geom_column, pg_info->schema_name,
793  pg_info->table_name, pg_info->geom_column);
794 
795  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
796  Vect__execute_pg(pg_info->conn, "ROLLBACK");
797  return -1;
798  }
799  }
800 
801  /* close transaction (create table) */
802  if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
803  return -1;
804  }
805 
806  return 0;
807 }
808 
809 /*!
810  \brief Creates new schema for feature table if not exists
811 
812  \param pg_info pointer to Format_info_pg
813 
814  \return -1 on error
815  \return 0 on success
816  */
817 int check_schema(const struct Format_info_pg *pg_info)
818 {
819  int i, found, nschema;
820  char stmt[DB_SQL_MAX];
821 
822  PGresult *result;
823 
824  if (!pg_info->conn || !pg_info->table_name) {
825  G_warning(_("No connection defined"));
826  return -1;
827  }
828 
829  /* add geometry column */
830  sprintf(stmt, "SELECT nspname FROM pg_namespace");
831  G_debug(2, "SQL: %s", stmt);
832  result = PQexec(pg_info->conn, stmt);
833 
834  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
835  PQclear(result);
836  Vect__execute_pg(pg_info->conn, "ROLLBACK");
837  return -1;
838  }
839 
840  found = FALSE;
841  nschema = PQntuples(result);
842  for (i = 0; i < nschema && !found; i++) {
843  if (strcmp(pg_info->schema_name, PQgetvalue(result, i, 0)) == 0)
844  found = TRUE;
845  }
846 
847  PQclear(result);
848 
849  if (!found) {
850  sprintf(stmt, "CREATE SCHEMA %s", pg_info->schema_name);
851  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
852  Vect__execute_pg(pg_info->conn, "ROLLBACK");
853  return -1;
854  }
855  G_warning(_("Schema <%s> doesn't exist, created"),
856  pg_info->schema_name);
857  }
858 
859  return 0;
860 }
861 
862 /*!
863  \brief Create new PostGIS topology schema
864 
865  - create topology schema
866  - add topology column to the feature table
867 
868  \todo Add constraints for grass-like tables
869 
870  \param pg_info pointer to Format_info_pg
871 
872  \return 0 on success
873  \return 1 topology disable, nothing to do
874  \return -1 on failure
875  */
876 int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
877 {
878  double tolerance;
879  char stmt[DB_SQL_MAX];
880  char *def_file;
881 
882  def_file = getenv("GRASS_VECTOR_PGFILE");
883 
884  /* read default values from PG file */
885  tolerance = 0.;
886  if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
887  FILE *fp;
888  const char *p;
889 
890  struct Key_Value *key_val;
891 
892  fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
893  if (!fp) {
894  G_fatal_error(_("Unable to open PG file"));
895  }
896  key_val = G_fread_key_value(fp);
897  fclose(fp);
898 
899  /* tolerance */
900  p = G_find_key_value("topo_tolerance", key_val);
901  if (p)
902  tolerance = atof(p);
903  G_debug(1, "PG: tolerance: %f", tolerance);
904 
905  /* topogeom column */
906  p = G_find_key_value("topogeom_name", key_val);
907  if (p)
908  pg_info->topogeom_column = G_store(p);
909  else
911  G_debug(1, "PG: topogeom_column :%s", pg_info->topogeom_column);
912 
913  /* topo-geo only (default: no) */
914  p = G_find_key_value("topo_geo_only", key_val);
915  if (p && G_strcasecmp(p, "yes") == 0)
916  pg_info->topo_geo_only = TRUE;
917  G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
918 
919  /* build simple features from topogeometry data */
920  p = G_find_key_value("simple_feature", key_val);
921  if (p && G_strcasecmp(p, "yes") == 0)
922  pg_info->topo_geo_only = TRUE;
923  G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
924 
925  G_free_key_value(key_val);
926  }
927 
928  /* begin transaction (create topo schema) */
929  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
930  return -1;
931  }
932 
933  /* create topology schema */
934  G_verbose_message(_("Creating topology schema <%s>..."),
935  pg_info->toposchema_name);
936  sprintf(stmt,
937  "SELECT topology.createtopology('%s', "
938  "find_srid('%s', '%s', '%s'), %f, '%s')",
939  pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
940  pg_info->geom_column, tolerance, with_z == WITH_Z ? "t" : "f");
941  pg_info->toposchema_id = Vect__execute_get_value_pg(pg_info->conn, stmt);
942  if (pg_info->toposchema_id == -1) {
943  Vect__execute_pg(pg_info->conn, "ROLLBACK");
944  return -1;
945  }
946 
947  /* add topo column to the feature table */
948  G_verbose_message(_("Adding new topology column <%s>..."),
949  pg_info->topogeom_column);
950  sprintf(stmt,
951  "SELECT topology.AddTopoGeometryColumn('%s', '%s', '%s', "
952  "'%s', '%s')",
953  pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
954  pg_info->topogeom_column, get_sftype(pg_info->feature_type));
955  if (-1 == Vect__execute_get_value_pg(pg_info->conn, stmt)) {
956  Vect__execute_pg(pg_info->conn, "ROLLBACK");
957  return -1;
958  }
959 
960  /* create index on topo column */
961  sprintf(stmt, "CREATE INDEX \"%s_%s_%s_idx\" ON \"%s\".\"%s\" (((%s).id))",
962  pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
963  pg_info->schema_name, pg_info->table_name,
964  pg_info->topogeom_column);
965  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
966  Vect__execute_pg(pg_info->conn, "ROLLBACK");
967  return -1;
968  }
969 
970  /* change constraints to deferrable initially deferred */
971  if (!pg_info->topo_geo_only) {
972  if (-1 == set_constraint_to_deferrable(pg_info, "node", "face_exists",
973  "containing_face", "face",
974  "face_id") ||
975  -1 == set_constraint_to_deferrable(pg_info, "edge_data",
976  "end_node_exists", "end_node",
977  "node", "node_id") ||
978  -1 == set_constraint_to_deferrable(pg_info, "edge_data",
979  "left_face_exists", "left_face",
980  "face", "face_id") ||
981  -1 == set_constraint_to_deferrable(
982  pg_info, "edge_data", "right_face_exists", "right_face",
983  "face", "face_id") ||
984  -1 == set_constraint_to_deferrable(pg_info, "edge_data",
985  "start_node_exists",
986  "start_node", "node", "node_id"))
987  return -1;
988  }
989 
990  /* create additional tables in topological schema to store
991  GRASS topology in DB */
992  if (!pg_info->topo_geo_only) {
993  /* (1) create 'node_grass' (see P_node struct)
994 
995  todo: add constraints for lines and angles
996  */
997  sprintf(stmt,
998  "CREATE TABLE \"%s\".%s (node_id SERIAL PRIMARY KEY, "
999  "lines integer[], angles float[])",
1000  pg_info->toposchema_name, TOPO_TABLE_NODE);
1001  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1002  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1003  return -1;
1004  }
1005 
1006  sprintf(stmt,
1007  "ALTER TABLE \"%s\".%s ADD CONSTRAINT node_exists "
1008  "FOREIGN KEY (node_id) REFERENCES \"%s\".node (node_id) "
1009  "DEFERRABLE INITIALLY DEFERRED",
1010  pg_info->toposchema_name, TOPO_TABLE_NODE,
1011  pg_info->toposchema_name);
1012  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1013  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1014  return -1;
1015  }
1016 
1017  /* (2) create 'line_grass' (see P_line struct)
1018 
1019  */
1020  sprintf(stmt,
1021  "CREATE TABLE \"%s\".%s (line_id SERIAL PRIMARY KEY, "
1022  "left_area integer, right_area integer)",
1023  pg_info->toposchema_name, TOPO_TABLE_LINE);
1024  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1025  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1026  return -1;
1027  }
1028 
1029  sprintf(stmt,
1030  "ALTER TABLE \"%s\".%s ADD CONSTRAINT line_exists "
1031  "FOREIGN KEY (line_id) REFERENCES \"%s\".edge_data (edge_id) "
1032  "DEFERRABLE INITIALLY DEFERRED",
1033  pg_info->toposchema_name, TOPO_TABLE_LINE,
1034  pg_info->toposchema_name);
1035  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1036  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1037  return -1;
1038  }
1039 
1040  /* (3) create 'area_grass' (see P_area struct)
1041 
1042  todo: add constraints for lines, centtroid and isles
1043  */
1044  sprintf(stmt,
1045  "CREATE TABLE \"%s\".%s (area_id SERIAL PRIMARY KEY, "
1046  "lines integer[], centroid integer, isles integer[])",
1047  pg_info->toposchema_name, TOPO_TABLE_AREA);
1048  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1049  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1050  return -1;
1051  }
1052 
1053  /* (4) create 'isle_grass' (see P_isle struct)
1054 
1055  todo: add constraints for lines and area
1056  */
1057  sprintf(stmt,
1058  "CREATE TABLE \"%s\".%s (isle_id SERIAL PRIMARY KEY, "
1059  "lines integer[], area integer)",
1060  pg_info->toposchema_name, TOPO_TABLE_ISLE);
1061  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1062  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1063  return -1;
1064  }
1065  }
1066 
1067  /* close transaction (create topo schema) */
1068  if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
1069  return -1;
1070  }
1071 
1072  return 0;
1073 }
1074 
1075 /*!
1076  \brief Create new PostGIS layer in given database (internal use only)
1077 
1078  V1_open_new_pg() must be called before this function.
1079 
1080  List of currently supported types:
1081  - GV_POINT (SF_POINT)
1082  - GV_LINE (SF_LINESTRING)
1083  - GV_BOUNDARY (SF_POLYGON)
1084 
1085  When PostGIS Topology the map level is updated to topological level
1086  and build level set to GV_BUILD_BASE.
1087 
1088  \param[in,out] Map pointer to Map_info structure
1089  \param type feature type (GV_POINT, GV_LINE, ...)
1090 
1091  \return 0 success
1092  \return -1 error
1093  */
1094 int create_pg_layer(struct Map_info *Map, int type)
1095 {
1096  int ndblinks;
1097 
1098  struct Format_info_pg *pg_info;
1099 
1100  pg_info = &(Map->fInfo.pg);
1101  if (!pg_info->conninfo) {
1102  G_warning(_("Connection string not defined"));
1103  return -1;
1104  }
1105 
1106  if (!pg_info->table_name) {
1107  G_warning(_("PostGIS feature table not defined"));
1108  return -1;
1109  }
1110 
1111  G_debug(1, "Vect__open_new_pg(): conninfo='%s' table='%s' -> type = %d",
1112  pg_info->conninfo, pg_info->table_name, type);
1113 
1114  /* determine geometry type */
1115 
1116  switch (type) {
1117  case GV_POINT:
1118  case GV_CENTROID:
1119  pg_info->feature_type = SF_POINT;
1120  break;
1121  case GV_LINE:
1122  case GV_BOUNDARY:
1123  pg_info->feature_type = SF_LINESTRING;
1124  break;
1125  case GV_AREA:
1126  pg_info->feature_type = SF_POLYGON;
1127  break;
1128  case GV_FACE:
1129  pg_info->feature_type = SF_POLYGON25D;
1130  break;
1131  case -2:
1132  pg_info->feature_type = SF_GEOMETRY;
1133  break;
1134  default:
1135  G_warning(_("Unsupported geometry type (%d)"), type);
1136  return -1;
1137  }
1138 
1139  /* coordinate dimension */
1140  pg_info->coor_dim = Vect_is_3d(Map) ? 3 : 2;
1141 
1142  /* create new PostGIS table */
1143  ndblinks = Vect_get_num_dblinks(Map);
1144  if (ndblinks > 0) {
1145  pg_info->fi = Vect_get_dblink(Map, 0); /* TODO: support more layers */
1146  if (pg_info->fi) {
1147  if (ndblinks > 1)
1148  G_warning(_("More layers defined, using driver <%s> and "
1149  "database <%s>"),
1150  pg_info->fi->driver, pg_info->fi->database);
1151  }
1152  else {
1153  G_warning(_("Database connection not defined. "
1154  "Unable to write attributes."));
1155  }
1156  }
1157 
1158  /* create new feature table */
1159  if (create_table(pg_info) == -1) {
1160  G_warning(_("Unable to create new PostGIS feature table"));
1161  return -1;
1162  }
1163 
1164  /* create new topology schema (if PostGIS topology support is enabled) */
1165  if (pg_info->toposchema_name) {
1166  /* force topological level */
1167  Map->level = LEVEL_2;
1168  Map->plus.built = GV_BUILD_BASE;
1169 
1170  /* track updated features, used in V2__add_line_to_topo_nat() */
1171  Vect_set_updated(Map, TRUE);
1172 
1173  if (create_topo_schema(pg_info, Vect_is_3d(Map)) == -1) {
1174  G_warning(_("Unable to create new PostGIS topology schema"));
1175  return -1;
1176  }
1177  }
1178 
1179  return 0;
1180 }
1181 
1182 /*!
1183  \brief Get simple feature type as a string
1184 
1185  Used for AddTopoGeometryColumn().
1186 
1187  Valid types:
1188  - SF_POINT
1189  - SF_LINESTRING
1190  - SF_POLYGON
1191 
1192  \return string with feature type
1193  \return empty string
1194  */
1195 char *get_sftype(SF_FeatureType sftype)
1196 {
1197  if (sftype == SF_POINT)
1198  return "POINT";
1199  else if (sftype == SF_LINESTRING)
1200  return "LINE";
1201  else if (sftype == SF_POLYGON)
1202  return "POLYGON";
1203  else if (sftype == SF_GEOMETRY || sftype == SF_GEOMETRYCOLLECTION)
1204  return "COLLECTION";
1205  else
1206  G_warning(_("Unsupported feature type %d"), sftype);
1207 
1208  return "";
1209 }
1210 
1211 /*!
1212  \brief Write vector features as PostGIS simple feature element
1213 
1214  \param Map pointer to Map_info structure
1215  \param type feature type (GV_POINT, GV_LINE, ...)
1216  \param points feature geometry (exterior + interior rings for polygonsx)
1217  \param nparts number of parts
1218  \param cats feature categories
1219 
1220  \return feature offset
1221  \return -1 on error
1222  */
1223 off_t write_line_sf(struct Map_info *Map, int type,
1224  const struct line_pnts **points, int nparts,
1225  const struct line_cats *cats)
1226 {
1227  int cat;
1228  off_t offset;
1229 
1230  SF_FeatureType sf_type;
1231 
1232  struct Format_info_pg *pg_info;
1233  struct Format_info_offset *offset_info;
1234 
1235  pg_info = &(Map->fInfo.pg);
1236  offset_info = &(pg_info->offset);
1237 
1238  if (nparts < 1)
1239  return -1;
1240 
1241  /* check required PG settings */
1242  if (!pg_info->conn) {
1243  G_warning(_("No connection defined"));
1244  return -1;
1245  }
1246  if (!pg_info->table_name) {
1247  G_warning(_("PostGIS feature table not defined"));
1248  return -1;
1249  }
1250 
1251  /* create PostGIS table if doesn't exist */
1252  if (pg_info->feature_type == SF_GEOMETRY) {
1253  if (create_pg_layer(Map, type) < 0)
1254  return -1;
1255  }
1256 
1257  /* get category & check for attributes */
1258  cat = -1;
1259  if (cats->n_cats > 0) {
1260  int field;
1261 
1262  if (pg_info->fi)
1263  field = pg_info->fi->number;
1264  else
1265  field = 1;
1266 
1267  if (!Vect_cat_get(cats, field, &cat))
1268  G_warning(_("No category defined for layer %d"), field);
1269  if (cats->n_cats > 1) {
1270  G_warning(_("Feature has more categories, using "
1271  "category %d (from layer %d)"),
1272  cat, field);
1273  }
1274  }
1275 
1276  sf_type = pg_info->feature_type;
1277 
1278  /* determine matching PostGIS feature geometry type */
1279  if (type & (GV_POINT | GV_KERNEL)) {
1280  if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
1281  G_warning(_("Point skipped (output feature type: %s)"),
1283  return 0;
1284  }
1285  }
1286  else if (type & GV_LINE) {
1287  if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
1288  G_warning(_("Line skipped (output feature type: %s)"),
1290  return 0;
1291  }
1292  }
1293  else if (type & GV_CENTROID) {
1294  if (sf_type != SF_POLYGON && sf_type != SF_POINT) {
1295  G_warning(_("Centroid skipped (output feature type: %s)"),
1297  return 0;
1298  }
1299  }
1300  else if (type & GV_BOUNDARY) {
1301  if (sf_type != SF_POLYGON && sf_type != SF_LINESTRING) {
1302  G_warning(_("Boundary skipped (output feature type: %s)"),
1304  return 0;
1305  }
1306  }
1307  else if (type & GV_FACE) {
1308  if (sf_type != SF_POLYGON25D) {
1309  G_warning(_("Face skipped (output feature type: %s)"),
1311  return 0;
1312  }
1313  }
1314  else {
1315  G_warning(_("Unsupported feature type %d"), type);
1316  return -1;
1317  }
1318 
1319  G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d", type,
1320  points[0]->n_points, cat);
1321 
1322  if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
1323  /* skip this check when writing PostGIS topology */
1324  int part, npoints;
1325 
1326  for (part = 0; part < nparts; part++) {
1327  npoints = points[part]->n_points - 1;
1328  if (points[part]->x[0] != points[part]->x[npoints] ||
1329  points[part]->y[0] != points[part]->y[npoints] ||
1330  points[part]->z[0] != points[part]->z[npoints]) {
1331  G_warning(_("Boundary is not closed. Skipping."));
1332  return -1;
1333  }
1334  }
1335  }
1336 
1337  /* write feature's geometry and fid */
1338  if (-1 == write_feature(Map, -1, type, points, nparts, cat)) {
1339  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1340  return -1;
1341  }
1342 
1343  /* update offset array */
1344  if (offset_info->array_num >= offset_info->array_alloc) {
1345  offset_info->array_alloc += 1000;
1346  offset_info->array = (int *)G_realloc(
1347  offset_info->array, offset_info->array_alloc * sizeof(int));
1348  }
1349  offset = offset_info->array_num;
1350 
1351  offset_info->array[offset_info->array_num++] = cat;
1352  if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
1353  /* register first part in offset array */
1354  offset_info->array[offset_info->array_num++] = 0;
1355  }
1356  G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
1357  (unsigned long)offset, offset_info->array_num, cat);
1358 
1359  return offset;
1360 }
1361 
1362 /*!
1363  \brief Write vector feature in PostGIS topology schema and
1364  updates internal topology structures
1365 
1366  \param Map vector map
1367  \param type feature type to be written
1368  \param points feature geometry
1369  \param is_node TRUE for nodes (written as points)
1370 
1371  \return feature id (build level >= GV_BUILD_BASE otherwise 0)
1372  \return 0 for nodes
1373  \return -1 on error
1374  */
1375 off_t write_line_tp(struct Map_info *Map, int type, int is_node,
1376  const struct line_pnts *points,
1377  const struct line_cats *cats)
1378 {
1379  int line, cat, line_id;
1380 
1381  struct Format_info_pg *pg_info;
1382  struct Plus_head *plus;
1383  struct field_info *Fi;
1384 
1385  pg_info = &(Map->fInfo.pg);
1386  plus = &(Map->plus);
1387 
1388  if (!(plus->update_cidx)) {
1389  plus->cidx_up_to_date = FALSE; /* category index will be outdated */
1390  }
1391 
1392  /* check type for nodes */
1393  if (is_node && type != GV_POINT) {
1394  G_warning(_("Invalid feature type (%d) for nodes"), type);
1395  return -1;
1396  }
1397 
1398  /* check required PG settings */
1399  if (!pg_info->conn) {
1400  G_warning(_("No connection defined"));
1401  return -1;
1402  }
1403  if (!pg_info->table_name) {
1404  G_warning(_("PostGIS feature table not defined"));
1405  return -1;
1406  }
1407  if (!pg_info->toposchema_name) {
1408  G_warning(_("PostGIS topology schema not defined"));
1409  return -1;
1410  }
1411 
1412  /* create PostGIS table if doesn't exist */
1413  if (pg_info->feature_type == SF_GEOMETRY) {
1414  if (create_pg_layer(Map, type) < 0)
1415  return -1;
1416  }
1417 
1418  if (!points)
1419  return 0;
1420 
1421  G_debug(3, "write_line_pg(): type = %d n_points = %d", type,
1422  points->n_points);
1423 
1424  Fi = pg_info->fi;
1425 
1426  cat = -1;
1427  if (cats && cats->n_cats > 0) {
1428  if (Fi) {
1429  if (!pg_info->dbdriver)
1430  open_db(pg_info);
1431  if (!Vect_cat_get(cats, Fi->number, &cat))
1432  G_warning(_("No category defined for layer %d"), Fi->number);
1433  if (cats->n_cats > 1) {
1434  G_warning(_("Feature has more categories, using "
1435  "category %d (from layer %d)"),
1436  cat, cats->field[0]);
1437  }
1438  }
1439  /* assume layer=1 */
1440  Vect_cat_get(cats, 1, &cat);
1441  }
1442 
1443  /* update GRASS topology before writing PostGIS feature */
1444  line = 0;
1445  if (plus->built >= GV_BUILD_BASE) {
1446  if (is_node) {
1447  /* nodes are given with negative id */
1448  line = -1 *
1449  dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
1450  }
1451  else {
1452  off_t offset;
1453 
1454  /* better is probably to check nextval directly */
1455  if (type & GV_POINTS) {
1456  offset = Vect_get_num_primitives(Map, GV_POINTS) + 1; /* next */
1457  offset += Vect_get_num_nodes(
1458  Map); /* nodes are also stored in 'node' table */
1459  }
1460  else { /* LINES */
1461  offset = Vect_get_num_primitives(Map, GV_LINES) + 1; /* next */
1462  }
1463 
1464  line = add_line_to_topo_pg(Map, offset, type, points);
1465  }
1466  }
1467 
1468  /* write new feature to PostGIS
1469  - feature table for simple features
1470  - feature table and topo schema for topological access
1471  */
1472  line_id = write_feature(Map, line, type, &points, 1, cat);
1473  if (line_id < 0) {
1474  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1475  return -1;
1476  }
1477 
1478  if (pg_info->cache.ctype == CACHE_MAP) {
1479  /* add line to the cache */
1480  Vect__reallocate_cache(&(pg_info->cache), 1, TRUE);
1481  pg_info->cache.lines[line - 1] = Vect_new_line_struct();
1482  pg_info->cache.lines_types[line - 1] = type;
1483  pg_info->cache.lines_cats[line - 1] = cat;
1484  }
1485 
1486  /* update offset array for nodes */
1487  if (is_node) {
1488  int node;
1489 
1490  struct Format_info_offset *offset;
1491 
1492  offset = &(pg_info->offset);
1493 
1494  node = abs(line);
1495  if (node > offset->array_alloc) {
1496  offset->array_alloc += 1000;
1497  offset->array = (int *)G_realloc(offset->array,
1498  offset->array_alloc * sizeof(int));
1499  }
1500 
1501  offset->array_num = node;
1502  offset->array[node - 1] = (int)line_id; /* node id starts at 1 */
1503  }
1504 
1505  /* update PostGIS-line topo */
1506  if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
1507  update_topo_face(Map, line); /* TODO: avoid extra statements */
1508 
1509  return !is_node ? line : 0;
1510 }
1511 
1512 /*!
1513  \brief Binary data to HEX
1514 
1515  Allocated buffer should be freed by G_free().
1516 
1517  \param nbytes number of bytes to allocate
1518  \param wkb_data WKB data
1519 
1520  \return allocated buffer with HEX data
1521  */
1522 char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
1523 {
1524  char *hex_data;
1525  int i, nlow, nhigh;
1526  static const char ach_hex[] = "0123456789ABCDEF";
1527 
1528  hex_data = (char *)G_malloc(nbytes * 2 + 1);
1529  hex_data[nbytes * 2] = '\0';
1530 
1531  for (i = 0; i < nbytes; i++) {
1532  nlow = wkb_data[i] & 0x0f;
1533  nhigh = (wkb_data[i] & 0xf0) >> 4;
1534 
1535  hex_data[i * 2] = ach_hex[nhigh];
1536  hex_data[i * 2 + 1] = ach_hex[nlow];
1537  }
1538 
1539  return hex_data;
1540 }
1541 
1542 /*!
1543  \brief Write point into WKB buffer
1544 
1545  See OGRPoint::exportToWkb from GDAL/OGR library
1546 
1547  \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
1548  \param points feature geometry
1549  \param with_z WITH_Z for 3D data
1550  \param[out] nsize buffer size
1551 
1552  \return allocated WKB buffer
1553  \return NULL on error
1554  */
1555 unsigned char *point_to_wkb(int byte_order, const struct line_pnts *points,
1556  int with_z, int *nsize)
1557 {
1558  unsigned char *wkb_data;
1559  unsigned int sf_type;
1560 
1561  if (points->n_points != 1)
1562  return NULL;
1563 
1564  /* allocate buffer */
1565  *nsize = with_z ? 29 : 21;
1566  wkb_data = G_malloc(*nsize);
1567  G_zero(wkb_data, *nsize);
1568 
1569  G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
1570 
1571  /* set the byte order */
1572  if (byte_order == ENDIAN_LITTLE)
1573  wkb_data[0] = '\001';
1574  else
1575  wkb_data[0] = '\000';
1576 
1577  /* set the geometry feature type */
1578  sf_type = with_z ? SF_POINT25D : SF_POINT;
1579 
1580  if (byte_order == ENDIAN_LITTLE)
1581  sf_type = LSBWORD32(sf_type);
1582  else
1583  sf_type = MSBWORD32(sf_type);
1584  memcpy(wkb_data + 1, &sf_type, 4);
1585 
1586  /* copy in the raw data */
1587  memcpy(wkb_data + 5, &(points->x[0]), 8);
1588  memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
1589 
1590  if (with_z) {
1591  memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
1592  }
1593 
1594  /* swap if needed */
1595  if (byte_order == ENDIAN_BIG) {
1596  SWAPDOUBLE(wkb_data + 5);
1597  SWAPDOUBLE(wkb_data + 5 + 8);
1598 
1599  if (with_z)
1600  SWAPDOUBLE(wkb_data + 5 + 16);
1601  }
1602 
1603  return wkb_data;
1604 }
1605 
1606 /*!
1607  \bried Write linestring into WKB buffer
1608 
1609  See OGRLineString::exportToWkb from GDAL/OGR library
1610 
1611  \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
1612  \param points feature geometry
1613  \param with_z WITH_Z for 3D data
1614  \param[out] nsize buffer size
1615 
1616  \return allocated WKB buffer
1617  \return NULL on error
1618  */
1619 unsigned char *linestring_to_wkb(int byte_order, const struct line_pnts *points,
1620  int with_z, int *nsize)
1621 {
1622  int i, point_size;
1623  unsigned char *wkb_data;
1624  unsigned int sf_type;
1625 
1626  if (points->n_points < 1)
1627  return NULL;
1628 
1629  /* allocate buffer */
1630  point_size = 8 * (with_z ? 3 : 2);
1631  *nsize = 5 + 4 + points->n_points * point_size;
1632  wkb_data = G_malloc(*nsize);
1633  G_zero(wkb_data, *nsize);
1634 
1635  G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
1636 
1637  /* set the byte order */
1638  if (byte_order == ENDIAN_LITTLE)
1639  wkb_data[0] = '\001';
1640  else
1641  wkb_data[0] = '\000';
1642 
1643  /* set the geometry feature type */
1644  sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
1645 
1646  if (byte_order == ENDIAN_LITTLE)
1647  sf_type = LSBWORD32(sf_type);
1648  else
1649  sf_type = MSBWORD32(sf_type);
1650  memcpy(wkb_data + 1, &sf_type, 4);
1651 
1652  /* copy in the data count */
1653  memcpy(wkb_data + 5, &(points->n_points), 4);
1654 
1655  /* copy in the raw data */
1656  for (i = 0; i < points->n_points; i++) {
1657  memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
1658  memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
1659 
1660  if (with_z) {
1661  memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
1662  }
1663  }
1664 
1665  /* swap if needed */
1666  if (byte_order == ENDIAN_BIG) {
1667  int npoints, nitems;
1668 
1669  npoints = SWAP32(points->n_points);
1670  memcpy(wkb_data + 5, &npoints, 4);
1671 
1672  nitems = (with_z ? 3 : 2) * points->n_points;
1673  for (i = 0; i < nitems; i++) {
1674  SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
1675  }
1676  }
1677 
1678  return wkb_data;
1679 }
1680 
1681 /*!
1682  \bried Write polygon into WKB buffer
1683 
1684  See OGRPolygon::exportToWkb from GDAL/OGR library
1685 
1686  \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
1687  \param ipoints list of ring geometries (first is outer ring)
1688  \param nrings number of rings
1689  \param with_z WITH_Z for 3D data
1690  \param[out] nsize buffer size
1691 
1692  \return allocated WKB buffer
1693  \return NULL on error
1694  */
1695 unsigned char *polygon_to_wkb(int byte_order, const struct line_pnts **points,
1696  int nrings, int with_z, int *nsize)
1697 {
1698  int i, ring, point_size, offset;
1699  unsigned char *wkb_data;
1700  unsigned int sf_type;
1701 
1702  /* check data validity */
1703  if (nrings < 1)
1704  return NULL;
1705  for (ring = 0; ring < nrings; ring++) {
1706  if (points[ring]->n_points < 3)
1707  return NULL;
1708  }
1709 
1710  /* allocate buffer */
1711  point_size = 8 * (with_z ? 3 : 2);
1712  *nsize = 9;
1713  for (ring = 0; ring < nrings; ring++)
1714  *nsize += 4 + point_size * points[ring]->n_points;
1715  wkb_data = G_malloc(*nsize);
1716  G_zero(wkb_data, *nsize);
1717 
1718  G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
1719 
1720  /* set the byte order */
1721  if (byte_order == ENDIAN_LITTLE)
1722  wkb_data[0] = '\001';
1723  else
1724  wkb_data[0] = '\000';
1725 
1726  /* set the geometry feature type */
1727  sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
1728 
1729  if (byte_order == ENDIAN_LITTLE)
1730  sf_type = LSBWORD32(sf_type);
1731  else
1732  sf_type = MSBWORD32(sf_type);
1733  memcpy(wkb_data + 1, &sf_type, 4);
1734 
1735  /* copy in the raw data */
1736  if (byte_order == ENDIAN_BIG) {
1737  int ncount;
1738 
1739  ncount = SWAP32(nrings);
1740  memcpy(wkb_data + 5, &ncount, 4);
1741  }
1742  else {
1743  memcpy(wkb_data + 5, &nrings, 4);
1744  }
1745 
1746  /* serialize rings */
1747  offset = 9;
1748  for (ring = 0; ring < nrings; ring++) {
1749  memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
1750  for (i = 0; i < points[ring]->n_points; i++) {
1751  memcpy(wkb_data + offset + 4 + point_size * i,
1752  &(points[ring]->x[i]), 8);
1753  memcpy(wkb_data + offset + 4 + 8 + point_size * i,
1754  &(points[ring]->y[i]), 8);
1755 
1756  if (with_z) {
1757  memcpy(wkb_data + offset + 4 + 16 + point_size * i,
1758  &(points[ring]->z[i]), 8);
1759  }
1760  }
1761 
1762  offset += 4 + point_size * points[ring]->n_points;
1763 
1764  /* swap if needed */
1765  if (byte_order == ENDIAN_BIG) {
1766  int npoints, nitems;
1767 
1768  npoints = SWAP32(points[ring]->n_points);
1769  memcpy(wkb_data + 5, &npoints, 4);
1770 
1771  nitems = (with_z ? 3 : 2) * points[ring]->n_points;
1772  for (i = 0; i < nitems; i++) {
1773  SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
1774  }
1775  }
1776  }
1777 
1778  return wkb_data;
1779 }
1780 
1781 /*!
1782  \brief Write feature to WKB buffer
1783 
1784  Allocated string buffer should be freed by G_free().
1785 
1786  \param pg_info pointer to Format_info_pg struct
1787  \param points array of geometries which form feature
1788  \param nparts number of geometries in array
1789  \param type feature type (GV_POINT, GV_LINE, ...)
1790  \param with_z WITH_Z for 3D data
1791 
1792  \return allocated string buffer
1793  \return NULL on error
1794  */
1795 char *line_to_wkb(struct Format_info_pg *pg_info,
1796  const struct line_pnts **points, int nparts, int type,
1797  int with_z)
1798 {
1799  int byte_order, nbytes, nsize;
1800  unsigned int sf_type;
1801 
1802  unsigned char *wkb_data;
1803  char *text_data, *text_data_p, *hex_data;
1804 
1805  byte_order = dig__byte_order_out();
1806 
1807  /* get wkb data */
1808  nbytes = -1;
1809  wkb_data = NULL;
1810  if (type & GV_POINTS) /* point or centroid */
1811  wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
1812  else if (type == GV_LINE ||
1813  (type == GV_BOUNDARY && pg_info->feature_type == SF_LINESTRING))
1814  wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
1815  else if (type & (GV_BOUNDARY | GV_FACE | GV_AREA)) {
1816  if (!pg_info->toposchema_name || type == GV_AREA) {
1817  /* PostGIS simple feature access */
1818  wkb_data =
1819  polygon_to_wkb(byte_order, points, nparts, with_z, &nbytes);
1820  }
1821  else {
1822  /* PostGIS topology access */
1823  wkb_data =
1824  linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
1825  }
1826  }
1827 
1828  if (!wkb_data || nbytes < 1) {
1829  G_warning(_("Unsupported feature type %d"), type);
1830  return NULL;
1831  }
1832 
1833  /* When converting to hex, each byte takes 2 hex characters. In
1834  addition we add in 8 characters to represent the SRID integer
1835  in hex, and one for a null terminator */
1836  nsize = nbytes * 2 + 8 + 1;
1837  text_data = text_data_p = (char *)G_malloc(nsize);
1838 
1839  /* convert the 1st byte, which is the endianness flag, to hex */
1840  hex_data = binary_to_hex(1, wkb_data);
1841  strcpy(text_data_p, hex_data);
1842  G_free(hex_data);
1843  text_data_p += 2;
1844 
1845  /* get the geom type which is bytes 2 through 5 */
1846  memcpy(&sf_type, wkb_data + 1, 4);
1847 
1848  /* add the SRID flag if an SRID is provided */
1849  if (pg_info->srid > 0) {
1850  unsigned int srs_flag;
1851 
1852  /* change the flag to little endianness */
1853  srs_flag = LSBWORD32(WKBSRIDFLAG);
1854  /* apply the flag */
1855  sf_type = sf_type | srs_flag;
1856  }
1857 
1858  /* write the geom type which is 4 bytes */
1859  hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
1860  strcpy(text_data_p, hex_data);
1861  G_free(hex_data);
1862  text_data_p += 8;
1863 
1864  /* include SRID if provided */
1865  if (pg_info->srid > 0) {
1866  unsigned int srs_id;
1867 
1868  /* force the srsid to little endianness */
1869  srs_id = LSBWORD32(pg_info->srid);
1870  hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
1871  strcpy(text_data_p, hex_data);
1872  G_free(hex_data);
1873  text_data_p += 8;
1874  }
1875 
1876  /* copy the rest of the data over - subtract 5 since we already
1877  copied 5 bytes above */
1878  hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
1879  strcpy(text_data_p, hex_data);
1880  G_free(hex_data);
1881 
1882  return text_data;
1883 }
1884 
1885 /*!
1886  \brief Insert feature into table
1887 
1888  \param Map pointer to Map_info structure
1889  \param line feature id (topo access only)
1890  \param type feature type (GV_POINT, GV_LINE, ...)
1891  \param points pointer to line_pnts struct
1892  \param nparts number of parts (rings for polygon)
1893  \param cat category number (-1 for no category)
1894 
1895  \return topo_id for PostGIS Topology
1896  \return 0 for simple features access
1897  \return -1 on error
1898  */
1899 int write_feature(struct Map_info *Map, int line, int type,
1900  const struct line_pnts **points, int nparts, int cat)
1901 {
1902  int with_z, topo_id;
1903  char *stmt, *geom_data;
1904 
1905  struct Format_info_pg *pg_info;
1906 
1907  pg_info = &(Map->fInfo.pg);
1908  with_z = Map->head.with_z;
1909 
1910  if (with_z && pg_info->coor_dim != 3) {
1911  G_warning(_("Trying to insert 3D data into feature table "
1912  "which store 2D data only"));
1913  return -1;
1914  }
1915  if (!with_z && pg_info->coor_dim != 2) {
1916  G_warning(_("Trying to insert 2D data into feature table "
1917  "which store 3D data only"));
1918  return -1;
1919  }
1920 
1921  /* build WKB geometry from line_pnts structures */
1922  geom_data = line_to_wkb(pg_info, points, nparts, type, with_z);
1923  if (!geom_data)
1924  return -1;
1925 
1926  /* start transaction */
1927  if (!pg_info->inTransaction) {
1928  pg_info->inTransaction = TRUE;
1929  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
1930  G_free(geom_data);
1931  return -1;
1932  }
1933  }
1934 
1935  /* write feature in PostGIS topology schema if enabled */
1936  topo_id = -1;
1937  if (pg_info->toposchema_name) {
1938  /* insert feature into topology schema (node or edge) */
1939  topo_id = insert_topo_element(Map, line, type, geom_data);
1940  if (topo_id < 0) {
1941  G_warning(_("Unable to insert topological element into PostGIS "
1942  "Topology schema"));
1943  G_free(geom_data);
1944 
1945  return -1;
1946  }
1947 
1948  if (pg_info->feature_type != SF_POLYGON) {
1949  /* define relation */
1950  Vect__define_topo_relation(pg_info, topo_id, topo_id);
1951  }
1952  }
1953 
1954  /* build INSERT statement
1955  simple feature geometry + attributes
1956  */
1957  stmt = build_insert_stmt(pg_info, geom_data, topo_id, cat);
1958 
1959  /* stmt can NULL when writing PostGIS topology with no attributes
1960  * attached */
1961  if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
1962  /* rollback transaction */
1963  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1964  G_free(geom_data);
1965  G_free(stmt);
1966 
1967  return -1;
1968  }
1969  G_free(geom_data);
1970  G_free(stmt);
1971 
1972  return pg_info->toposchema_name ? topo_id : 0;
1973 }
1974 
1975 /*!
1976  \brief Build INSERT statement to add new feature to the feature
1977  table
1978 
1979  Note: Allocated string should be freed.
1980 
1981  \param pg_info pointer to Format_info_pg structure
1982  \param geom_data geometry data
1983  \param type feature type (GV_POINT, GV_LINE, ...) - (only for PostGIS
1984  Topology) \param id topology element id (only for PostGIS Topology) \param
1985  cat category number (or -1 for no category) \param Fi pointer to field_info
1986  structure (NULL for no attributes)
1987 
1988  \return allocated string with INSERT statement
1989  */
1990 char *build_insert_stmt(const struct Format_info_pg *pg_info,
1991  const char *geom_data, int topo_id, int cat)
1992 {
1993  int topogeom_type;
1994 
1995  char *stmt, buf[DB_SQL_MAX];
1996 
1997  struct field_info *Fi;
1998 
1999  topogeom_type = -1;
2000  if (pg_info->toposchema_name) {
2001  topogeom_type = type_to_topogeom(pg_info);
2002  if (topogeom_type < 0)
2003  return NULL;
2004  }
2005 
2006  Fi = pg_info->fi;
2007 
2008  stmt = NULL;
2009  if (Fi && cat > -1) {
2010  /* write attributes (simple features and topology elements) */
2011  int col, ncol, more;
2012  int sqltype, ctype, is_fid;
2013  char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
2014 
2015  const char *colname;
2016 
2017  dbString dbstmt;
2018  dbCursor cursor;
2019  dbTable *table;
2020  dbColumn *column;
2021  dbValue *value;
2022 
2023  db_init_string(&dbstmt);
2024  buf_val[0] = '\0';
2025 
2026  /* read & set attributes */
2027  sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key, cat);
2028  G_debug(4, "SQL: %s", buf);
2029  db_set_string(&dbstmt, buf);
2030 
2031  /* prepare INSERT statement */
2032  sprintf(buf, "INSERT INTO \"%s\".\"%s\" (", pg_info->schema_name,
2033  pg_info->table_name);
2034 
2035  /* select data */
2036  if (db_open_select_cursor(pg_info->dbdriver, &dbstmt, &cursor,
2037  DB_SEQUENTIAL) != DB_OK) {
2038  G_warning(_("Unable to select attributes for category %d"), cat);
2039  }
2040  else {
2041  if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
2042  G_warning(_("Unable to fetch data from table <%s>"), Fi->table);
2043  }
2044 
2045  if (!more) {
2046  G_warning(_("No database record for category %d, "
2047  "no attributes will be written"),
2048  cat);
2049  }
2050  else {
2051  table = db_get_cursor_table(&cursor);
2053 
2054  for (col = 0; col < ncol; col++) {
2055  column = db_get_table_column(table, col);
2056  colname = db_get_column_name(column);
2057 
2058  /* -> values */
2059  value = db_get_column_value(column);
2060  /* for debug only */
2061  db_convert_column_value_to_string(column, &dbstmt);
2062  G_debug(3, "col %d : val = %s", col,
2063  db_get_string(&dbstmt));
2064 
2065  sqltype = db_get_column_sqltype(column);
2066  ctype = db_sqltype_to_Ctype(sqltype);
2067 
2068  is_fid = strcmp(pg_info->fid_column, colname) == 0;
2069 
2070  /* check fid column (must be integer) */
2071  if (is_fid == TRUE && ctype != DB_C_TYPE_INT) {
2072  G_warning(_("FID column must be integer, column <%s> "
2073  "ignored!"),
2074  colname);
2075  continue;
2076  }
2077 
2078  /* -> columns */
2079  sprintf(buf_tmp, "\"%s\"", colname);
2080  strcat(buf, buf_tmp);
2081  if (col < ncol - 1)
2082  strcat(buf, ",");
2083 
2084  /* prevent writing NULL values */
2085  if (!db_test_value_isnull(value)) {
2086  switch (ctype) {
2087  case DB_C_TYPE_INT:
2088  sprintf(buf_tmp, "%d", db_get_value_int(value));
2089  break;
2090  case DB_C_TYPE_DOUBLE:
2091  sprintf(buf_tmp, "%.14f",
2092  db_get_value_double(value));
2093  break;
2094  case DB_C_TYPE_STRING: {
2095  char *value_tmp;
2096 
2097  value_tmp = G_str_replace(
2098  db_get_value_string(value), "'", "''");
2099  sprintf(buf_tmp, "'%s'", value_tmp);
2100  G_free(value_tmp);
2101  break;
2102  }
2103  case DB_C_TYPE_DATETIME:
2104  db_convert_column_value_to_string(column, &dbstmt);
2105  sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
2106  break;
2107  default:
2108  G_warning(_("Unsupported column type %d"), ctype);
2109  sprintf(buf_tmp, "NULL");
2110  break;
2111  }
2112  }
2113  else {
2114  if (is_fid == TRUE)
2115  G_warning(_("Invalid value for FID column: NULL"));
2116  sprintf(buf_tmp, "NULL");
2117  }
2118  strcat(buf_val, buf_tmp);
2119  if (col < ncol - 1)
2120  strcat(buf_val, ",");
2121  }
2122 
2123  if (!pg_info->toposchema_name) {
2124  /* simple feature access */
2125  G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)", buf,
2126  pg_info->geom_column, buf_val, geom_data);
2127  }
2128  else {
2129  /* PostGIS topology access, write geometry in
2130  * topology schema, skip geometry at this point */
2131  if (buf[strlen(buf) - 1] == ',') { /* last column skipped */
2132  buf[strlen(buf) - 1] = '\0';
2133  buf_val[strlen(buf_val) - 1] = '\0';
2134  }
2135  G_asprintf(&stmt,
2136  "%s, %s) VALUES (%s, '(%d, 1, %d, "
2137  "%d)'::topology.TopoGeometry)",
2138  buf, pg_info->topogeom_column, buf_val,
2139  pg_info->toposchema_id, topo_id, topogeom_type);
2140  }
2141  }
2142  }
2143  }
2144  else {
2145  /* no attributes */
2146  if (!pg_info->toposchema_name) {
2147  /* no attributes (simple features access) */
2148  if (cat > 0) {
2149  /* cetegory defined */
2150  G_asprintf(&stmt,
2151  "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
2152  "(%d, '%s'::GEOMETRY)",
2153  pg_info->schema_name, pg_info->table_name,
2154  GV_KEY_COLUMN, pg_info->geom_column, cat, geom_data);
2155  }
2156  else {
2157  /* no category */
2158  G_asprintf(&stmt,
2159  "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
2160  "('%s'::GEOMETRY)",
2161  pg_info->schema_name, pg_info->table_name,
2162  pg_info->geom_column, geom_data);
2163  }
2164  }
2165  else {
2166  if (cat > 0) {
2167  /* no attributes (topology elements) */
2168  G_asprintf(&stmt,
2169  "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
2170  "(%d, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
2171  pg_info->schema_name, pg_info->table_name,
2172  GV_KEY_COLUMN, pg_info->topogeom_column, cat,
2173  pg_info->toposchema_id, topo_id, topogeom_type);
2174  }
2175  }
2176  }
2177 
2178  return stmt;
2179 }
2180 
2181 /*!
2182  \brief Insert topological element into 'node' or 'edge' table
2183 
2184  Negative id for nodes, 0 for next value.
2185 
2186  \param Map pointer to Map_info struct
2187  \param id feature id (0 for next val)
2188  \param type feature type (GV_POINT, GV_LINE, ...)
2189  \param geom_data geometry in wkb
2190 
2191  \return new topo id
2192  \return -1 on error
2193  */
2194 int insert_topo_element(struct Map_info *Map, int id, int type,
2195  const char *geom_data)
2196 {
2197  int ret, topo_id;
2198  char *stmt, stmt_id[DB_SQL_MAX];
2199  struct Format_info_pg *pg_info;
2200  struct Plus_head *plus;
2201  struct P_line *Line;
2202 
2203  pg_info = &(Map->fInfo.pg);
2204  plus = &(Map->plus);
2205 
2206  Line = NULL;
2207  if (plus->built >= GV_BUILD_BASE) {
2208  if (id > 0) { /* -> feature */
2209  topo_id = id;
2210  if (topo_id > Map->plus.n_lines) {
2211  G_warning(_("Invalid feature %d (max: %d)"), topo_id,
2212  Map->plus.n_lines);
2213  return -1;
2214  }
2215  Line = Map->plus.Line[topo_id];
2216 
2217  if (Line->type & GV_POINTS) {
2218  /* set topo_id for points */
2219  topo_id = Vect_get_num_primitives(Map, GV_POINTS) +
2220  Vect_get_num_nodes(Map);
2221  }
2222  }
2223  else if (id < 0) { /* node */
2224  topo_id = abs(id);
2225  if (type != GV_POINT) {
2226  G_warning(_("Invalid feature type (%d) for node"), type);
2227  return -1;
2228  }
2229  if (topo_id > Map->plus.n_nodes) {
2230  G_warning(_("Invalid node %d (%d)"), topo_id,
2231  Map->plus.n_nodes);
2232  return -1;
2233  }
2234 
2235  /* increment topo_id - also points and centroids are
2236  * stored in 'node' table */
2237  topo_id += Vect_get_num_primitives(Map, GV_POINTS);
2238  }
2239  }
2240 
2241  stmt = NULL;
2242  switch (type) {
2243  case GV_POINT: {
2244  /* insert new node */
2245 #if USE_TOPO_STMT
2246  G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
2247  pg_info->toposchema_name, geom_data);
2248 #else
2249  if (id == 0) {
2250  /* get node_id */
2251  sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
2252  pg_info->toposchema_name);
2253  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2254  }
2255 
2256  /* build insert statement */
2257  G_asprintf(&stmt,
2258  "INSERT INTO \"%s\".node (node_id, geom) VALUES "
2259  "(%d, '%s'::GEOMETRY)",
2260  pg_info->toposchema_name, topo_id, geom_data);
2261 #endif
2262  break;
2263  }
2264  case GV_LINE:
2265  case GV_BOUNDARY: {
2266  /* insert new edge */
2267 #if USE_TOPO_STMT
2268  G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
2269  pg_info->toposchema_name, geom_data);
2270 #else
2271  int n1, n2, nle, nre;
2272 
2273  struct Format_info_offset *offset;
2274 
2275  offset = &(pg_info->offset);
2276 
2277  if (id == 0) {
2278  /* get edge_id */
2279  sprintf(stmt_id, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
2280  pg_info->toposchema_name);
2281  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2282  }
2283 
2284  nle = -topo_id; /* assuming isolated lines */
2285  nre = topo_id;
2286 
2287  if (Line) {
2288  int i, n, next_edge;
2289  struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
2290 
2291  topo_id = (int)Line->offset;
2292  /* start & end node */
2293  n1 = topo->N1;
2294  n2 = topo->N2;
2295 
2296  /* next left & right edge */
2297  for (i = 0; i < 2; i++) {
2298  n = Vect_get_node_n_lines(Map, i == 0 ? n1 : n2);
2299  if (n < 2) /* no connection */
2300  continue;
2301 
2302  next_edge =
2303  update_next_edge(Map, n, i == 0 ? topo_id : -topo_id);
2304  if (next_edge != 0) {
2305  if (i == 0)
2306  nre = next_edge; /* update next right edge for start
2307  node */
2308  else
2309  nle =
2310  next_edge; /* update next left edge for end node */
2311  }
2312  else {
2313  G_warning(_("Unable to determine next left/right edge for "
2314  "edge %d"),
2315  topo_id);
2316  }
2317  }
2318  }
2319  else {
2320  G_warning(_("Unable to insert new edge. Topology not available."));
2321  return -1;
2322  }
2323 
2324  G_debug(3, "new edge: id=%d next_left_edge=%d next_right_edge=%d",
2325  topo_id, nle, nre);
2326 
2327  if (n1 > offset->array_num ||
2328  n2 > offset->array_num) /* node id starts at 1 */
2329  return -1;
2330 
2331  /* build insert statement */
2332  G_asprintf(
2333  &stmt,
2334  "INSERT INTO \"%s\".edge_data (edge_id, start_node, end_node, "
2335  "next_left_edge, abs_next_left_edge, next_right_edge, "
2336  "abs_next_right_edge, "
2337  "left_face, right_face, geom) VALUES "
2338  "(%d, %d, %d, %d, %d, %d, %d, 0, 0, '%s'::GEOMETRY)",
2339  pg_info->toposchema_name, topo_id, offset->array[n1 - 1],
2340  offset->array[n2 - 1], nle, abs(nle), nre, abs(nre), geom_data);
2341 #endif
2342  break;
2343  }
2344  case GV_CENTROID: {
2345  /* insert new node (with containing_face) */
2346 #if USE_TOPO_STMT
2347  G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
2348  pg_info->toposchema_name, geom_data);
2349 #else
2350  int area;
2351 
2352  if (id == 0) {
2353  /* get node_id */
2354  sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
2355  pg_info->toposchema_name);
2356  topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
2357  }
2358 
2359  if (Line) {
2360  struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
2361 
2362  area = topo->area;
2363  }
2364  else {
2365  area = 0;
2366  }
2367 
2368  G_asprintf(
2369  &stmt,
2370  "INSERT INTO \"%s\".node (node_id, containing_face, geom) VALUES "
2371  "(%d, %d, '%s'::GEOMETRY)",
2372  pg_info->toposchema_name, topo_id, area, geom_data);
2373 #endif
2374  break;
2375  }
2376  default:
2377  G_warning(_("Unsupported feature type %d"), type);
2378  break;
2379  }
2380 
2381  /* execute insert statement */
2382  ret = Vect__execute_pg(pg_info->conn, stmt);
2383  G_free(stmt);
2384 
2385  if (ret == -1) {
2386  /* rollback transaction */
2387  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2388  return -1;
2389  }
2390 
2391  return topo_id;
2392 }
2393 
2394 int type_to_topogeom(const struct Format_info_pg *pg_info)
2395 {
2396  int topogeom_type;
2397 
2398  topogeom_type = -1;
2399  switch (pg_info->feature_type) {
2400  case SF_POINT:
2401  topogeom_type = 1;
2402  break;
2403  case SF_LINESTRING:
2404  topogeom_type = 2;
2405  break;
2406  case SF_POLYGON:
2407  topogeom_type = 3;
2408  break;
2409  default:
2410  G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
2411  }
2412 
2413  return topogeom_type;
2414 }
2415 
2416 int Vect__define_topo_relation(const struct Format_info_pg *pg_info,
2417  int topo_id, int element_id)
2418 {
2419  int topogeom_type;
2420  char stmt[DB_SQL_MAX];
2421 
2422  topogeom_type = type_to_topogeom(pg_info);
2423  if (topogeom_type < 0)
2424  return -1;
2425 
2426  sprintf(stmt, "INSERT into \"%s\".relation VALUES(%d, 1, %d, %d)",
2427  pg_info->toposchema_name, topo_id, element_id, topogeom_type);
2428  G_debug(3, "SQL: %s", stmt);
2429 
2430  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2431  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2432  return -1;
2433  }
2434 
2435  return 0;
2436 }
2437 
2438 /*!
2439  \brief Find next line (topo only)
2440 
2441  \param Map pointer to Map_info struct
2442  \param nlines number of lines
2443  \param line current line
2444  \param[out] left left line
2445  \param[out] right right line
2446 
2447  \return left (line < 0) or right (line > 0) next edge
2448  \return 0 on failure
2449  */
2450 int update_next_edge(struct Map_info *Map, int nlines, int line)
2451 {
2452  int ret, next_line, edge;
2453  char stmt[DB_SQL_MAX];
2454 
2455  const struct Format_info_pg *pg_info;
2456  struct P_line *Line_next, *Line;
2457 
2458  Line = Line_next = NULL;
2459 
2460  pg_info = &(Map->fInfo.pg);
2461 
2462  /* find next line
2463  start node -> next on the left
2464  end node -> next on the right
2465  */
2466  next_line =
2467  dig_angle_next_line(&(Map->plus), line, GV_LEFT, GV_LINES, NULL);
2468  G_debug(3, "line=%d next_line=%d", line, next_line);
2469  if (next_line == 0) {
2470  G_warning(_("Invalid topology"));
2471  return 0;
2472  }
2473 
2474  Line = Map->plus.Line[abs(line)];
2475  Line_next = Map->plus.Line[abs(next_line)];
2476  if (!Line || !Line_next) {
2477  G_warning(_("Invalid topology"));
2478  return 0;
2479  }
2480 
2481  if (line > 0) {
2482  edge = Line->offset;
2483  ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
2484  }
2485  else {
2486  edge = -Line->offset;
2487  ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
2488  }
2489 
2490  if (next_line < 0) {
2491  sprintf(stmt,
2492  "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
2493  "abs_next_left_edge = %d WHERE edge_id = %d AND "
2494  "abs_next_left_edge = %d",
2495  pg_info->toposchema_name, edge, abs(edge),
2496  (int)Line_next->offset, (int)Line_next->offset);
2497  G_debug(3, "update edge=%d next_left_edge=%d (?)",
2498  (int)Line_next->offset, edge);
2499  }
2500  else {
2501  sprintf(stmt,
2502  "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
2503  "abs_next_right_edge = %d WHERE edge_id = %d AND "
2504  "abs_next_right_edge = %d",
2505  pg_info->toposchema_name, edge, abs(edge),
2506  (int)Line_next->offset, (int)Line_next->offset);
2507  G_debug(3, "update edge=%d next_right_edge=%d (?)",
2508  (int)Line_next->offset, edge);
2509  }
2510 
2511  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2512  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2513  return 0;
2514  }
2515 
2516  if (nlines > 2) {
2517  /* more lines connected to the node
2518 
2519  start node -> next on the right
2520  end node -> next on the left
2521  */
2522  next_line =
2523  dig_angle_next_line(&(Map->plus), line, GV_RIGHT, GV_LINES, NULL);
2524  Line_next = Map->plus.Line[abs(next_line)];
2525 
2526  if (next_line < 0) {
2527  sprintf(stmt,
2528  "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
2529  "abs_next_left_edge = %d WHERE edge_id = %d",
2530  pg_info->toposchema_name, edge, abs(edge),
2531  (int)Line_next->offset);
2532  G_debug(3, "update edge=%d next_left_edge=%d",
2533  (int)Line_next->offset, edge);
2534  }
2535  else {
2536  sprintf(stmt,
2537  "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
2538  "abs_next_right_edge = %d WHERE edge_id = %d",
2539  pg_info->toposchema_name, edge, abs(edge),
2540  (int)Line_next->offset);
2541  G_debug(3, "update edge=%d next_right_edge=%d",
2542  (int)Line_next->offset, edge);
2543  }
2544 
2545  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2546  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2547  return 0;
2548  }
2549  }
2550 
2551  return ret;
2552 }
2553 
2554 /*!
2555  \brief Insert new face to the 'face' table (topo only)
2556 
2557  \param Map pointer to Map_info struct
2558  \param area area id (negative id for isles)
2559 
2560  \return 0 on error
2561  \return area id on success (>0)
2562  */
2563 int Vect__insert_face_pg(struct Map_info *Map, int area)
2564 {
2565  char *stmt;
2566 
2567  struct Format_info_pg *pg_info;
2568  struct bound_box box;
2569 
2570  if (area == 0)
2571  return 0; /* universal face has id '0' in PostGIS Topology */
2572 
2573  stmt = NULL;
2574  pg_info = &(Map->fInfo.pg);
2575 
2576  /* check if face exists */
2577 
2578  /* get mbr of the area */
2579  if (area > 0)
2580  Vect_get_area_box(Map, area, &box);
2581  else
2582  Vect_get_isle_box(Map, abs(area), &box);
2583 
2584  /* insert face if not exists */
2585  G_asprintf(&stmt,
2586  "INSERT INTO \"%s\".face (face_id, mbr) VALUES "
2587  "(%d, ST_GeomFromText('POLYGON((%.12f %.12f, %.12f %.12f, %.12f "
2588  "%.12f, %.12f %.12f, "
2589  "%.12f %.12f))', %d))",
2590  pg_info->toposchema_name, area, box.W, box.S, box.W, box.N,
2591  box.E, box.N, box.E, box.S, box.W, box.S, pg_info->srid);
2592  G_debug(3, "new face id=%d", area);
2593  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2594  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2595  return 0;
2596  }
2597  G_free(stmt);
2598 
2599  return area;
2600 }
2601 
2602 #if 0 /* unused */
2603 /*!
2604  \brief Delete existing face (currently unused)
2605 
2606  \todo Set foreign keys as DEFERRABLE INITIALLY DEFERRED and use SET
2607  CONSTRAINTS ALL DEFERRED
2608 
2609  \param Map pointer to Map_info struct
2610  \param area area id to delete
2611 
2612  \return 0 on success
2613  \return -1 on error
2614  */
2615 int delete_face(struct Map_info *Map, int area)
2616 {
2617  char stmt[DB_SQL_MAX];
2618 
2619  const struct Format_info_pg *pg_info;
2620 
2621  pg_info = &(Map->fInfo.pg);
2622 
2623  /* update centroids first */
2624  sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 "
2625  "WHERE containing_face = %d", pg_info->toposchema_name, area);
2626  G_debug(3, "SQL: %s", stmt);
2627  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2628  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2629  return -1;
2630  }
2631 
2632  /* update also edges (left face) */
2633  sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0 "
2634  "WHERE left_face = %d", pg_info->toposchema_name, area);
2635  G_debug(3, "SQL: %s", stmt);
2636  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2637  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2638  return -1;
2639  }
2640 
2641  /* update also edges (left face) */
2642  sprintf(stmt, "UPDATE \"%s\".edge_data SET right_face = 0 "
2643  "WHERE right_face = %d", pg_info->toposchema_name, area);
2644  G_debug(3, "SQL: %s", stmt);
2645  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2646  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2647  return -1;
2648  }
2649 
2650  /* delete face */
2651  sprintf(stmt, "DELETE FROM \"%s\".face WHERE face_id = %d",
2652  pg_info->toposchema_name, area);
2653  G_debug(3, "delete face id=%d", area);
2654  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2655  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2656  return -1;
2657  }
2658 
2659  return 0;
2660 }
2661 
2662 /*!
2663  \brief Update lines (next left and right edges)
2664 
2665  - isolated edges
2666  next left edge: -edge
2667  next right edge: edge
2668 
2669  - connected edges
2670  next left edge: next edge or -edge
2671  next right edge: next edge or edge
2672 
2673  \param Map pointer to Map_info struct
2674  \param line feature id
2675 
2676  \return 0 on success
2677  \return -1 on error
2678  */
2679 int update_topo_edge(struct Map_info *Map, int line)
2680 {
2681  int i, n;
2682  int nle, nre, next_edge;
2683  char stmt[DB_SQL_MAX];
2684 
2685  struct Format_info_pg *pg_info;
2686  struct P_line *Line;
2687 
2688  pg_info = &(Map->fInfo.pg);
2689 
2690  if (line < 1 || line > Map->plus.n_lines) {
2691  G_warning(_("Attempt to access non-existing feature %d"), line);
2692  return -1;
2693  }
2694  Line = Map->plus.Line[line];
2695  if (!Line) {
2696  G_warning(_("Attempt to access dead feature %d"), line);
2697  return -1;
2698  }
2699 
2700  struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
2701 
2702  nre = nle = 0; /* edge = 0 is an illegal value */
2703 
2704  /* check for line connection */
2705  for (i = 0; i < 2; i++) {
2706  /* first check start node then end node */
2707  n = i == 0 ? Vect_get_node_n_lines(Map, topo->N1)
2708  : Vect_get_node_n_lines(Map, topo->N2);
2709 
2710  if (n < 2) /* no connection */
2711  continue;
2712 
2713  next_edge = update_next_edge(Map, n, i == 0 ? line : -line);
2714  if (next_edge != 0) {
2715  if (i == 0)
2716  nre = next_edge; /* update next right edge for start node */
2717  else
2718  nle = next_edge; /* update next left edge for end node */
2719  }
2720  else {
2721  G_warning(_("Unable to determine next left/right edge"));
2722  return -1;
2723  }
2724  }
2725 
2726  if (nle == 0 && nre == 0) /* nothing changed */
2727  return 0;
2728 
2729  if (nle != 0 && nre != 0) {
2730  /* update both next left and right edge */
2731  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2732  "next_left_edge = %d, abs_next_left_edge = %d, "
2733  "next_right_edge = %d, abs_next_right_edge = %d "
2734  "WHERE edge_id = %d", pg_info->toposchema_name,
2735  nle, abs(nle), nre, abs(nre), (int)Line->offset);
2736  }
2737  else if (nle != 0) {
2738  /* update next left edge only */
2739  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2740  "next_left_edge = %d, abs_next_left_edge = %d "
2741  "WHERE edge_id = %d", pg_info->toposchema_name,
2742  nle, abs(nle), (int)Line->offset);
2743  }
2744  else {
2745  /* update next right edge only */
2746  sprintf(stmt, "UPDATE \"%s\".edge_data SET "
2747  "next_right_edge = %d, abs_next_right_edge = %d "
2748  "WHERE edge_id = %d", pg_info->toposchema_name,
2749  nre, abs(nre), (int)Line->offset);
2750  }
2751  G_debug(3, "update edge=%d next_left_edge=%d next_right_edge=%d",
2752  (int)Line->offset, nle, nre);
2753 
2754  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2755  /* rollback transaction */
2756  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2757  return -1;
2758  }
2759 
2760  return 0;
2761 }
2762 #endif
2763 
2764 /*!
2765  \brief Update lines (left and right faces)
2766 
2767  TODO: handle isles
2768 
2769  \param Map pointer to Map_info struct
2770  \param line feature id
2771 
2772  \return 0 on success
2773  \return -1 on error
2774  */
2775 int update_topo_face(struct Map_info *Map, int line)
2776 {
2777  int i, s, area, face[2];
2778  char stmt[DB_SQL_MAX];
2779 
2780  struct Format_info_pg *pg_info;
2781  struct P_line *Line, *Line_i;
2782  struct P_area *Area;
2783  struct P_topo_b *topo, *topo_i;
2784 
2785  pg_info = &(Map->fInfo.pg);
2786 
2787  if (line < 1 || line > Map->plus.n_lines) {
2788  G_warning(_("Attempt to access non-existing feature %d"), line);
2789  return -1;
2790  }
2791  Line = Map->plus.Line[line];
2792  if (!Line) {
2793  G_warning(_("Attempt to access dead feature %d"), line);
2794  return -1;
2795  }
2796 
2797  topo = (struct P_topo_b *)Line->topo;
2798 
2799  /* for both side on the current boundary (line) */
2800  /* create new faces */
2801  for (s = 0; s < 2; s++) { /* for each side */
2802  area = s == 0 ? topo->left : topo->right;
2803  if (area <= 0) /* no area - skip */
2804  continue;
2805 
2806  face[s] = Vect__insert_face_pg(Map, area);
2807  if (face[s] < 1) {
2808  G_warning(_("Unable to create new face"));
2809  return -1;
2810  }
2811  }
2812 
2813  /* update edges forming faces */
2814  for (s = 0; s < 2; s++) { /* for each side */
2815  area = s == 0 ? topo->left : topo->right;
2816  if (area <= 0) /* no area - skip */
2817  continue;
2818 
2819  Area = Map->plus.Area[area];
2820  for (i = 0; i < Area->n_lines; i++) {
2821  Line_i = Map->plus.Line[abs(Area->lines[i])];
2822  topo_i = (struct P_topo_b *)Line_i->topo;
2823 
2824  sprintf(stmt,
2825  "UPDATE \"%s\".edge_data SET "
2826  "left_face = %d, right_face = %d "
2827  "WHERE edge_id = %d",
2828  pg_info->toposchema_name,
2829  topo_i->left > 0 ? topo_i->left : 0,
2830  topo_i->right > 0 ? topo_i->right : 0, (int)Line_i->offset);
2831  G_debug(2, "SQL: %s", stmt);
2832 
2833  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2834  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2835  return -1;
2836  }
2837  }
2838 
2839  /* update also centroids (stored as nodes) */
2840  if (Area->centroid > 0) {
2841  Line_i = Map->plus.Line[Area->centroid];
2842  sprintf(stmt,
2843  "UPDATE \"%s\".node SET containing_face = %d "
2844  "WHERE node_id = %d",
2845  pg_info->toposchema_name, face[s], (int)Line_i->offset);
2846  G_debug(2, "SQL: %s", stmt);
2847 
2848  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2849  /* rollback transaction */
2850  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2851  return -1;
2852  }
2853  }
2854  }
2855 
2856  return 0;
2857 }
2858 
2859 /*!
2860  \brief Add line to native and PostGIS topology
2861 
2862  \param Map vector map
2863  \param offset ???
2864  \param type feature type
2865  \param Points feature vertices
2866 
2867  \return feature id
2868  \return -1 on error
2869  */
2870 int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
2871  const struct line_pnts *points)
2872 {
2873  int line, n_nodes;
2874 
2875  struct Plus_head *plus;
2876 
2877  plus = &(Map->plus);
2878 
2879  Vect_reset_updated(Map);
2880  line = V2__add_line_to_topo_nat(Map, offset, type, points, NULL, -1, NULL);
2881 
2882  /* insert new nodes into 'node' table */
2884  if (n_nodes > 0) {
2885  int i, node;
2886  double x, y, z;
2887 
2888  if (!Points)
2889  Points = Vect_new_line_struct();
2890 
2891  for (i = 0; i < n_nodes; i++) {
2892  node = Vect_get_updated_node(Map, i);
2893  /* skip updated and deleted nodes */
2894  if (node > 0 || plus->Node[abs(node)] == NULL)
2895  continue;
2896 
2897  G_debug(3, " new node: %d", node);
2898 
2899  Vect_get_node_coor(Map, abs(node), &x, &y, &z);
2900  Vect_reset_line(Points);
2901  Vect_append_point(Points, x, y, z);
2902 
2903  write_feature(Map, node, GV_POINT,
2904  (const struct line_pnts **)&Points, 1, -1);
2905  }
2906  }
2907 
2908  return line;
2909 }
2910 
2911 /*!
2912  \brief Delete line from native and PostGIS topology
2913 
2914  \param Map vector map
2915  \param line feature id to remove from topo
2916  \param type feature type
2917  \param Points feature vertices
2918 
2919  \return 0 on success
2920  \return -1 on error
2921  */
2922 int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
2923  const struct line_pnts *Points)
2924 {
2925  int N1, N2, node_id;
2926  char stmt[DB_SQL_MAX];
2927 
2928  struct Format_info_pg *pg_info;
2929  struct P_node *Node;
2930 
2931  pg_info = &(Map->fInfo.pg);
2932 
2933  Vect_reset_updated(Map);
2934 
2935  if (!(type & GV_LINES))
2936  return 0;
2937 
2938  Vect_get_line_nodes(Map, line, &N1, &N2);
2939  if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
2940  return -1;
2941 
2942  Node = Map->plus.Node[N1];
2943  if (!Node || Node->n_lines == 0) {
2944  node_id = pg_info->offset.array[N1 - 1];
2945  sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
2946  pg_info->toposchema_name, node_id);
2947  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2948  G_warning(_("Unable to delete node %d"), node_id);
2949  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2950  return -1;
2951  }
2952  }
2953 
2954  Node = Map->plus.Node[N2];
2955  if (!Node || Node->n_lines == 0) {
2956  node_id = pg_info->offset.array[N2 - 1];
2957  sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
2958  pg_info->toposchema_name, node_id);
2959  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
2960  G_warning(_("Unable to delete node %d"), node_id);
2961  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2962  return -1;
2963  }
2964  }
2965 
2966  return 0;
2967 }
2968 
2969 int set_constraint_to_deferrable(struct Format_info_pg *pg_info,
2970  const char *table, const char *constraint,
2971  const char *column, const char *ref_table,
2972  const char *ref_column)
2973 {
2974  char stmt[DB_SQL_MAX];
2975 
2976  sprintf(stmt, "ALTER TABLE \"%s\".%s DROP CONSTRAINT %s",
2977  pg_info->toposchema_name, table, constraint);
2978  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
2979  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2980  return -1;
2981  }
2982 
2983  sprintf(stmt,
2984  "ALTER TABLE \"%s\".%s ADD CONSTRAINT %s "
2985  "FOREIGN KEY (%s) REFERENCES \"%s\".%s (%s) "
2986  "DEFERRABLE INITIALLY DEFERRED",
2987  pg_info->toposchema_name, table, constraint, column,
2988  pg_info->toposchema_name, ref_table, ref_column);
2989  if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
2990  Vect__execute_pg(pg_info->conn, "ROLLBACK");
2991  return -1;
2992  }
2993 
2994  return 0;
2995 }
2996 
2997 /*!
2998  \brief Open database connection with attribute table
2999 
3000  \param pg_info pointer to Format_info_pg struct
3001 
3002  \return pointer to dbDriver on success
3003  \return NULL on failure
3004  */
3005 dbDriver *open_db(struct Format_info_pg *pg_info)
3006 {
3007  dbDriver *driver;
3008  dbHandle handle;
3009 
3010  struct field_info *Fi;
3011 
3012  db_init_handle(&handle);
3013 
3014  Fi = pg_info->fi;
3015 
3016  pg_info->dbdriver = driver = db_start_driver(Fi->driver);
3017  if (!driver) {
3018  G_warning(_("Unable to start driver <%s>"), Fi->driver);
3019  return NULL;
3020  }
3021  db_set_handle(&handle, Fi->database, NULL);
3022  if (db_open_database(driver, &handle) != DB_OK) {
3023  G_warning(_("Unable to open database <%s> by driver <%s>"),
3024  Fi->database, Fi->driver);
3026  pg_info->dbdriver = NULL;
3027  return NULL;
3028  }
3029 
3030  return pg_info->dbdriver;
3031 }
3032 #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:143
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:79
#define FALSE
Definition: gis.h:83
#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:47
#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:1596
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
Definition: read_pg.c:1562
void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
Reallocate lines cache.
Definition: read_pg.c:1623
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:21
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:2416
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:2563
#define TOPOGEOM_COLUMN
Definition: write_pg.c:35
#define x