GRASS GIS 8 Programmer's Manual  8.5.0dev(2024)-bea8435a9e
open_pg.c
Go to the documentation of this file.
1 /*!
2  \file lib/vector/Vlib/open_pg.c
3 
4  \brief Vector library - Open PostGIS layer as vector map layer
5 
6  Higher level functions for reading/writing/manipulating vectors.
7 
8  (C) 2011-2013 by the GRASS Development Team
9 
10  This program is free software under the GNU General Public License
11  (>=v2). Read the file COPYING that comes with GRASS for details.
12 
13  \author Martin Landa <landa.martin gmail.com>
14  */
15 
16 #include <string.h>
17 #include <stdlib.h>
18 
19 #include <grass/vector.h>
20 #include <grass/dbmi.h>
21 #include <grass/glocale.h>
22 
23 #include "local_proto.h"
24 
25 #ifdef HAVE_POSTGRES
26 #include "pg_local_proto.h"
27 
28 struct line_data {
29  int id;
30  int fid;
31  int start_node;
32  int end_node;
33  int left_face;
34  int right_face;
35  char *wkb_geom;
36 };
37 
38 static char *get_key_column(struct Format_info_pg *);
39 static SF_FeatureType ftype_from_string(const char *);
40 static int drop_table(struct Format_info_pg *);
41 static void connect_db(struct Format_info_pg *);
42 static int check_topo(struct Format_info_pg *, struct Plus_head *);
43 static int parse_bbox(const char *, struct bound_box *);
44 static struct P_node *read_p_node(struct Plus_head *, int, int, const char *,
45  const char *, const char *,
46  struct Format_info_pg *, int);
47 static struct P_line *read_p_line(struct Plus_head *, int,
48  const struct line_data *, int,
49  struct Format_info_cache *);
50 static struct P_area *read_p_area(struct Plus_head *, int, const char *, int,
51  const char *);
52 static struct P_isle *read_p_isle(struct Plus_head *, int, const char *, int);
53 static void notice_processor(void *, const char *);
54 static char **scan_array(const char *);
55 static int remap_node(const struct Format_info_offset *, int);
56 static int remap_line(const struct Plus_head *, off_t, int);
57 
58 #define NOPG_UNUSED
59 #else
60 #define NOPG_UNUSED UNUSED
61 #endif
62 
63 /*!
64  \brief Open vector map - PostGIS feature table on non-topological
65  level
66 
67  \param[in,out] Map pointer to Map_info structure
68  \param update TRUE for write mode, otherwise read-only
69 
70  \return 0 success
71  \return -1 error
72  */
73 int V1_open_old_pg(struct Map_info *Map NOPG_UNUSED, int update NOPG_UNUSED)
74 {
75 #ifdef HAVE_POSTGRES
76  int found;
77 
78  char stmt[DB_SQL_MAX];
79 
80  PGresult *res;
81 
82  struct Format_info_pg *pg_info;
83 
84  G_debug(2, "V1_open_old_pg(): update = %d", update);
85 
86  pg_info = &(Map->fInfo.pg);
87  if (!pg_info->conninfo) {
88  G_warning(_("Connection string not defined"));
89  return -1;
90  }
91 
92  if (!pg_info->table_name) {
93  G_warning(_("PostGIS feature table not defined"));
94  return -1;
95  }
96 
97  G_debug(1, "V1_open_old_pg(): conninfo='%s' table='%s'", pg_info->conninfo,
98  pg_info->table_name);
99 
100  /* connect database */
101  if (!pg_info->conn)
102  connect_db(pg_info);
103 
104  /* get fid and geometry column */
105  sprintf(stmt,
106  "SELECT f_geometry_column, coord_dimension, srid, type "
107  "FROM geometry_columns WHERE f_table_schema = '%s' AND "
108  "f_table_name = '%s'",
109  pg_info->schema_name, pg_info->table_name);
110  G_debug(2, "SQL: %s", stmt);
111 
112  res = PQexec(pg_info->conn, stmt);
113  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
114  G_fatal_error("%s\n%s", _("No feature tables found in database."),
115  PQresultErrorMessage(res));
116 
117  found = PQntuples(res) > 0 ? TRUE : FALSE;
118  if (found) {
119  /* geometry column */
120  pg_info->geom_column = G_store(PQgetvalue(res, 0, 0));
121  G_debug(3, "\t-> table = %s column = %s", pg_info->table_name,
122  pg_info->geom_column);
123  /* fid column */
124  pg_info->fid_column = get_key_column(pg_info);
125  /* coordinates dimension */
126  pg_info->coor_dim = atoi(PQgetvalue(res, 0, 1));
127  /* SRS ID */
128  pg_info->srid = atoi(PQgetvalue(res, 0, 2));
129  /* feature type */
130  pg_info->feature_type = ftype_from_string(PQgetvalue(res, 0, 3));
131  }
132  PQclear(res);
133 
134  /* fid -1 for empty / -2 for full cache */
135  pg_info->cache.fid = pg_info->cache.ctype != CACHE_MAP ? -1 : -2;
136 
137  if (!found) {
138  G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
139  pg_info->table_name);
140  return 0; /* avoid calling G_fatal_error() */
141  }
142 
143  /* check for topo schema */
144  check_topo(pg_info, &(Map->plus));
145 
146  return 0;
147 #else
148  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
149  return -1;
150 #endif
151 }
152 
153 /*!
154  \brief Open vector map - PostGIS feature table on topological level
155 
156  Simple feature access:
157  - open feature index file
158  PostGIS Topology:
159  - check if topological schema exists
160 
161  \param[in,out] Map pointer to Map_info structure
162 
163  \return 0 success
164  \return -1 error
165  */
167 {
168 #ifdef HAVE_POSTGRES
169  struct Format_info_pg *pg_info;
170 
171  PGresult *res;
172 
173  G_debug(3, "V2_open_old_pg(): name = %s mapset = %s", Map->name,
174  Map->mapset);
175 
176  pg_info = &(Map->fInfo.pg);
177 
178  if (pg_info->toposchema_name) {
179  char stmt[DB_SQL_MAX];
180 
181  /* get topo schema id */
182  sprintf(stmt, "SELECT id FROM topology.topology WHERE name = '%s'",
183  pg_info->toposchema_name);
184  res = PQexec(pg_info->conn, stmt);
185  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
186  G_warning("%s\n%s", _("Topology schema not found."),
187  PQresultErrorMessage(res));
188  if (res)
189  PQclear(res);
190  return -1;
191  }
192  pg_info->toposchema_id = atoi(PQgetvalue(res, 0, 0));
193  PQclear(res);
194  }
195  else {
196  /* fidx file needed only for simple features access */
197  if (Vect_open_fidx(Map, &(pg_info->offset)) != 0) {
198  G_warning(
199  _("Unable to open feature index file for vector map <%s>"),
200  Vect_get_full_name(Map));
201  G_zero(&(pg_info->offset), sizeof(struct Format_info_offset));
202  }
203  }
204  return 0;
205 #else
206  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
207  return -1;
208 #endif
209 }
210 
211 /*!
212  \brief Prepare PostGIS database for creating new feature table
213  (level 1)
214 
215  New PostGIS table is created when writing features by
216  Vect_wrile_line().
217 
218  \param[out] Map pointer to Map_info structure
219  \param name name of PostGIS feature table to create
220  \param with_z WITH_Z for 3D vector data otherwise WITHOUT_Z
221 
222  \return 0 success
223  \return -1 error
224  */
226  const char *name NOPG_UNUSED, int with_z NOPG_UNUSED)
227 {
228 #ifdef HAVE_POSTGRES
229  char stmt[DB_SQL_MAX];
230 
231  struct Format_info_pg *pg_info;
232 
233  PGresult *res;
234 
235  G_debug(2, "V1_open_new_pg(): name = %s with_z = %d", name, with_z);
236 
237  pg_info = &(Map->fInfo.pg);
238  if (!pg_info->conninfo) {
239  G_warning(_("Connection string not defined"));
240  return -1;
241  }
242 
243  if (!pg_info->table_name) {
244  G_warning(_("PostGIS feature table not defined"));
245  return -1;
246  }
247 
248  G_debug(1, "V1_open_new_pg(): conninfo='%s' table='%s'", pg_info->conninfo,
249  pg_info->table_name);
250 
251  /* connect database & get DB name */
252  connect_db(pg_info);
253 
254  /* if schema not defined, use 'public' */
255  if (!pg_info->schema_name)
256  pg_info->schema_name = G_store("public");
257 
258  /* if fid_column not defined, use 'fid' */
259  if (!pg_info->fid_column)
260  pg_info->fid_column = G_store(GV_PG_FID_COLUMN);
261 
262  /* if geom_column not defined, use 'geom' */
263  if (!pg_info->geom_column)
265 
266  /* check if feature table already exists */
267  sprintf(stmt,
268  "SELECT * FROM pg_tables "
269  "WHERE schemaname = '%s' AND tablename = '%s'",
270  pg_info->schema_name, pg_info->table_name);
271  G_debug(2, "SQL: %s", stmt);
272 
273  res = PQexec(pg_info->conn, stmt);
274  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
275  G_fatal_error("%s\n%s", _("No feature tables found in database."),
276  PQresultErrorMessage(res));
277 
278  if (PQntuples(res) > 0) {
279  /* table found */
280  if (G_get_overwrite()) {
281  G_warning(_("PostGIS layer <%s.%s> already exists and will be "
282  "overwritten"),
283  pg_info->schema_name, pg_info->table_name);
284  if (drop_table(pg_info) == -1) {
285  G_warning(_("Unable to delete PostGIS layer <%s>"),
286  pg_info->table_name);
287  return -1;
288  }
289  }
290  else {
291  G_warning(
292  _("PostGIS layer <%s.%s> already exists in database '%s'"),
293  pg_info->schema_name, pg_info->table_name, pg_info->db_name);
294  return -1;
295  }
296  }
297 
298  /* no feature in cache */
299  pg_info->cache.fid = -1;
300 
301  /* unknown feature type */
302  pg_info->feature_type = SF_GEOMETRY;
303 
304  PQclear(res);
305 
306  return 0;
307 #else
308  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
309  return -1;
310 #endif
311 }
312 
313 /*!
314  \brief Read full-topology for PostGIS links
315 
316  Note: Only 2D topological primitives are currently supported
317 
318  \param[in,out] Map pointer to Map_info structure
319  \param head_only TRUE to read only header
320  \param update TRUE to clean GRASS topology in update mode
321 
322  \return 0 on success
323  \return 1 topology layer does not exist
324  \return -1 on error
325  */
327  int head_only NOPG_UNUSED, int update NOPG_UNUSED)
328 {
329 #ifdef HAVE_POSTGRES
330  int ret;
331  struct Plus_head *plus;
332  struct Format_info_pg *pg_info;
333 
334  Map->open = VECT_OPEN_CODE; /* needed by load_plus */
335 
336  plus = &(Map->plus);
337  pg_info = &(Map->fInfo.pg);
338 
339  /* check for topo schema */
340  if (check_topo(pg_info, plus) != 0)
341  return 1;
342 
343  /* free and init plus structure, update spatial and category
344  * indices */
345  dig_init_plus(plus);
346  plus->Spidx_new = TRUE; /* force building spatial and category indices */
347  plus->update_cidx = TRUE;
348  Map->support_updated = FALSE; /* don't write support files */
349 
350  ret = Vect__load_plus_pg(Map, head_only);
351 
352  if (update)
353  Vect__clean_grass_db_topo(pg_info);
354 
355  plus->cidx_up_to_date = TRUE; /* category index built from topo */
356 
357  return ret;
358 #else
359  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
360  return -1;
361 #endif
362 }
363 
364 #ifdef HAVE_POSTGRES
365 /*!
366  \brief Get key column for feature table
367 
368  \param pg_info pointer to Format_info_pg
369 
370  \return string buffer with key column name
371  \return NULL on missing key column
372  */
373 char *get_key_column(struct Format_info_pg *pg_info)
374 {
375  char *key_column;
376  char stmt[DB_SQL_MAX];
377 
378  PGresult *res;
379 
380  sprintf(stmt,
381  "SELECT kcu.column_name "
382  "FROM INFORMATION_SCHEMA.TABLES t "
383  "LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc "
384  "ON tc.table_catalog = t.table_catalog "
385  "AND tc.table_schema = t.table_schema "
386  "AND tc.table_name = t.table_name "
387  "AND tc.constraint_type = 'PRIMARY KEY' "
388  "LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu "
389  "ON kcu.table_catalog = tc.table_catalog "
390  "AND kcu.table_schema = tc.table_schema "
391  "AND kcu.table_name = tc.table_name "
392  "AND kcu.constraint_name = tc.constraint_name "
393  "WHERE t.table_schema = '%s' AND t.table_name = '%s'",
394  pg_info->schema_name, pg_info->table_name);
395  G_debug(2, "SQL: %s", stmt);
396 
397  res = PQexec(pg_info->conn, stmt);
398  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1 ||
399  strlen(PQgetvalue(res, 0, 0)) < 1) {
400  G_warning(_("No key column detected."));
401  if (res)
402  PQclear(res);
403  return NULL;
404  }
405  key_column = G_store(PQgetvalue(res, 0, 0));
406 
407  PQclear(res);
408 
409  return key_column;
410 }
411 
412 /*!
413  \brief Get simple feature type from string
414 
415  \param type string
416 
417  \return SF type
418  */
419 SF_FeatureType ftype_from_string(const char *type)
420 {
421  SF_FeatureType sf_type;
422 
423  if (G_strcasecmp(type, "POINT") == 0)
424  return SF_POINT;
425  else if (G_strcasecmp(type, "LINESTRING") == 0)
426  return SF_LINESTRING;
427  else if (G_strcasecmp(type, "POLYGON") == 0)
428  return SF_POLYGON;
429  else if (G_strcasecmp(type, "MULTIPOINT") == 0)
430  return SF_MULTIPOINT;
431  else if (G_strcasecmp(type, "MULTILINESTRING") == 0)
432  return SF_MULTILINESTRING;
433  else if (G_strcasecmp(type, "MULTIPOLYGON") == 0)
434  return SF_MULTIPOLYGON;
435  else if (G_strcasecmp(type, "GEOMETRYCOLLECTION") == 0)
436  return SF_GEOMETRYCOLLECTION;
437  else
438  return SF_GEOMETRY;
439 
440  G_debug(3, "ftype_from_string(): type='%s' -> %d", type, sf_type);
441 
442  return sf_type;
443 }
444 
445 /*!
446  \brief Drop feature table and topology schema if exists
447 
448  \param pg_info pointer to Format_info_pg
449 
450  \return -1 on error
451  \return 0 on success
452  */
453 int drop_table(struct Format_info_pg *pg_info)
454 {
455  int i;
456  char stmt[DB_SQL_MAX];
457  char *topo_schema;
458 
459  PGresult *result, *result_drop;
460 
461  /* check if topology schema exists */
462  sprintf(stmt,
463  "SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'topology'");
464  if (Vect__execute_get_value_pg(pg_info->conn, stmt) != 0) {
465  /* drop topology schema(s) related to the feature table */
466  sprintf(stmt,
467  "SELECT t.name FROM topology.layer AS l JOIN "
468  "topology.topology AS t ON l.topology_id = t.id "
469  "WHERE l.table_name = '%s'",
470  pg_info->table_name);
471  G_debug(2, "SQL: %s", stmt);
472 
473  result = PQexec(pg_info->conn, stmt);
474  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
475  G_warning(_("Execution failed: %s"), PQerrorMessage(pg_info->conn));
476  PQclear(result);
477  return -1;
478  }
479  for (i = 0; i < PQntuples(result); i++) {
480  topo_schema = PQgetvalue(result, i, 0);
481  sprintf(stmt, "SELECT topology.DropTopology('%s')", topo_schema);
482  G_debug(2, "SQL: %s", stmt);
483 
484  result_drop = PQexec(pg_info->conn, stmt);
485  if (!result_drop || PQresultStatus(result_drop) != PGRES_TUPLES_OK)
486  G_warning(_("Execution failed: %s"),
487  PQerrorMessage(pg_info->conn));
488 
489  G_verbose_message(_("PostGIS topology schema <%s> dropped"),
490  topo_schema);
491  PQclear(result_drop);
492  }
493  PQclear(result);
494  }
495 
496  /* drop feature table */
497  sprintf(stmt, "DROP TABLE \"%s\".\"%s\"", pg_info->schema_name,
498  pg_info->table_name);
499  G_debug(2, "SQL: %s", stmt);
500 
501  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
502  return -1;
503  }
504 
505  return 0;
506 }
507 
508 /*!
509  \brief Establish PG connection (pg_info->conninfo)
510 
511  Check if DB is spatial as defined by PostGIS.
512 
513  \param pg_info pointer to Format_info_pg
514  */
515 void connect_db(struct Format_info_pg *pg_info)
516 {
517  char stmt[DB_SQL_MAX];
518 
519  /* check if connection string already contains user/passwd */
520  if (!strstr(pg_info->conninfo, "user")) {
521  char dbname[GNAME_MAX];
522  char *p;
523  const char *user, *passwd, *host, *port;
524 
525  dbname[0] = '\0';
526  p = strstr(pg_info->conninfo, "dbname");
527  if (p) {
528  int i;
529 
530  p += strlen("dbname") + 1; /* dbname= */
531 
532  for (i = 0; *p && *p != ' '; i++, p++)
533  dbname[i] = *p;
534  }
535 
536  /* try connection settings for given database first, then try
537  * any settings defined for pg driver */
538  db_get_login("pg", dbname, &user, &passwd, &host, &port);
539  /* any settings defined for pg driver disabled - can cause
540  problems when running multiple local/remote db clusters
541  if (strlen(dbname) > 0 && !user && !passwd)
542  db_get_login("pg", NULL, &user, &passwd, &host, &port);
543  */
544  if (user || passwd || host || port) {
545  char conninfo[DB_SQL_MAX];
546 
547  sprintf(conninfo, "%s", pg_info->conninfo);
548  if (user) {
549  strcat(conninfo, " user=");
550  strcat(conninfo, user);
551  }
552  if (passwd) {
553  strcat(conninfo, " password=");
554  strcat(conninfo, passwd);
555  }
556  if (host) {
557  strcat(conninfo, " host=");
558  strcat(conninfo, host);
559  }
560  if (port) {
561  strcat(conninfo, " port=");
562  strcat(conninfo, port);
563  }
564  G_free(pg_info->conninfo);
565  pg_info->conninfo = G_store(conninfo);
566  }
567  }
568 
569  pg_info->conn = PQconnectdb(pg_info->conninfo);
570  G_debug(1, " PQconnectdb(): %s", pg_info->conninfo);
571  if (PQstatus(pg_info->conn) == CONNECTION_BAD)
572  G_fatal_error("%s\n%s",
573  _("Connection to PostgreSQL database failed. "
574  "Try to set up username/password by db.login."),
575  PQerrorMessage(pg_info->conn));
576 
577  /* get DB name */
578  pg_info->db_name = G_store(PQdb(pg_info->conn));
579  if (!pg_info->db_name)
580  G_warning(_("Unable to get database name"));
581 
582  sprintf(
583  stmt,
584  "SELECT COUNT(*) FROM pg_tables WHERE tablename = 'spatial_ref_sys'");
585  if (Vect__execute_get_value_pg(pg_info->conn, stmt) != 1) {
586  PQfinish(pg_info->conn);
587  G_fatal_error(_("<%s> is not PostGIS database. DB table "
588  "'spatial_ref_sys' not found."),
589  pg_info->db_name ? pg_info->db_name : pg_info->conninfo);
590  }
591 
592  if (pg_info->toposchema_name) {
593  /* check if topology schema exists */
594  sprintf(stmt,
595  "SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'topology'");
596  if (Vect__execute_get_value_pg(pg_info->conn, stmt) == 0) {
597  PQfinish(pg_info->conn);
599  _("PostGIS Topology extension not found in the database <%s>"),
600  pg_info->db_name);
601  }
602  }
603 
604  /* print notice messages only on verbose level */
605  PQsetNoticeProcessor(pg_info->conn, notice_processor, NULL);
606 }
607 
608 /*!
609  \brief Check for topology schema (pg_info->toposchema_name)
610 
611  \param pg_info pointer to Format_info_pg
612 
613  \return 0 schema exists
614  \return 1 schema doesn't exists
615  */
616 int check_topo(struct Format_info_pg *pg_info, struct Plus_head *plus)
617 {
618  char stmt[DB_SQL_MAX];
619 
620  PGresult *res;
621 
622  /* connect database */
623  if (!pg_info->conn)
624  connect_db(pg_info);
625 
626  if (pg_info->toposchema_name)
627  return 0;
628 
629  /* check if topology layer/schema exists */
630  sprintf(stmt,
631  "SELECT t.id,t.name,t.hasz,l.feature_column FROM topology.layer "
632  "AS l JOIN topology.topology AS t ON l.topology_id = t.id "
633  "WHERE schema_name = '%s' AND table_name = '%s'",
634  pg_info->schema_name, pg_info->table_name);
635  G_debug(2, "SQL: %s", stmt);
636 
637  res = PQexec(pg_info->conn, stmt);
638  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
639  G_debug(1, "Topology layers for '%s.%s' not found (%s)",
640  pg_info->schema_name, pg_info->table_name,
641  PQerrorMessage(pg_info->conn));
642  if (res)
643  PQclear(res);
644  return 1;
645  }
646 
647  pg_info->toposchema_id = atoi(PQgetvalue(res, 0, 0));
648  pg_info->toposchema_name = G_store(PQgetvalue(res, 0, 1));
649  pg_info->topogeom_column = G_store(PQgetvalue(res, 0, 3));
650 
651  /* check extra GRASS tables */
652  sprintf(stmt,
653  "SELECT COUNT(*) FROM pg_tables WHERE schemaname = '%s' "
654  "AND tablename LIKE '%%_grass'",
655  pg_info->toposchema_name);
656  if (Vect__execute_get_value_pg(pg_info->conn, stmt) != TOPO_TABLE_NUM)
657  pg_info->topo_geo_only = TRUE;
658 
659  G_debug(
660  1,
661  "PostGIS topology detected: schema = %s column = %s topo_geo_only = %d",
662  pg_info->toposchema_name, pg_info->topogeom_column,
663  pg_info->topo_geo_only);
664 
665  /* check for 3D */
666  if (strcmp(PQgetvalue(res, 0, 2), "t") == 0)
667  plus->with_z = WITH_Z;
668  PQclear(res);
669 
670  return 0;
671 }
672 
673 /*!
674  \brief Parse BBOX string
675 
676  \param value string buffer
677  \param[out] bbox pointer to output bound_box struct
678 
679  \return 0 on success
680  \return -1 on error
681  */
682 int parse_bbox(const char *value, struct bound_box *bbox)
683 {
684  unsigned int i;
685  size_t length, prefix_length;
686  char **tokens, **tokens_coord, *coord;
687 
688  if (strlen(value) < 1) {
689  G_warning(_("Empty bounding box"));
690  return -1;
691  }
692 
693  prefix_length = strlen("box3d(");
694  if (G_strncasecmp(value, "box3d(", prefix_length) != 0)
695  return -1;
696 
697  /* strip off "bbox3d(...)" */
698  length = strlen(value);
699  coord = G_malloc(length - prefix_length);
700  for (i = prefix_length; i < length; i++)
701  coord[i - prefix_length] = value[i];
702  coord[length - prefix_length - 1] = '\0';
703 
704  tokens = G_tokenize(coord, ",");
705  G_free(coord);
706 
707  if (G_number_of_tokens(tokens) != 2) {
708  G_free_tokens(tokens);
709  return -1;
710  }
711 
712  /* parse bbox LL corner */
713  tokens_coord = G_tokenize(tokens[0], " ");
714  if (G_number_of_tokens(tokens_coord) != 3) {
715  G_free_tokens(tokens);
716  G_free_tokens(tokens_coord);
717  }
718  bbox->W = atof(tokens_coord[0]);
719  bbox->S = atof(tokens_coord[1]);
720  bbox->B = atof(tokens_coord[2]);
721 
722  G_free_tokens(tokens_coord);
723 
724  /* parse bbox UR corner */
725  tokens_coord = G_tokenize(tokens[1], " ");
726  if (G_number_of_tokens(tokens_coord) != 3) {
727  G_free_tokens(tokens);
728  G_free_tokens(tokens_coord);
729  }
730  bbox->E = atof(tokens_coord[0]);
731  bbox->N = atof(tokens_coord[1]);
732  bbox->T = atof(tokens_coord[2]);
733 
734  G_free_tokens(tokens_coord);
735  G_free_tokens(tokens);
736 
737  return 0;
738 }
739 
740 /*!
741  \brief Read P_node structure
742 
743  See dig_Rd_P_node() for reference.
744 
745  \param plus pointer to Plus_head structure
746  \param n index (starts at 1)
747  \param id node id (table "node")
748  \param wkb_data geometry data (wkb)
749  \param lines_data lines array or NULL
750  \param angles_data angles array or NULL
751  \param pg_info pointer to Format_info_pg sttucture
752  \param geom_only TRUE to read node's geometry
753 
754  \return pointer to new P_node struct
755  \return NULL on error
756  */
757 struct P_node *read_p_node(struct Plus_head *plus, int n, int id,
758  const char *wkb_data, const char *lines_data,
759  const char *angles_data,
760  struct Format_info_pg *pg_info, int geom_only)
761 {
762  int i, cnt;
763  char **lines, **angles;
764 
765  struct P_node *node;
766  struct line_pnts *points;
767 
768  PGresult *res;
769 
770  /* get lines connected to the node */
771  cnt = 0;
772  lines = angles = NULL;
773  if (!geom_only) {
774  if (!lines_data && !angles_data) { /* pg_info->topo_geo_only == TRUE */
775  char stmt[DB_SQL_MAX];
776 
777  sprintf(
778  stmt,
779  "SELECT edge_id,'s' as node,"
780  "ST_Azimuth(ST_StartPoint(geom), ST_PointN(geom, 2)) AS angle"
781  " FROM \"%s\".edge WHERE start_node = %d UNION ALL "
782  "SELECT edge_id,'e' as node,"
783  "ST_Azimuth(ST_EndPoint(geom), ST_PointN(geom, "
784  "ST_NumPoints(geom) - 1)) AS angle"
785  " FROM \"%s\".edge WHERE end_node = %d"
786  " ORDER BY angle DESC",
787  pg_info->toposchema_name, id, pg_info->toposchema_name, id);
788  G_debug(2, "SQL: %s", stmt);
789  res = PQexec(pg_info->conn, stmt);
790  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
791  G_warning(
792  _("Inconsistency in topology: unable to read node %d"), id);
793  if (res)
794  PQclear(res);
795  return NULL;
796  }
797  cnt = PQntuples(res);
798  }
799  else { /* pg_info->topo_geo_only != TRUE */
800  lines = scan_array(lines_data);
801  angles = scan_array(angles_data);
802 
803  cnt = G_number_of_tokens(lines);
804  if (cnt != G_number_of_tokens(angles))
805  return NULL; /* 'lines' and 'angles' array must have the same
806  size */
807  }
808 
809  if (cnt == 0) { /* dead */
810  plus->Node[n] = NULL;
811  return NULL;
812  }
813  }
814 
815  node = dig_alloc_node();
816  node->n_lines = cnt;
817  G_debug(4, "read_p_node(): id = %d, n_lines = %d", id, cnt);
818 
819  if (!geom_only) {
820  if (dig_node_alloc_line(node, node->n_lines) == -1)
821  return NULL;
822 
823  /* lines / angles */
824  if (lines) {
825  for (i = 0; i < node->n_lines; i++) {
826  node->lines[i] = atoi(lines[i]);
827  node->angles[i] = atof(angles[i]);
828 
829  G_debug(5, "\tline = %d angle = %f", node->lines[i],
830  node->angles[i]);
831  }
832 
833  G_free_tokens(lines);
834  G_free_tokens(angles);
835  }
836  else {
837  for (i = 0; i < node->n_lines; i++) {
838  node->lines[i] = atoi(PQgetvalue(res, i, 0));
839  if (strcmp(PQgetvalue(res, i, 1), "s") != 0) {
840  /* end node */
841  node->lines[i] *= -1;
842  }
843  node->angles[i] = M_PI / 2 - atof(PQgetvalue(res, i, 2));
844  /* angles range <-PI; PI> */
845  if (node->angles[i] > M_PI)
846  node->angles[i] = node->angles[i] - 2 * M_PI;
847  if (node->angles[i] < -1.0 * M_PI)
848  node->angles[i] = node->angles[i] + 2 * M_PI;
849  G_debug(5, "\tline = %d angle = %f", node->lines[i],
850  node->angles[i]);
851  }
852  PQclear(res);
853  }
854  }
855 
856  /* get node coordinates */
857  if (SF_POINT !=
858  Vect__cache_feature_pg(wkb_data, FALSE, FALSE, &(pg_info->cache), NULL))
859  G_warning(_("Inconsistency in topology: node %d - unexpected feature "
860  "type %d"),
861  n, pg_info->cache.sf_type);
862 
863  points = pg_info->cache.lines[0];
864  node->x = points->x[0];
865  node->y = points->y[0];
866  if (plus->with_z)
867  node->z = points->z[0];
868  else
869  node->z = 0.0;
870 
871  /* update spatial index */
872  if (plus->Spidx_new)
873  dig_spidx_add_node(plus, n, node->x, node->y, node->z);
874 
875  if (plus->uplist.do_uplist)
876  /* collect updated nodes if requested */
877  dig_node_add_updated(plus, n);
878 
879  plus->Node[n] = node;
880 
881  return node;
882 }
883 
884 /*!
885  \brief Read P_line structure
886 
887  See dig_Rd_P_line() for reference.
888 
889  Supported feature types:
890  - GV_POINT
891  - GV_LINE
892  - GV_BOUNDARY
893 
894  \param plus pointer to Plus_head structure
895  \param n index (starts at 1)
896  \param data edge data (id, start/end node, left/right face, ...)
897  \param topo_geo_only TRUE for topo-geo-only mode
898  \param cache pointer to Format_info_cache structure
899 
900  \return pointer to P_line struct
901  \return NULL on error
902  */
903 struct P_line *read_p_line(struct Plus_head *plus, int n,
904  const struct line_data *data, int topo_geo_only,
905  struct Format_info_cache *cache)
906 {
907  int tp, cat;
908  struct P_line *line;
909 
910  if (data->start_node == 0 && data->end_node == 0) {
911  if (data->left_face == 0)
912  tp = GV_POINT;
913  else
914  tp = GV_CENTROID;
915  }
916  else if (data->left_face == 0 && data->right_face == 0) {
917  tp = GV_LINE;
918  }
919  else {
920  tp = GV_BOUNDARY;
921  }
922 
923  if (tp == 0) { /* dead ??? */
924  plus->Line[n] = NULL;
925  return NULL;
926  }
927 
928  line = dig_alloc_line();
929 
930  /* type & offset ( = id) */
931  line->type = tp;
932  line->offset = data->id;
933  G_debug(4, "read_p_line(): id/offset = %d type = %d", data->id, line->type);
934 
935  /* topo */
936  if (line->type == GV_POINT) {
937  line->topo = NULL;
938  }
939  else {
940  line->topo = dig_alloc_topo(line->type);
941 
942  if ((line->type & GV_LINES) &
943  (data->start_node < 0 || data->end_node < 0))
944  return NULL;
945 
946  /* lines */
947  if (line->type == GV_LINE) {
948  struct P_topo_l *topo = (struct P_topo_l *)line->topo;
949 
950  topo->N1 = data->start_node;
951  topo->N2 = data->end_node;
952  }
953  /* boundaries */
954  else if (line->type == GV_BOUNDARY) {
955  struct P_topo_b *topo = (struct P_topo_b *)line->topo;
956 
957  topo->N1 = data->start_node;
958  topo->N2 = data->end_node;
959 
960  if (topo_geo_only) {
961  topo->left = topo->right = 0;
962  }
963  else {
964  topo->left = data->left_face;
965  topo->right = data->right_face;
966  }
967  }
968  /* centroids */
969  else if (line->type == GV_CENTROID) {
970  struct P_topo_c *topo = (struct P_topo_c *)line->topo;
971 
972  topo->area = data->left_face;
973  }
974  }
975 
976  Vect__cache_feature_pg(data->wkb_geom, FALSE, tp, cache, NULL);
977  cat = cache->lines_cats[cache->lines_num - 1] =
978  data->fid > 0 ? data->fid : -1;
979 
980  /* update spatial index */
981  if (plus->Spidx_new) {
982  struct line_pnts *points;
983  struct bound_box box;
984 
985  points = cache->lines[cache->lines_num - 1];
986  dig_line_box(points, &box);
987  dig_spidx_add_line(plus, n, &box);
988  }
989 
990  /* update category index */
991  if (plus->update_cidx)
992  dig_cidx_add_cat(plus, cat > 0 ? 1 : 0, cat > 0 ? cat : 0, n, tp);
993 
994  if (plus->uplist.do_uplist) {
995  /* collect updated lines if requested */
996  dig_line_add_updated(plus, n, line->offset);
997  }
998 
999  plus->Line[n] = line;
1000 
1001  return line;
1002 }
1003 
1004 /*!
1005  \brief Read P_area structure
1006 
1007  \param plus pointer to Plus_head structure
1008  \param n index (starts at 1)
1009  \param lines_data lines array (see P_area struct)
1010  \param centroid centroid id (see P_area struct)
1011  \param isles_data lines array (see P_area struct)
1012 
1013  \return pointer to P_area struct
1014  \return NULL on error
1015  */
1016 struct P_area *read_p_area(struct Plus_head *plus, int n,
1017  const char *lines_data, int centroid,
1018  const char *isles_data)
1019 {
1020  int i;
1021  int nlines, nisles;
1022  char **lines, **isles;
1023 
1024  struct P_area *area;
1025 
1026  lines = scan_array(lines_data);
1027  nlines = G_number_of_tokens(lines);
1028  isles = scan_array(isles_data);
1029  nisles = G_number_of_tokens(isles);
1030 
1031  if (nlines < 1) {
1032  G_warning(_("Area %d without boundary detected"), n);
1033  return NULL;
1034  }
1035 
1036  G_debug(3, "read_p_area(): n = %d nlines = %d nisles = %d", n, nlines,
1037  nisles);
1038 
1039  /* allocate area */
1040  area = dig_alloc_area();
1041  dig_area_alloc_line(area, nlines);
1042  dig_area_alloc_isle(area, nisles);
1043 
1044  /* set lines */
1045  area->n_lines = nlines;
1046  for (i = 0; i < nlines; i++) {
1047  area->lines[i] = atoi(lines[i]);
1048  }
1049 
1050  /* set isles */
1051  area->n_isles = nisles;
1052  for (i = 0; i < nisles; i++) {
1053  area->isles[i] = atoi(isles[i]);
1054  }
1055 
1056  /* set centroid */
1057  area->centroid = remap_line(plus, centroid, GV_CENTROID);
1058 
1061 
1062  plus->Area[n] = area;
1063 
1064  return area;
1065 }
1066 
1067 /*!
1068  \brief Read P_isle structure
1069 
1070  \param plus pointer to Plus_head structure
1071  \param n index (starts at 1)
1072  \param lines_data lines array (see P_isle struct)
1073  \param area area id (see P_isle struct)
1074 
1075  \return pointer to P_isle struct
1076  \return NULL on error
1077  */
1078 struct P_isle *read_p_isle(struct Plus_head *plus, int n,
1079  const char *lines_data, int area)
1080 {
1081  int i;
1082  int nlines;
1083  char **lines;
1084 
1085  struct P_isle *isle;
1086 
1087  lines = scan_array(lines_data);
1088  nlines = G_number_of_tokens(lines);
1089 
1090  if (nlines < 1) {
1091  G_warning(_("Isle %d without boundary detected"), n);
1092  return NULL;
1093  }
1094 
1095  G_debug(3, "read_p_isle(): n = %d nlines = %d", n, nlines);
1096 
1097  /* allocate isle */
1098  isle = dig_alloc_isle();
1099  dig_isle_alloc_line(isle, nlines);
1100 
1101  /* set lines */
1102  isle->n_lines = nlines;
1103  for (i = 0; i < nlines; i++) {
1104  isle->lines[i] = atoi(lines[i]);
1105  }
1106 
1107  /* set area */
1108  isle->area = area;
1109 
1111 
1112  plus->Isle[n] = isle;
1113 
1114  return isle;
1115 }
1116 
1117 /*!
1118  \brief Read topo from PostGIS topology schema -- header info only
1119 
1120  \param[in,out] plus pointer to Plus_head struct
1121 
1122  \return 0 on success
1123  \return -1 on error
1124  */
1126 {
1127  char stmt[DB_SQL_MAX];
1128 
1129  struct Format_info_pg *pg_info;
1130  struct Plus_head *plus;
1131 
1132  PGresult *res;
1133 
1134  plus = &(Map->plus);
1135  pg_info = &(Map->fInfo.pg);
1136 
1137  plus->off_t_size = -1;
1138 
1139  /* get map bounding box
1140  first try to get info from 'topology.grass' table */
1141  sprintf(stmt, "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d", TOPO_BBOX,
1142  TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, pg_info->toposchema_id);
1143  G_debug(2, "SQL: %s", stmt);
1144  res = PQexec(pg_info->conn, stmt);
1145  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
1146  PQclear(res);
1147 
1148  /* otherwise try to calculate bbox from TopoGeometry elements */
1149  sprintf(stmt, "SELECT ST_3DExtent(%s) FROM \"%s\".\"%s\"",
1150  pg_info->topogeom_column, pg_info->schema_name,
1151  pg_info->table_name);
1152  G_debug(2, "SQL: %s", stmt);
1153  res = PQexec(pg_info->conn, stmt);
1154  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1155  PQntuples(res) != 1 || strlen(PQgetvalue(res, 0, 0)) < 1) {
1156  G_warning(_("Unable to get map bounding box from topology"));
1157  PQclear(res);
1158  return -1;
1159  }
1160  }
1161 
1162  if (parse_bbox(PQgetvalue(res, 0, 0), &(plus->box)) != 0) {
1163  G_warning(_("Unable to parse map bounding box:\n%s"),
1164  PQgetvalue(res, 0, 0));
1165  return -1;
1166  }
1167  PQclear(res);
1168 
1169  /* get number of topological elements */
1170 
1171  /* nodes
1172  note: isolated nodes are registered in GRASS Topology model */
1173  sprintf(stmt,
1174  "SELECT COUNT(DISTINCT node) FROM (SELECT start_node AS node "
1175  "FROM \"%s\".edge GROUP BY start_node UNION ALL SELECT end_node "
1176  "AS node FROM \"%s\".edge GROUP BY end_node) AS foo",
1177  pg_info->toposchema_name, pg_info->toposchema_name);
1178  plus->n_nodes = Vect__execute_get_value_pg(pg_info->conn, stmt);
1179  if (!pg_info->topo_geo_only) {
1180  int n_nodes;
1181 
1182  /* check nodes consistency */
1183  sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".%s",
1184  pg_info->toposchema_name, TOPO_TABLE_NODE);
1185  n_nodes = Vect__execute_get_value_pg(pg_info->conn, stmt);
1186  if (n_nodes != plus->n_nodes) {
1187  G_warning(_("Different number of nodes detected (%d, %d)"),
1188  plus->n_nodes, n_nodes);
1189  return -1;
1190  }
1191  }
1192  G_debug(3, "Vect_open_topo_pg(): n_nodes=%d", plus->n_nodes);
1193 
1194  /* lines (edges in PostGIS Topology model) */
1195  sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".edge", pg_info->toposchema_name);
1196  /* + isolated nodes as points
1197  + centroids */
1198  plus->n_lines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1199 
1200  /* areas (faces with face_id > 0 in PostGIS Topology model) */
1201  sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".face WHERE face_id > 0",
1202  pg_info->toposchema_name);
1203  plus->n_areas = Vect__execute_get_value_pg(pg_info->conn, stmt);
1204  if (!pg_info->topo_geo_only) {
1205  int n_areas;
1206 
1207  /* check areas consistency */
1208  sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".%s",
1209  pg_info->toposchema_name, TOPO_TABLE_AREA);
1210  n_areas = Vect__execute_get_value_pg(pg_info->conn, stmt);
1211  if (n_areas != plus->n_areas) {
1212  G_warning(_("Different number of areas detected (%d, %d)"),
1213  plus->n_areas, n_areas);
1214  return -1;
1215  }
1216  }
1217  G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
1218 
1219  /* isles (faces with face_id <=0 in PostGIS Topology model)
1220  note: universal face is represented in GRASS Topology model as isle
1221  (area=0)
1222  */
1223  sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".face WHERE face_id < 0",
1224  pg_info->toposchema_name);
1225  plus->n_isles = Vect__execute_get_value_pg(pg_info->conn, stmt);
1226  if (!pg_info->topo_geo_only) {
1227  int n_isles;
1228 
1229  /* check areas consistency */
1230  sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".%s",
1231  pg_info->toposchema_name, TOPO_TABLE_ISLE);
1232  n_isles = Vect__execute_get_value_pg(pg_info->conn, stmt);
1233  if (n_isles != plus->n_isles) {
1234  G_warning(_("Different number of areas detected (%d, %d)"),
1235  plus->n_isles, n_isles);
1236  return -1;
1237  }
1238  }
1239  G_debug(3, "Vect_open_topo_pg(): n_isles=%d", plus->n_isles);
1240 
1241  /* number of features according the type */
1242 
1243  /* points */
1244  sprintf(stmt,
1245  "SELECT COUNT(*) FROM \"%s\".node WHERE containing_face "
1246  "IS NULL AND node_id NOT IN "
1247  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1248  "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1249  "\"%s\".edge GROUP BY end_node) AS foo)",
1250  pg_info->toposchema_name, pg_info->toposchema_name,
1251  pg_info->toposchema_name);
1252  plus->n_plines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1253  G_debug(3, "Vect_open_topo_pg(): n_plines=%d", plus->n_plines);
1254 
1255  /* lines */
1256  sprintf(stmt,
1257  "SELECT COUNT(*) FROM \"%s\".edge WHERE "
1258  "left_face = 0 AND right_face = 0",
1259  pg_info->toposchema_name);
1260  plus->n_llines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1261  G_debug(3, "Vect_open_topo_pg(): n_llines=%d", plus->n_llines);
1262 
1263  /* boundaries */
1264  sprintf(stmt,
1265  "SELECT COUNT(*) FROM \"%s\".edge WHERE "
1266  "left_face != 0 OR right_face != 0",
1267  pg_info->toposchema_name);
1268  plus->n_blines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1269  G_debug(3, "Vect_open_topo_pg(): n_blines=%d", plus->n_blines);
1270 
1271  /* centroids */
1272  sprintf(stmt,
1273  "SELECT COUNT(*) FROM \"%s\".node WHERE containing_face "
1274  "IS NOT NULL AND node_id NOT IN "
1275  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1276  "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1277  "\"%s\".edge GROUP BY end_node) AS foo)",
1278  pg_info->toposchema_name, pg_info->toposchema_name,
1279  pg_info->toposchema_name);
1280  plus->n_clines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1281  G_debug(3, "Vect_open_topo_pg(): n_clines=%d", plus->n_clines);
1282 
1283  /* update number of lines - add points and centroids */
1284  plus->n_lines += plus->n_plines + plus->n_clines;
1285  G_debug(3, "Vect_open_topo_pg(): n_lines=%d", plus->n_lines);
1286 
1287  return 0;
1288 }
1289 
1290 /*!
1291  \brief Read topo info from PostGIS topology schema
1292 
1293  \param pg_info pointer to Format_info_pg
1294  \param[in,out] plus pointer to Plus_head struct
1295  \param head_only TRUE to read only header info
1296 
1297  \return 0 on success
1298  \return -1 on error
1299  */
1300 int Vect__load_plus_pg(struct Map_info *Map, int head_only)
1301 {
1302  int i, side, line;
1303  char stmt[DB_SQL_MAX];
1304 
1305  struct Format_info_pg *pg_info;
1306  struct Plus_head *plus;
1307  struct P_line *Line;
1308  struct line_pnts *Points;
1309  struct ilist *List;
1310  struct bound_box box;
1311 
1312  PGresult *res;
1313 
1314  pg_info = &(Map->fInfo.pg);
1315  plus = &(Map->plus);
1316 
1317  if (Vect__load_plus_head(Map) != 0)
1318  return -1;
1319 
1320  if (head_only)
1321  return 0;
1322 
1323  Points = Vect_new_line_struct();
1324  List = Vect_new_list();
1325 
1326  /* read nodes (GRASS Topo)
1327  note: standalone nodes (ie. points/centroids) are ignored
1328  */
1330 
1331  /* read lines (GRASS Topo)
1332  - standalone nodes -> points|centroids
1333  - edges -> lines/boundaries
1334  */
1335  Vect__free_cache(&(pg_info->cache));
1336  pg_info->cache.ctype = CACHE_MAP;
1337 
1339 
1340  /* build areas */
1341  if (pg_info->topo_geo_only) {
1342  /* build areas for boundaries
1343  reset values -> build from scratch */
1344  plus->n_areas = plus->n_isles = 0;
1345  for (line = 1; line <= plus->n_lines; line++) {
1346  Line = plus->Line[line]; /* centroids: Line is NULL */
1347  if (!Line || Line->type != GV_BOUNDARY)
1348  continue;
1349 
1350  for (i = 0; i < 2; i++) { /* for both sides build an area/isle */
1351  side = i == 0 ? GV_LEFT : GV_RIGHT;
1352 
1353  G_debug(3, "Build area for line = %d, side = %d", i, side);
1354  Vect_build_line_area(Map, line, side);
1355  }
1356  }
1357  }
1358  else {
1359  int cat;
1360 
1361  /* read areas from 'area_grass' table */
1362  sprintf(stmt,
1363  "SELECT area_id,lines,centroid,isles FROM \"%s\".%s ORDER BY "
1364  "area_id",
1365  pg_info->toposchema_name, TOPO_TABLE_AREA);
1366  G_debug(2, "SQL: %s", stmt);
1367 
1368  res = PQexec(pg_info->conn, stmt);
1369  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1370  plus->n_areas != PQntuples(res)) {
1371  if (res)
1372  PQclear(res);
1373  return -1;
1374  }
1375 
1376  dig_alloc_areas(plus, plus->n_areas);
1377  G_zero(plus->Area, sizeof(struct P_area *) *
1378  (plus->n_areas + 1)); /* index starts at 1 */
1379  G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
1380 
1381  for (i = 0; i < plus->n_areas; i++) {
1382  read_p_area(plus, i + 1, (char *)PQgetvalue(res, i, 1),
1383  atoi(PQgetvalue(res, i, 2)),
1384  (char *)PQgetvalue(res, i, 3));
1385 
1386  if (plus->Spidx_new) {
1387  /* update spatial index */
1388  Vect_get_area_points(Map, i + 1, Points);
1389  dig_line_box(Points, &box);
1390  dig_spidx_add_area(&(Map->plus), i + 1, &box);
1391  }
1392 
1393  if (plus->update_cidx) {
1394  /* update category index */
1395  cat =
1396  pg_info->cache.lines_cats[plus->Area[i + 1]->centroid - 1];
1397  dig_cidx_add_cat(plus, cat > 0 ? 1 : 0, cat > 0 ? cat : 0,
1398  i + 1, GV_AREA);
1399  }
1400  }
1401  PQclear(res);
1402  }
1403  plus->built = GV_BUILD_AREAS;
1404 
1405  /* attach isles */
1406  if (pg_info->topo_geo_only) {
1407  plus->n_isles = 0; /* reset isles */
1408  G_warning(
1409  _("To be implemented: isles not attached in Topo-Geo-only mode"));
1410  }
1411  else {
1412  /* read isles from 'isle_grass' table */
1413  sprintf(stmt,
1414  "SELECT isle_id,lines,area FROM \"%s\".%s ORDER BY isle_id",
1415  pg_info->toposchema_name, TOPO_TABLE_ISLE);
1416  G_debug(2, "SQL: %s", stmt);
1417 
1418  res = PQexec(pg_info->conn, stmt);
1419  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1420  plus->n_isles != PQntuples(res)) {
1421  if (res)
1422  PQclear(res);
1423  return -1;
1424  }
1425 
1426  dig_alloc_isles(plus, plus->n_isles);
1427  G_zero(plus->Isle, sizeof(struct P_isle *) *
1428  (plus->n_isles + 1)); /* index starts at 1 */
1429  G_debug(3, "Vect_open_topo_pg(): n_isles=%d", plus->n_isles);
1430 
1431  for (i = 0; i < plus->n_isles; i++) {
1432  read_p_isle(plus, i + 1, (char *)PQgetvalue(res, i, 1),
1433  atoi(PQgetvalue(res, i, 2)));
1434 
1435  if (plus->Spidx_new) {
1436  /* update spatial index */
1437  Vect_get_isle_points(Map, i + 1, Points);
1438  dig_line_box(Points, &box);
1439  dig_spidx_add_isle(&(Map->plus), i + 1, &box);
1440  }
1441  }
1442  PQclear(res);
1443  }
1444  plus->built = GV_BUILD_ATTACH_ISLES;
1445 
1446  /* attach centroids */
1447  if (pg_info->topo_geo_only && plus->n_areas > 0) {
1448  int area;
1449  struct P_area *Area;
1450  struct P_topo_c *topo;
1451 
1452  for (line = 1; line <= plus->n_lines; line++) {
1453  Line = plus->Line[line];
1454  if (Line->type != GV_CENTROID)
1455  continue;
1456 
1457  Vect_read_line(Map, Points, NULL, line);
1458  area = Vect_find_area(Map, Points->x[0], Points->y[0]);
1459  topo = (struct P_topo_c *)Line->topo;
1460  topo->area = area;
1461  Area = plus->Area[topo->area];
1462  Area->centroid = Line->offset;
1463  }
1464  }
1465  plus->built = GV_BUILD_CENTROIDS;
1466 
1467  /* done */
1468  plus->built = GV_BUILD_ALL;
1469 
1470  Vect_destroy_line_struct(Points);
1471  Vect_destroy_list(List);
1472 
1473  return 0;
1474 }
1475 
1476 /*!
1477  \brief Read nodes from DB
1478 
1479  \param Map pointer to Map_info struct
1480  \param geom_only read only node's geometry
1481 
1482  \return number of nodes
1483  */
1484 int Vect__load_map_nodes_pg(struct Map_info *Map, int geom_only)
1485 {
1486  int i, id, n_nodes;
1487  char stmt[DB_SQL_MAX];
1488  struct Plus_head *plus;
1489  struct Format_info_pg *pg_info;
1490  struct Format_info_offset *offset;
1491 
1492  PGresult *res;
1493 
1494  plus = &(Map->plus);
1495  pg_info = &(Map->fInfo.pg);
1496  offset = &(pg_info->offset);
1497 
1498  if (pg_info->topo_geo_only || geom_only)
1499  sprintf(stmt,
1500  "SELECT node_id,geom FROM \"%s\".node WHERE node_id IN "
1501  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1502  "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1503  "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
1504  pg_info->toposchema_name, pg_info->toposchema_name,
1505  pg_info->toposchema_name);
1506  else
1507  sprintf(
1508  stmt,
1509  "SELECT node.node_id,geom,lines,angles FROM \"%s\".node AS node "
1510  "JOIN \"%s\".%s AS node_grass ON node.node_id = node_grass.node_id "
1511  "ORDER BY node_id",
1512  pg_info->toposchema_name, pg_info->toposchema_name,
1513  TOPO_TABLE_NODE);
1514  G_debug(2, "SQL: %s", stmt);
1515  res = PQexec(pg_info->conn, stmt);
1516  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1517  (!geom_only && PQntuples(res) != plus->n_nodes)) {
1518  G_warning(_("Inconsistency in topology: number of "
1519  "nodes %d (should be %d)"),
1520  PQntuples(res), plus->n_nodes);
1521  if (res)
1522  PQclear(res);
1523  return -1;
1524  }
1525 
1526  n_nodes = PQntuples(res);
1527  G_debug(3, "load_plus(): n_nodes = %d", n_nodes);
1528  dig_alloc_nodes(plus, n_nodes);
1529  offset->array = (int *)G_malloc(sizeof(int) * n_nodes);
1530  offset->array_num = offset->array_alloc = n_nodes;
1531  for (i = 0; i < n_nodes; i++) {
1532  G_debug(5, "node: %d", i);
1533  id = atoi(PQgetvalue(res, i, 0));
1534  read_p_node(
1535  plus, i + 1, /* node index starts at 1 */
1536  id, (const char *)PQgetvalue(res, i, 1),
1537  !pg_info->topo_geo_only ? (const char *)PQgetvalue(res, i, 2)
1538  : NULL,
1539  !pg_info->topo_geo_only ? (const char *)PQgetvalue(res, i, 3)
1540  : NULL,
1541  pg_info, geom_only);
1542  /* update offset */
1543  offset->array[i] = id;
1544  }
1545  PQclear(res);
1546 
1547  return n_nodes;
1548 }
1549 
1550 /*!
1551  \brief Read features from DB
1552 
1553  \param Map pointer to Map_info struct
1554 
1555  \return number of features
1556  */
1558 {
1559  int i, id, ntuples;
1560 
1561  char stmt[DB_SQL_MAX];
1562 
1563  struct Plus_head *plus;
1564  struct Format_info_pg *pg_info;
1565  struct line_data line_data;
1566  struct Format_info_offset *offset;
1567 
1568  PGresult *res;
1569 
1570  plus = &(Map->plus);
1571  pg_info = &(Map->fInfo.pg);
1572  offset = &(pg_info->offset);
1573 
1574  dig_alloc_lines(plus, plus->n_lines);
1575  G_zero(plus->Line, sizeof(struct P_line *) *
1576  (plus->n_lines + 1)); /* index starts at 1 */
1577 
1578  /* read PostGIS Topo standalone nodes (containing_face is null)
1579  -> points
1580  */
1581  if (pg_info->topo_geo_only)
1582  sprintf(stmt,
1583  "SELECT tt.node_id,tt.geom,ft.%s FROM \"%s\".node AS tt "
1584  "LEFT JOIN \"%s\".\"%s\" AS ft ON "
1585  "(%s).type = 1 AND (%s).id = node_id WHERE containing_face "
1586  "IS NULL AND node_id NOT IN "
1587  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1588  "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1589  "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
1590  pg_info->fid_column, pg_info->toposchema_name,
1591  pg_info->schema_name, pg_info->table_name,
1592  pg_info->topogeom_column, pg_info->topogeom_column,
1593  pg_info->toposchema_name, pg_info->toposchema_name);
1594  else
1595  sprintf(stmt,
1596  "SELECT tt.node_id,tt.geom,ft.%s "
1597  "FROM \"%s\".node AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1598  "(%s).type = 1 AND (%s).id = node_id WHERE node_id NOT IN "
1599  "(SELECT node_id FROM \"%s\".%s) AND containing_face IS NULL "
1600  "ORDER BY node_id",
1601  pg_info->fid_column, pg_info->toposchema_name,
1602  pg_info->schema_name, pg_info->table_name,
1603  pg_info->topogeom_column, pg_info->topogeom_column,
1604  pg_info->toposchema_name, TOPO_TABLE_NODE);
1605  G_debug(2, "SQL: %s", stmt);
1606  res = PQexec(pg_info->conn, stmt);
1607  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1608  PQntuples(res) > plus->n_plines) {
1609  G_warning(_("Inconsistency in topology: number of "
1610  "points %d (should be %d)"),
1611  PQntuples(res), plus->n_plines);
1612  if (res)
1613  PQclear(res);
1614  return -1;
1615  }
1616 
1617  ntuples = PQntuples(res); /* plus->n_plines */
1618  G_zero(&line_data, sizeof(struct line_data));
1619  for (i = 0; i < ntuples; i++) {
1620  /* process standalone nodes (PostGIS Topo) */
1621  line_data.id = atoi(PQgetvalue(res, i, 0));
1622  line_data.wkb_geom = (char *)PQgetvalue(res, i, 1);
1623  line_data.fid = atoi(PQgetvalue(res, i, 2)); /* feature id */
1624 
1625  read_p_line(plus, i + 1, &line_data, pg_info->topo_geo_only,
1626  &(pg_info->cache));
1627  }
1628  PQclear(res);
1629 
1630  /* read PostGIS Topo edges
1631  -> lines
1632  -> boundaries
1633  */
1634  if (pg_info->topo_geo_only)
1635  sprintf(stmt,
1636  "SELECT edge_id,start_node,end_node,left_face,right_face AS "
1637  "right_area,tt.geom,ft.%s "
1638  "FROM \"%s\".edge AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1639  "(%s).type = 2 AND "
1640  "(%s).id = edge_id ORDER BY edge_id",
1641  pg_info->fid_column, pg_info->toposchema_name,
1642  pg_info->schema_name, pg_info->table_name,
1643  pg_info->topogeom_column, pg_info->topogeom_column);
1644  else
1645  sprintf(
1646  stmt,
1647  "SELECT "
1648  "edge_id,start_node,end_node,left_area,right_area,tt.geom,ft.%s "
1649  "FROM \"%s\".edge AS tt LEFT JOIN \"%s\".\"%s\" ON "
1650  "edge_id = line_id LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 "
1651  "AND "
1652  "(%s).id = edge_id ORDER BY edge_id",
1653  pg_info->fid_column, pg_info->toposchema_name,
1654  pg_info->toposchema_name, TOPO_TABLE_LINE, pg_info->schema_name,
1655  pg_info->table_name, pg_info->topogeom_column,
1656  pg_info->topogeom_column);
1657 
1658  G_debug(2, "SQL: %s", stmt);
1659  res = PQexec(pg_info->conn, stmt);
1660  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1661  PQntuples(res) > plus->n_lines) {
1662  G_warning(_("Inconsistency in topology: number of "
1663  "lines %d (should be %d)"),
1664  PQntuples(res), plus->n_lines);
1665  if (res)
1666  PQclear(res);
1667  return -1;
1668  }
1669 
1670  /* process edges (PostGIS Topo) */
1671  ntuples = PQntuples(res);
1672  for (i = 0; i < ntuples; i++) {
1673  line_data.id = atoi(PQgetvalue(res, i, 0));
1674  line_data.start_node = remap_node(offset, atoi(PQgetvalue(res, i, 1)));
1675  line_data.end_node = remap_node(offset, atoi(PQgetvalue(res, i, 2)));
1676  line_data.left_face = atoi(PQgetvalue(res, i, 3));
1677  line_data.right_face = atoi(PQgetvalue(res, i, 4));
1678  line_data.wkb_geom = (char *)PQgetvalue(res, i, 5);
1679  line_data.fid = atoi(PQgetvalue(res, i, 6)); /* feature id */
1680 
1681  id = plus->n_plines + i + 1; /* points already registered */
1682  read_p_line(plus, id, &line_data, pg_info->topo_geo_only,
1683  &(pg_info->cache));
1684  }
1685  PQclear(res);
1686 
1687  /* read PostGIS Topo standalone nodes (containing_face is not null)
1688  -> centroids
1689  */
1690  if (pg_info->topo_geo_only)
1691  sprintf(
1692  stmt,
1693  "SELECT node_id,tt.geom,containing_face,ft.%s FROM "
1694  "\"%s\".node AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1695  "(%s).type = 3 AND (%s).id = containing_face WHERE containing_face "
1696  "IS NOT NULL AND node_id NOT IN "
1697  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1698  "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1699  "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
1700  pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name,
1701  pg_info->table_name, pg_info->topogeom_column,
1702  pg_info->topogeom_column, pg_info->toposchema_name,
1703  pg_info->toposchema_name);
1704  else
1705  sprintf(stmt,
1706  "SELECT tt.node_id,tt.geom,containing_face,ft.%s FROM "
1707  "\"%s\".node AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1708  "(%s).type = 3 AND (%s).id = containing_face WHERE "
1709  "node_id NOT IN (SELECT node_id FROM \"%s\".%s) AND "
1710  "containing_face "
1711  "IS NOT NULL ORDER BY node_id",
1712  pg_info->fid_column, pg_info->toposchema_name,
1713  pg_info->schema_name, pg_info->table_name,
1714  pg_info->topogeom_column, pg_info->topogeom_column,
1715  pg_info->toposchema_name, TOPO_TABLE_NODE);
1716  G_debug(2, "SQL: %s", stmt);
1717  res = PQexec(pg_info->conn, stmt);
1718  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1719  PQntuples(res) != plus->n_clines) {
1720  G_warning(_("Inconsistency in topology: number of "
1721  "centroids %d (should be %d)"),
1722  PQntuples(res), plus->n_clines);
1723  if (res)
1724  PQclear(res);
1725  return -1;
1726  }
1727 
1728  G_zero(&line_data, sizeof(struct line_data));
1729  id = plus->n_plines + plus->n_llines + plus->n_blines + 1;
1730  for (i = 0; i < plus->n_clines; i++) {
1731  line_data.id = atoi(PQgetvalue(res, i, 0));
1732  line_data.wkb_geom = (char *)PQgetvalue(res, i, 1);
1733  line_data.left_face = atoi(PQgetvalue(res, i, 2)); /* face id */
1734  line_data.fid = atoi(PQgetvalue(res, i, 3)); /* feature id */
1735  /* area id and face id can be different */
1736 
1737  read_p_line(plus, id + i, &line_data, pg_info->topo_geo_only,
1738  &(pg_info->cache));
1739  }
1740  PQclear(res);
1741 
1742  plus->built = GV_BUILD_BASE;
1743 
1744  return plus->n_lines;
1745 }
1746 
1747 /*
1748  \brief PostgreSQL notice processor
1749 
1750  Print out NOTICE message only on verbose level
1751  */
1752 void notice_processor(void *arg UNUSED, const char *message)
1753 {
1754  if (G_verbose() > G_verbose_std()) {
1755  fprintf(stderr, "%s", message);
1756  }
1757 }
1758 
1759 /*!
1760  \brief Scan string array
1761 
1762  Creates tokens based on string array, eg. '{1, 2, 3}' become
1763  [1,2,3].
1764 
1765  Allocated tokes should be freed by G_free_tokens().
1766 
1767  \param sArray string array
1768 
1769  \return tokens
1770  */
1771 char **scan_array(const char *sarray)
1772 {
1773  char *buf, **tokens;
1774  int i, len;
1775 
1776  /* remove '{}' */
1777  len = strlen(sarray) - 1; /* skip '}' */
1778  buf = (char *)G_malloc(len);
1779 
1780  for (i = 1; i < len; i++)
1781  buf[i - 1] = sarray[i];
1782  buf[len - 1] = '\0';
1783 
1784  tokens = G_tokenize(buf, ",");
1785  G_free(buf);
1786 
1787  return tokens;
1788 }
1789 
1790 /*!
1791  \brief Get node id from offset
1792 
1793  \param offset pointer to Format_info_offset struct
1794  \param node node to find
1795 
1796  \return node id
1797  \return -1 not found
1798  */
1799 int remap_node(const struct Format_info_offset *offset, int node)
1800 {
1801  /* probably not needed
1802  int i;
1803  for (i = node-1; i < offset->array_num; i++) {
1804  if (offset->array[i] == node)
1805  return i + 1;
1806  }
1807 
1808  return -1;
1809  */
1810 
1811  return offset->array[node - 1];
1812 }
1813 
1814 /*!
1815  \brief Get line id from offset
1816 
1817  \param plus pointer to Plus_head struct
1818  \param offset line offset
1819  \param type line type
1820 
1821  \return line id
1822  \return -1 not found
1823  */
1824 int remap_line(const struct Plus_head *plus, off_t offset, int type)
1825 {
1826  int i;
1827 
1828  struct P_line *Line;
1829 
1830  for (i = (int)offset; i <= plus->n_lines; i++) {
1831  Line = plus->Line[i];
1832 
1833  if (!Line || Line->type != type)
1834  continue;
1835 
1836  if ((int)Line->offset == offset)
1837  return i;
1838  }
1839 
1840  return -1;
1841 }
1842 #endif
int Vect__clean_grass_db_topo(struct Format_info_pg *pg_info)
Clean-up GRASS Topology tables.
Definition: build_pg.c:998
#define NULL
Definition: ccmath.h:32
#define DB_SQL_MAX
Definition: dbmi.h:142
int db_get_login(const char *, const char *, const char **, const char **, const char **, const char **)
Get login parameters for driver/database.
Definition: login.c:381
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
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition: defs/gis.h:94
void void G_verbose_message(const char *,...) __attribute__((format(printf
int G_get_overwrite(void)
Get overwrite value.
Definition: parser.c:957
int G_verbose(void)
Get current verbosity level.
Definition: verbose.c:60
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition: gis/token.c:198
int G_number_of_tokens(char **)
Return number of tokens.
Definition: gis/token.c:179
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition: strings.c:47
int G_strncasecmp(const char *, const char *, int)
String compare ignoring case (upper or lower) - limited number of characters.
Definition: strings.c:69
int G_debug(int, const char *,...) __attribute__((format(printf
int G_verbose_std(void)
Get standard verbosity level.
Definition: verbose.c:91
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition: gis/token.c:47
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_open_fidx(struct Map_info *, struct Format_info_offset *)
Open feature index file.
Definition: open_ogr.c:254
int Vect_build_line_area(struct Map_info *, int, int)
Build area on given side of line (GV_LEFT or GV_RIGHT)
Definition: build.c:79
const char * Vect_get_full_name(struct Map_info *)
Get fully qualified name of vector map.
int Vect_get_isle_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points for given isle.
int Vect_get_area_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points (outer ring) of given area.
void Vect_destroy_list(struct ilist *)
Frees all memory associated with a struct ilist, including the struct itself.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
struct ilist * Vect_new_list(void)
Creates and initializes a struct ilist.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition: line.c:45
int Vect_find_area(struct Map_info *, double, double)
Find the nearest area.
#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_MULTIPOLYGON
Definition: dig_defines.h:249
@ SF_MULTILINESTRING
Definition: dig_defines.h:248
@ SF_LINESTRING
Definition: dig_defines.h:242
@ SF_GEOMETRY
Definition: dig_defines.h:240
@ SF_GEOMETRYCOLLECTION
Definition: dig_defines.h:250
@ SF_MULTIPOINT
Definition: dig_defines.h:247
@ SF_POINT
Definition: dig_defines.h:241
#define VECT_OPEN_CODE
Vector map open code.
Definition: dig_defines.h:111
#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_ATTACH_ISLES
Topology levels - attach islands to areas.
Definition: dig_defines.h:129
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
Definition: dig_defines.h:125
#define GV_BUILD_ALL
Topology levels - build everything (currently same as GV_BUILD_CENTROIDS)
Definition: dig_defines.h:134
#define WITH_Z
Definition: dig_defines.h:172
#define GV_BUILD_AREAS
Topology levels - build areas.
Definition: dig_defines.h:127
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
Definition: dig_defines.h:131
#define GV_RIGHT
Definition: dig_defines.h:176
#define GV_PG_GEOMETRY_COLUMN
GRASS-PostGIS data provider - default geometry column.
Definition: dig_defines.h:273
#define GV_AREA
Definition: dig_defines.h:189
#define GV_LEFT
Boundary side indicator left/right.
Definition: dig_defines.h:175
#define GV_PG_FID_COLUMN
GRASS-PostGIS data provider - default fid column.
Definition: dig_defines.h:271
int dig_spidx_add_isle(struct Plus_head *, int, const struct bound_box *)
Add new island to spatial index.
Definition: spindex.c:395
void dig_node_add_updated(struct Plus_head *, int)
Add node to updated.
int dig_alloc_nodes(struct Plus_head *, int)
Reallocate array of pointers to nodes.
Definition: struct_alloc.c:105
struct P_isle * dig_alloc_isle(void)
Allocate new isle structure.
Definition: struct_alloc.c:301
int dig_cidx_add_cat(struct Plus_head *, int, int, int, int)
Definition: diglib/cindex.c:72
int dig_spidx_add_area(struct Plus_head *, int, const struct bound_box *)
Add new area to spatial index.
Definition: spindex.c:360
int dig_alloc_areas(struct Plus_head *, int)
Reallocate array of pointers to areas.
Definition: struct_alloc.c:218
int dig_init_plus(struct Plus_head *)
Initialize Plus_head structure.
Definition: plus.c:30
int dig_alloc_isles(struct Plus_head *, int)
Reallocate array of pointers to isles.
Definition: struct_alloc.c:243
int dig_spidx_add_node(struct Plus_head *, int, double, double, double)
Add new node to spatial index.
Definition: spindex.c:291
int dig_area_alloc_isle(struct P_area *, int)
Allocate space in P_area for add new isles.
Definition: struct_alloc.c:440
int dig_node_alloc_line(struct P_node *, int)
Allocate space in P_node struct.
Definition: struct_alloc.c:69
void * dig_alloc_topo(char)
Allocate new topo struct.
Definition: struct_alloc.c:145
int dig_alloc_lines(struct Plus_head *, int)
Reallocate array of pointers to lines.
Definition: struct_alloc.c:193
struct P_area * dig_alloc_area(void)
Allocate new area structure.
Definition: struct_alloc.c:266
void dig_line_add_updated(struct Plus_head *, int, off_t)
Add new line to updated.
struct P_line * dig_alloc_line(void)
Allocate new line structure.
Definition: struct_alloc.c:127
int dig_isle_alloc_line(struct P_isle *, int)
Allocate space in P_isle for add new lines.
Definition: struct_alloc.c:466
int dig_area_alloc_line(struct P_area *, int)
allocate space in P_area for add new lines
Definition: struct_alloc.c:414
int dig_spidx_add_line(struct Plus_head *, int, const struct bound_box *)
Add new line to spatial index.
Definition: spindex.c:326
int dig_line_box(const struct line_pnts *, struct bound_box *)
struct P_node * dig_alloc_node(void)
Allocate new node structure.
Definition: struct_alloc.c:30
#define TRUE
Definition: gis.h:79
#define FALSE
Definition: gis.h:83
#define GNAME_MAX
Definition: gis.h:191
#define UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition: gis.h:47
#define M_PI
Definition: gis.h:158
#define _(str)
Definition: glocale.h:10
const char * name
Definition: named_colr.c:6
int V2_open_old_pg(struct Map_info *Map NOPG_UNUSED)
Open vector map - PostGIS feature table on topological level.
Definition: open_pg.c:166
int Vect__load_plus_head(struct Map_info *Map)
Read topo from PostGIS topology schema – header info only.
Definition: open_pg.c:1125
#define NOPG_UNUSED
Definition: open_pg.c:58
int Vect__load_map_nodes_pg(struct Map_info *Map, int geom_only)
Read nodes from DB.
Definition: open_pg.c:1484
int Vect__open_topo_pg(struct Map_info *Map NOPG_UNUSED, int head_only NOPG_UNUSED, int update NOPG_UNUSED)
Read full-topology for PostGIS links.
Definition: open_pg.c:326
void notice_processor(void *arg UNUSED, const char *message)
Definition: open_pg.c:1752
int Vect__load_map_lines_pg(struct Map_info *Map)
Read features from DB.
Definition: open_pg.c:1557
int Vect__load_plus_pg(struct Map_info *Map, int head_only)
Read topo info from PostGIS topology schema.
Definition: open_pg.c:1300
int V1_open_old_pg(struct Map_info *Map NOPG_UNUSED, int update NOPG_UNUSED)
Open vector map - PostGIS feature table on non-topological level.
Definition: open_pg.c:73
int V1_open_new_pg(struct Map_info *Map NOPG_UNUSED, const char *name NOPG_UNUSED, int with_z NOPG_UNUSED)
Prepare PostGIS database for creating new feature table (level 1)
Definition: open_pg.c:225
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
SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon, int force_type, struct Format_info_cache *cache, struct feat_parts *fparts)
Read geometry from HEX data.
Definition: read_pg.c:778
if(!(yy_init))
Definition: sqlp.yy.c:775
Lines cache for reading feature (non-native formats)
Definition: dig_structs.h:450
int ctype
Cache type.
Definition: dig_structs.h:496
SF_FeatureType sf_type
Simple feature type (currently used only by PG format)
Definition: dig_structs.h:490
long fid
Feature id.
Definition: dig_structs.h:486
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
int lines_num
Number of lines which forms current feature.
Definition: dig_structs.h:478
Data structure used for building pseudo-topology.
Definition: dig_structs.h:388
int * array
Offset list.
Definition: dig_structs.h:436
int array_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 * db_name
Database name (derived from conninfo)
Definition: dig_structs.h:598
char * fid_column
FID column.
Definition: dig_structs.h:614
char * schema_name
Schema name.
Definition: dig_structs.h:602
PGconn * conn
PGconn object (generated by PQconnectdb)
Definition: dig_structs.h:650
char * toposchema_name
Topology schema name and id.
Definition: dig_structs.h:686
int coor_dim
Coordinates dimension (2D or 3D)
Definition: dig_structs.h:626
char * conninfo
Connection string.
Definition: dig_structs.h:594
PGresult * res
Definition: dig_structs.h:651
struct Format_info_cache cache
Lines cache for reading feature.
Definition: dig_structs.h:670
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
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
Vector map info.
Definition: dig_structs.h:1243
struct Format_info fInfo
Format info for non-native formats.
Definition: dig_structs.h:1400
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1270
Area (topology) info.
Definition: dig_structs.h:1583
plus_t n_isles
Number of islands inside.
Definition: dig_structs.h:1609
plus_t * isles
1st generation interior islands
Definition: dig_structs.h:1617
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1587
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1598
plus_t centroid
Number of first centroid within area.
Definition: dig_structs.h:1605
Isle (topology) info.
Definition: dig_structs.h:1623
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1638
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1627
plus_t area
Area it exists w/in, if any.
Definition: dig_structs.h:1645
Vector geometry.
Definition: dig_structs.h:1553
char type
Line type.
Definition: dig_structs.h:1564
off_t offset
Offset in coor file for line.
Definition: dig_structs.h:1571
void * topo
Topology info.
Definition: dig_structs.h:1577
Topological feature - node.
Definition: dig_structs.h:1433
double x
X coordinate.
Definition: dig_structs.h:1437
plus_t n_lines
Number of attached lines (size of lines, angle)
Definition: dig_structs.h:1456
float * angles
List of angles of connected lines.
Definition: dig_structs.h:1472
plus_t * lines
List of connected lines.
Definition: dig_structs.h:1463
double z
Z coordinate (used only for 3D data)
Definition: dig_structs.h:1445
double y
Y coordinate.
Definition: dig_structs.h:1441
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 N1
Start node.
Definition: dig_structs.h:1496
plus_t N2
End node.
Definition: dig_structs.h:1500
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
int do_uplist
Indicates if the list of updated features is maintained.
Definition: dig_structs.h:1161
int with_z
2D/3D vector data
Definition: dig_structs.h:786
struct P_line ** Line
Array of vector geometries.
Definition: dig_structs.h:871
plus_t n_lines
Current number of lines.
Definition: dig_structs.h:931
int Spidx_new
Build new spatial index.
Definition: dig_structs.h:1043
plus_t n_plines
Current number of points.
Definition: dig_structs.h:886
int off_t_size
Offset size.
Definition: dig_structs.h:801
plus_t n_nodes
Current number of topological features derived from vector geometries.
Definition: dig_structs.h:923
plus_t n_blines
Current number of boundaries.
Definition: dig_structs.h:894
struct P_area ** Area
Array of areas.
Definition: dig_structs.h:875
plus_t n_clines
Current number of centroids.
Definition: dig_structs.h:898
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
plus_t n_isles
Current number of isles.
Definition: dig_structs.h:939
struct Plus_head::@10 uplist
List of updated lines/nodes.
struct bound_box box
Bounding box of features.
Definition: dig_structs.h:861
struct P_isle ** Isle
Array of isles.
Definition: dig_structs.h:879
struct P_node ** Node
Array of nodes.
Definition: dig_structs.h:867
plus_t n_areas
Current number of areas.
Definition: dig_structs.h:935
int built
Highest level of topology currently available.
Definition: dig_structs.h:857
plus_t n_llines
Current number of lines.
Definition: dig_structs.h:890
Bounding box.
Definition: dig_structs.h:64
double W
West.
Definition: dig_structs.h:80
double T
Top.
Definition: dig_structs.h:84
double S
South.
Definition: dig_structs.h:72
double N
North.
Definition: dig_structs.h:68
double E
East.
Definition: dig_structs.h:76
double B
Bottom.
Definition: dig_structs.h:88
List of integers.
Definition: gis.h:709
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
double * z
Array of Z coordinates.
Definition: dig_structs.h:1663
void Vect__free_cache(struct Format_info_cache *cache)