open_pg.c 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438
  1. /*!
  2. \file lib/vector/Vlib/open_pg.c
  3. \brief Vector library - Open PostGIS layer as vector map layer
  4. Higher level functions for reading/writing/manipulating vectors.
  5. (C) 2011-2012 by the GRASS Development Team
  6. This program is free software under the GNU General Public License
  7. (>=v2). Read the file COPYING that comes with GRASS for details.
  8. \author Martin Landa <landa.martin gmail.com>
  9. */
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include <grass/vector.h>
  13. #include <grass/dbmi.h>
  14. #include <grass/glocale.h>
  15. #ifdef HAVE_POSTGRES
  16. #include "pg_local_proto.h"
  17. struct edge_data {
  18. int id;
  19. int start_node;
  20. int end_node;
  21. int left_face;
  22. int right_face;
  23. };
  24. static char *get_key_column(struct Format_info_pg *);
  25. static SF_FeatureType ftype_from_string(const char *);
  26. static int drop_table(struct Format_info_pg *);
  27. static int check_schema(const struct Format_info_pg *);
  28. static int create_table(struct Format_info_pg *, const struct field_info *);
  29. static void connect_db(struct Format_info_pg *);
  30. static int check_topo(struct Format_info_pg *, struct Plus_head *);
  31. static int parse_bbox(const char *, struct bound_box *, int);
  32. static int num_of_records(const struct Format_info_pg *, const char *);
  33. static int read_p_node(struct Plus_head *, int, int, struct Format_info_pg *);
  34. static int read_p_line(struct Plus_head *, int, const struct edge_data *);
  35. static int read_p_area(struct Plus_head *, int, int, struct Format_info_pg *);
  36. static int load_plus_head(struct Format_info_pg *, struct Plus_head *);
  37. #endif
  38. /*!
  39. \brief Open vector map - PostGIS feature table (level 1 - without topology)
  40. \todo Check database instead of geometry_columns
  41. \param[in,out] Map pointer to Map_info structure
  42. \param update TRUE for write mode, otherwise read-only
  43. \return 0 success
  44. \return -1 error
  45. */
  46. int V1_open_old_pg(struct Map_info *Map, int update)
  47. {
  48. #ifdef HAVE_POSTGRES
  49. int found;
  50. char stmt[DB_SQL_MAX];
  51. PGresult *res;
  52. struct Format_info_pg *pg_info;
  53. pg_info = &(Map->fInfo.pg);
  54. if (!pg_info->conninfo) {
  55. G_warning(_("Connection string not defined"));
  56. return -1;
  57. }
  58. if (!pg_info->table_name) {
  59. G_warning(_("PostGIS feature table not defined"));
  60. return -1;
  61. }
  62. G_debug(1, "V1_open_old_pg(): conninfo='%s' table='%s'",
  63. pg_info->conninfo, pg_info->table_name);
  64. /* connect database */
  65. if (!pg_info->conn)
  66. connect_db(pg_info);
  67. /* get DB name */
  68. pg_info->db_name = G_store(PQdb(pg_info->conn));
  69. if (!pg_info->db_name) {
  70. G_warning(_("Unable to get database name"));
  71. return -1;
  72. }
  73. /* get fid and geometry column */
  74. sprintf(stmt, "SELECT f_geometry_column, coord_dimension, srid, type "
  75. "FROM geometry_columns WHERE f_table_schema = '%s' AND "
  76. "f_table_name = '%s'", pg_info->schema_name, pg_info->table_name);
  77. G_debug(2, "SQL: %s", stmt);
  78. res = PQexec(pg_info->conn, stmt);
  79. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
  80. G_fatal_error("%s\n%s", _("No feature tables found in database."),
  81. PQresultErrorMessage(res));
  82. found = PQntuples(res) > 0 ? TRUE : FALSE;
  83. if (found) {
  84. /* geometry column */
  85. pg_info->geom_column = G_store(PQgetvalue(res, 0, 0));
  86. G_debug(3, "\t-> table = %s column = %s", pg_info->table_name,
  87. pg_info->geom_column);
  88. /* fid column */
  89. pg_info->fid_column = get_key_column(pg_info);
  90. /* coordinates dimension */
  91. pg_info->coor_dim = atoi(PQgetvalue(res, 0, 1));
  92. /* SRS ID */
  93. pg_info->srid = atoi(PQgetvalue(res, 0, 2));
  94. /* feature type */
  95. pg_info->feature_type = ftype_from_string(PQgetvalue(res, 0, 3));
  96. }
  97. PQclear(res);
  98. /* no feature in cache */
  99. pg_info->cache.fid = -1;
  100. if (!found) {
  101. G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
  102. pg_info->table_name);
  103. return -1;
  104. }
  105. /* check for topo schema */
  106. check_topo(pg_info, &(Map->plus));
  107. return 0;
  108. #else
  109. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  110. return -1;
  111. #endif
  112. }
  113. /*!
  114. \brief Open vector map - PostGIS feature table (level 2 - feature index)
  115. \param[in,out] Map pointer to Map_info structure
  116. \return 0 success
  117. \return -1 error
  118. */
  119. int V2_open_old_pg(struct Map_info *Map)
  120. {
  121. #ifdef HAVE_POSTGRES
  122. G_debug(3, "V2_open_old_pg(): name = %s mapset = %s", Map->name,
  123. Map->mapset);
  124. if (Vect_open_fidx(Map, &(Map->fInfo.pg.offset)) != 0) {
  125. G_warning(_("Unable to open feature index file for vector map <%s>"),
  126. Vect_get_full_name(Map));
  127. G_zero(&(Map->fInfo.pg.offset), sizeof(struct Format_info_offset));
  128. }
  129. return 0;
  130. #else
  131. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  132. return -1;
  133. #endif
  134. }
  135. /*!
  136. \brief Prepare PostGIS database for creating new feature table
  137. (level 1)
  138. \todo To implement
  139. \param[out] Map pointer to Map_info structure
  140. \param name name of PostGIS feature table to create
  141. \param with_z WITH_Z for 3D vector data otherwise WITHOUT_Z
  142. \return 0 success
  143. \return -1 error
  144. */
  145. int V1_open_new_pg(struct Map_info *Map, const char *name, int with_z)
  146. {
  147. #ifdef HAVE_POSTGRES
  148. char stmt[DB_SQL_MAX];
  149. struct Format_info_pg *pg_info;
  150. PGresult *res;
  151. pg_info = &(Map->fInfo.pg);
  152. if (!pg_info->conninfo) {
  153. G_warning(_("Connection string not defined"));
  154. return -1;
  155. }
  156. if (!pg_info->table_name) {
  157. G_warning(_("PostGIS feature table not defined"));
  158. return -1;
  159. }
  160. G_debug(1, "V1_open_new_pg(): conninfo='%s' table='%s'",
  161. pg_info->conninfo, pg_info->table_name);
  162. /* connect database */
  163. pg_info->conn = PQconnectdb(pg_info->conninfo);
  164. G_debug(2, " PQconnectdb(): %s", pg_info->conninfo);
  165. if (PQstatus(pg_info->conn) == CONNECTION_BAD)
  166. G_fatal_error("%s\n%s",
  167. _("Connection ton PostgreSQL database failed."),
  168. PQerrorMessage(pg_info->conn));
  169. /* get DB name */
  170. pg_info->db_name = G_store(PQdb(pg_info->conn));
  171. if (!pg_info->db_name) {
  172. G_warning(_("Unable to get database name"));
  173. return -1;
  174. }
  175. /* if schema not defined, use 'public' */
  176. if (!pg_info->schema_name) {
  177. pg_info->schema_name = G_store("public");
  178. }
  179. /* if fid_column not defined, use 'ogc_fid' */
  180. if (!pg_info->fid_column) {
  181. pg_info->fid_column = G_store("ogc_fid");
  182. }
  183. /* if geom_column not defined, use 'wkb_geometry' */
  184. if (!pg_info->geom_column) {
  185. pg_info->geom_column = G_store("wkb_geometry");
  186. }
  187. /* check if feature table already exists */
  188. sprintf(stmt, "SELECT * FROM pg_tables "
  189. "WHERE schemaname = '%s' AND tablename = '%s'",
  190. pg_info->schema_name, pg_info->table_name);
  191. G_debug(2, "SQL: %s", stmt);
  192. res = PQexec(pg_info->conn, stmt);
  193. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
  194. G_fatal_error("%s\n%s", _("No feature tables found in database."),
  195. PQresultErrorMessage(res));
  196. if (PQntuples(res) > 0) {
  197. /* table found */
  198. if (G_get_overwrite()) {
  199. G_warning(_("PostGIS layer <%s.%s> already exists and will be overwritten"),
  200. pg_info->schema_name, pg_info->table_name);
  201. if (drop_table(pg_info) == -1) {
  202. G_warning(_("Unable to delete PostGIS layer <%s>"),
  203. pg_info->table_name);
  204. return -1;
  205. }
  206. }
  207. else {
  208. G_fatal_error(_("PostGIS layer <%s.%s> already exists in database '%s'"),
  209. pg_info->schema_name, pg_info->table_name,
  210. pg_info->db_name);
  211. return -1;
  212. }
  213. }
  214. /* no feature in cache */
  215. pg_info->cache.fid = -1;
  216. /* unknown feature type */
  217. pg_info->feature_type = SF_UNKNOWN;
  218. PQclear(res);
  219. return 0;
  220. #else
  221. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  222. return -1;
  223. #endif
  224. }
  225. /*!
  226. \brief Create new PostGIS layer in given database (level 2)
  227. V1_open_new_pg() is required to be called before this function.
  228. List of currently supported types:
  229. - GV_POINT (SF_POINT)
  230. - GV_LINE (SF_LINESTRING)
  231. - GV_BOUNDARY (SF_POLYGON)
  232. \param[in,out] Map pointer to Map_info structure
  233. \param type feature type (GV_POINT, GV_LINE, ...)
  234. \return 0 success
  235. \return -1 error
  236. */
  237. int V2_open_new_pg(struct Map_info *Map, int type)
  238. {
  239. #ifdef HAVE_POSTGRES
  240. int ndblinks;
  241. struct Format_info_pg *pg_info;
  242. struct field_info *Fi;
  243. struct Key_Value *projinfo, *projunits;
  244. Fi = NULL;
  245. pg_info = &(Map->fInfo.pg);
  246. if (!pg_info->conninfo) {
  247. G_warning(_("Connection string not defined"));
  248. return -1;
  249. }
  250. if (!pg_info->table_name) {
  251. G_warning(_("PostGIS feature table not defined"));
  252. return -1;
  253. }
  254. G_debug(1, "V2_open_new_pg(): conninfo='%s' table='%s' -> type = %d",
  255. pg_info->conninfo, pg_info->table_name, type);
  256. /* get spatial reference */
  257. projinfo = G_get_projinfo();
  258. projunits = G_get_projunits();
  259. pg_info->srid = 0; /* TODO */
  260. // Ogr_spatial_ref = GPJ_grass_to_osr(projinfo, projunits);
  261. G_free_key_value(projinfo);
  262. G_free_key_value(projunits);
  263. /* determine geometry type */
  264. switch (type) {
  265. case GV_POINT:
  266. pg_info->feature_type = SF_POINT;
  267. break;
  268. case GV_LINE:
  269. pg_info->feature_type = SF_LINESTRING;
  270. break;
  271. case GV_BOUNDARY:
  272. pg_info->feature_type = SF_POLYGON;
  273. break;
  274. default:
  275. G_warning(_("Unsupported geometry type (%d)"), type);
  276. return -1;
  277. }
  278. /* coordinate dimension */
  279. pg_info->coor_dim = Vect_is_3d(Map) ? 3 : 2;
  280. /* create new PostGIS table */
  281. ndblinks = Vect_get_num_dblinks(Map);
  282. if (ndblinks > 0) {
  283. Fi = Vect_get_dblink(Map, 0);
  284. if (Fi) {
  285. if (ndblinks > 1)
  286. G_warning(_("More layers defined, using driver <%s> and "
  287. "database <%s>"), Fi->driver, Fi->database);
  288. }
  289. else {
  290. G_warning(_("Database connection not defined. "
  291. "Unable to write attributes."));
  292. }
  293. }
  294. if (create_table(pg_info, Fi) == -1) {
  295. G_warning(_("Unable to create new PostGIS table"));
  296. return -1;
  297. }
  298. if (Fi)
  299. G_free(Fi);
  300. return 0;
  301. #else
  302. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  303. return -1;
  304. #endif
  305. }
  306. /*!
  307. \brief Read full-topology for PostGIS links
  308. Note: Only 2D topological primitives are currently supported
  309. \param[in,out] Map pointer to Map_info structure
  310. \param head_only TRUE to read only header
  311. \return 0 on success
  312. \return 1 topology layer does not exist
  313. \return -1 on error
  314. */
  315. int Vect_open_topo_pg(struct Map_info *Map, int head_only)
  316. {
  317. #ifdef HAVE_POSTGRES
  318. struct Plus_head *plus;
  319. struct Format_info_pg *pg_info;
  320. plus = &(Map->plus);
  321. pg_info = &(Map->fInfo.pg);
  322. /* check for topo schema */
  323. if (check_topo(pg_info, plus) != 0)
  324. return 1;
  325. /* free and init plus structure */
  326. dig_init_plus(plus);
  327. return load_plus(pg_info, plus, head_only);
  328. #else
  329. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  330. return -1;
  331. #endif
  332. }
  333. #ifdef HAVE_POSTGRES
  334. /*!
  335. \brief Get key column for feature table
  336. \param pg_info pointer to Format_info_pg
  337. \return string buffer with key column name
  338. \return NULL on missing key column
  339. */
  340. char *get_key_column(struct Format_info_pg *pg_info)
  341. {
  342. char *key_column;
  343. char stmt[DB_SQL_MAX];
  344. PGresult *res;
  345. sprintf(stmt,
  346. "SELECT kcu.column_name "
  347. "FROM INFORMATION_SCHEMA.TABLES t "
  348. "LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc "
  349. "ON tc.table_catalog = t.table_catalog "
  350. "AND tc.table_schema = t.table_schema "
  351. "AND tc.table_name = t.table_name "
  352. "AND tc.constraint_type = 'PRIMARY KEY' "
  353. "LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu "
  354. "ON kcu.table_catalog = tc.table_catalog "
  355. "AND kcu.table_schema = tc.table_schema "
  356. "AND kcu.table_name = tc.table_name "
  357. "AND kcu.constraint_name = tc.constraint_name "
  358. "WHERE t.table_schema = '%s' AND t.table_name = '%s'",
  359. pg_info->schema_name, pg_info->table_name);
  360. G_debug(2, "SQL: %s", stmt);
  361. res = PQexec(pg_info->conn, stmt);
  362. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  363. PQntuples(res) != 1 || strlen(PQgetvalue(res, 0, 0)) < 1) {
  364. G_warning(_("No key column detected."));
  365. if (res)
  366. PQclear(res);
  367. return NULL;
  368. }
  369. key_column = G_store(PQgetvalue(res, 0, 0));
  370. PQclear(res);
  371. return key_column;
  372. }
  373. /*!
  374. \brief Get simple feature type from string
  375. \param type string
  376. \return SF type
  377. */
  378. SF_FeatureType ftype_from_string(const char *type)
  379. {
  380. SF_FeatureType sf_type;
  381. if (G_strcasecmp(type, "POINT") == 0)
  382. return SF_POINT;
  383. else if (G_strcasecmp(type, "LINESTRING") == 0)
  384. return SF_LINESTRING;
  385. else if (G_strcasecmp(type, "POLYGON") == 0)
  386. return SF_POLYGON;
  387. else if (G_strcasecmp(type, "MULTIPOINT") == 0)
  388. return SF_MULTIPOINT;
  389. else if (G_strcasecmp(type, "MULTILINESTRING") == 0)
  390. return SF_MULTILINESTRING;
  391. else if (G_strcasecmp(type, "MULTIPOLYGON") == 0)
  392. return SF_MULTIPOLYGON;
  393. else if (G_strcasecmp(type, "GEOMETRYCOLLECTION") == 0)
  394. return SF_GEOMETRYCOLLECTION;
  395. else
  396. return SF_UNKNOWN;
  397. G_debug(3, "ftype_from_string(): type='%s' -> %d", type, sf_type);
  398. return sf_type;
  399. }
  400. /*!
  401. \brief Drop feature table
  402. \param pg_info pointer to Format_info_pg
  403. \return -1 on error
  404. \return 0 on success
  405. */
  406. int drop_table(struct Format_info_pg *pg_info)
  407. {
  408. char stmt[DB_SQL_MAX];
  409. sprintf(stmt, "DROP TABLE \"%s\".\"%s\"",
  410. pg_info->schema_name, pg_info->table_name);
  411. G_debug(2, "SQL: %s", stmt);
  412. if (execute(pg_info->conn, stmt) == -1) {
  413. return -1;
  414. }
  415. return 0;
  416. }
  417. /*!
  418. \brief Creates new schema for feature table if not exists
  419. \param pg_info pointer to Format_info_pg
  420. \return -1 on error
  421. \return 0 on success
  422. */
  423. int check_schema(const struct Format_info_pg *pg_info)
  424. {
  425. int i, found, nschema;
  426. char stmt[DB_SQL_MAX];
  427. PGresult *result;
  428. /* add geometry column */
  429. sprintf(stmt, "SELECT nspname FROM pg_namespace");
  430. G_debug(2, "SQL: %s", stmt);
  431. result = PQexec(pg_info->conn, stmt);
  432. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  433. PQclear(result);
  434. execute(pg_info->conn, "ROLLBACK");
  435. return -1;
  436. }
  437. found = FALSE;
  438. nschema = PQntuples(result);
  439. for (i = 0; i < nschema && !found; i++) {
  440. if (strcmp(pg_info->schema_name, PQgetvalue(result, i, 0)) == 0)
  441. found = TRUE;
  442. }
  443. PQclear(result);
  444. if (!found) {
  445. sprintf(stmt, "CREATE SCHEMA %s", pg_info->schema_name);
  446. if (execute(pg_info->conn, stmt) == -1) {
  447. execute(pg_info->conn, "ROLLBACK");
  448. return -1;
  449. }
  450. G_warning(_("Schema <%s> doesn't exist, created"),
  451. pg_info->schema_name);
  452. }
  453. return 0;
  454. }
  455. /*!
  456. \brief Create new feature table
  457. \param pg_info pointer to Format_info_pg
  458. \param Fi pointer to field_info
  459. \return -1 on error
  460. \return 0 on success
  461. */
  462. int create_table(struct Format_info_pg *pg_info, const struct field_info *Fi)
  463. {
  464. int spatial_index, primary_key;
  465. char stmt[DB_SQL_MAX], *geom_type;
  466. PGresult *result;
  467. /* by default create spatial index & add primary key */
  468. spatial_index = primary_key = TRUE;
  469. if (G_find_file2("", "PG", G_mapset())) {
  470. FILE *fp;
  471. const char *p;
  472. struct Key_Value *key_val;
  473. fp = G_fopen_old("", "PG", G_mapset());
  474. if (!fp) {
  475. G_fatal_error(_("Unable to open PG file"));
  476. }
  477. key_val = G_fread_key_value(fp);
  478. fclose(fp);
  479. /* disable spatial index ? */
  480. p = G_find_key_value("spatial_index", key_val);
  481. if (p && G_strcasecmp(p, "off") == 0)
  482. spatial_index = FALSE;
  483. /* disable primary key ? */
  484. p = G_find_key_value("primary_key", key_val);
  485. if (p && G_strcasecmp(p, "off") == 0)
  486. primary_key = FALSE;
  487. }
  488. /* create schema if not exists */
  489. if (G_strcasecmp(pg_info->schema_name, "public") != 0) {
  490. if (check_schema(pg_info) != 0)
  491. return -1;
  492. }
  493. /* prepare CREATE TABLE statement */
  494. sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL",
  495. pg_info->schema_name, pg_info->table_name, pg_info->fid_column);
  496. if (Fi) {
  497. /* append attributes */
  498. int col, ncols, sqltype, length, ctype;
  499. char stmt_col[DB_SQL_MAX];
  500. const char *colname;
  501. dbString dbstmt;
  502. dbHandle handle;
  503. dbDriver *driver;
  504. dbCursor cursor;
  505. dbTable *table;
  506. dbColumn *column;
  507. db_init_string(&dbstmt);
  508. db_init_handle(&handle);
  509. pg_info->dbdriver = driver = db_start_driver(Fi->driver);
  510. if (!driver) {
  511. G_warning(_("Unable to start driver <%s>"), Fi->driver);
  512. return -1;
  513. }
  514. db_set_handle(&handle, Fi->database, NULL);
  515. if (db_open_database(driver, &handle) != DB_OK) {
  516. G_warning(_("Unable to open database <%s> by driver <%s>"),
  517. Fi->database, Fi->driver);
  518. db_close_database_shutdown_driver(driver);
  519. pg_info->dbdriver = NULL;
  520. return -1;
  521. }
  522. /* describe table */
  523. db_set_string(&dbstmt, "select * from ");
  524. db_append_string(&dbstmt, Fi->table);
  525. db_append_string(&dbstmt, " where 0 = 1");
  526. if (db_open_select_cursor(driver, &dbstmt,
  527. &cursor, DB_SEQUENTIAL) != DB_OK) {
  528. G_warning(_("Unable to open select cursor: '%s'"),
  529. db_get_string(&dbstmt));
  530. db_close_database_shutdown_driver(driver);
  531. pg_info->dbdriver = NULL;
  532. return -1;
  533. }
  534. table = db_get_cursor_table(&cursor);
  535. ncols = db_get_table_number_of_columns(table);
  536. G_debug(3,
  537. "copying attributes: driver = %s database = %s table = %s cols = %d",
  538. Fi->driver, Fi->database, Fi->table, ncols);
  539. for (col = 0; col < ncols; col++) {
  540. column = db_get_table_column(table, col);
  541. colname = db_get_column_name(column);
  542. sqltype = db_get_column_sqltype(column);
  543. ctype = db_sqltype_to_Ctype(sqltype);
  544. length = db_get_column_length(column);
  545. G_debug(3, "\tcolumn = %d name = %s type = %d length = %d",
  546. col, colname, sqltype, length);
  547. if (strcmp(pg_info->fid_column, colname) == 0) {
  548. /* skip fid column if exists */
  549. G_debug(3, "\t%s skipped", pg_info->fid_column);
  550. continue;
  551. }
  552. /* append column */
  553. sprintf(stmt_col, ",%s %s", colname, db_sqltype_name(sqltype));
  554. strcat(stmt, stmt_col);
  555. if (ctype == DB_C_TYPE_STRING) {
  556. /* length only for string columns */
  557. sprintf(stmt_col, "(%d)", length);
  558. strcat(stmt, stmt_col);
  559. }
  560. }
  561. db_free_string(&dbstmt);
  562. }
  563. strcat(stmt, ")"); /* close CREATE TABLE statement */
  564. /* begin transaction (create table) */
  565. if (execute(pg_info->conn, "BEGIN") == -1) {
  566. return -1;
  567. }
  568. /* create table */
  569. G_debug(2, "SQL: %s", stmt);
  570. if (execute(pg_info->conn, stmt) == -1) {
  571. execute(pg_info->conn, "ROLLBACK");
  572. return -1;
  573. }
  574. /* add primary key ? */
  575. if (primary_key) {
  576. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD PRIMARY KEY (%s)",
  577. pg_info->schema_name, pg_info->table_name,
  578. pg_info->fid_column);
  579. G_debug(2, "SQL: %s", stmt);
  580. if (execute(pg_info->conn, stmt) == -1) {
  581. execute(pg_info->conn, "ROLLBACK");
  582. return -1;
  583. }
  584. }
  585. /* determine geometry type (string) */
  586. switch (pg_info->feature_type) {
  587. case (SF_POINT):
  588. geom_type = "POINT";
  589. break;
  590. case (SF_LINESTRING):
  591. geom_type = "LINESTRING";
  592. break;
  593. case (SF_POLYGON):
  594. geom_type = "POLYGON";
  595. break;
  596. default:
  597. G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
  598. execute(pg_info->conn, "ROLLBACK");
  599. return -1;
  600. }
  601. /* add geometry column */
  602. sprintf(stmt, "SELECT AddGeometryColumn('%s', '%s', "
  603. "'%s', %d, '%s', %d)",
  604. pg_info->schema_name, pg_info->table_name,
  605. pg_info->geom_column, pg_info->srid,
  606. geom_type, pg_info->coor_dim);
  607. G_debug(2, "SQL: %s", stmt);
  608. result = PQexec(pg_info->conn, stmt);
  609. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  610. PQclear(result);
  611. execute(pg_info->conn, "ROLLBACK");
  612. return -1;
  613. }
  614. /* create index ? */
  615. if (spatial_index) {
  616. sprintf(stmt,
  617. "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" USING GIST (%s)",
  618. pg_info->table_name, pg_info->geom_column,
  619. pg_info->schema_name, pg_info->table_name,
  620. pg_info->geom_column);
  621. G_debug(2, "SQL: %s", stmt);
  622. if (execute(pg_info->conn, stmt) == -1) {
  623. execute(pg_info->conn, "ROLLBACK");
  624. return -1;
  625. }
  626. }
  627. /* close transaction (create table) */
  628. if (execute(pg_info->conn, "COMMIT") == -1) {
  629. return -1;
  630. }
  631. return 0;
  632. }
  633. /*!
  634. \brief Establish PG connection (pg_info->conninfo)
  635. \param pg_info pointer to Format_info_pg
  636. */
  637. void connect_db(struct Format_info_pg *pg_info)
  638. {
  639. pg_info->conn = PQconnectdb(pg_info->conninfo);
  640. G_debug(2, " PQconnectdb(): %s", pg_info->conninfo);
  641. if (PQstatus(pg_info->conn) == CONNECTION_BAD)
  642. G_fatal_error("%s\n%s",
  643. _("Connection ton PostgreSQL database failed."),
  644. PQerrorMessage(pg_info->conn));
  645. }
  646. /*!
  647. \brief Check for topology schema (pg_info->toposchema_name)
  648. \param pg_info pointer to Format_info_pg
  649. \return 0 schema exists
  650. \return 1 schema doesn't exists
  651. */
  652. int check_topo(struct Format_info_pg *pg_info, struct Plus_head *plus)
  653. {
  654. char stmt[DB_SQL_MAX];
  655. PGresult *res;
  656. /* connect database */
  657. if (!pg_info->conn)
  658. connect_db(pg_info);
  659. if (pg_info->toposchema_name)
  660. return 0;
  661. /* check if topology layer/schema exists */
  662. sprintf(stmt,
  663. "SELECT t.name,t.hasz,l.feature_column FROM topology.layer "
  664. "AS l JOIN topology.topology AS t ON l.topology_id = t.id "
  665. "WHERE schema_name = '%s' AND table_name = '%s'",
  666. pg_info->schema_name, pg_info->table_name);
  667. G_debug(2, "SQL: %s", stmt);
  668. res = PQexec(pg_info->conn, stmt);
  669. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  670. PQntuples(res) != 1) {
  671. G_debug(1, "Topology layers for '%s.%s' not found (%s)",
  672. pg_info->schema_name, pg_info->table_name,
  673. PQerrorMessage(pg_info->conn));
  674. if (res)
  675. PQclear(res);
  676. return 1;
  677. }
  678. pg_info->toposchema_name = G_store(PQgetvalue(res, 0, 0));
  679. pg_info->topogeom_column = G_store(PQgetvalue(res, 0, 2));
  680. G_debug(1, "PostGIS topology detected: schema = %s column = %s",
  681. pg_info->toposchema_name, pg_info->topogeom_column);
  682. /* check for 3D */
  683. if (strcmp(PQgetvalue(res, 0, 1), "t") == 0)
  684. plus->with_z = WITH_Z;
  685. PQclear(res);
  686. return 0;
  687. }
  688. /*!
  689. \brief Parse BBOX string
  690. \param value string buffer
  691. \param[out] bbox pointer to output bound_box struct
  692. \return 0 on success
  693. \return -1 on error
  694. */
  695. int parse_bbox(const char *value, struct bound_box *bbox, int with_z)
  696. {
  697. unsigned int i;
  698. size_t length, prefix_length;
  699. char **tokens, **tokens_coord, *coord;
  700. prefix_length = strlen("box3d(");
  701. if (G_strncasecmp(value, "box3d(", prefix_length) != 0)
  702. return -1;
  703. /* strip off "bbox3d(...)" */
  704. length = strlen(value);
  705. coord = G_malloc(length - prefix_length);
  706. for (i = prefix_length; i < length; i++)
  707. coord[i-prefix_length] = value[i];
  708. coord[length-prefix_length-1] = '\0';
  709. tokens = G_tokenize(coord, ",");
  710. G_free(coord);
  711. if (G_number_of_tokens(tokens) != 2) {
  712. G_free_tokens(tokens);
  713. return -1;
  714. }
  715. /* parse bbox LL corner */
  716. tokens_coord = G_tokenize(tokens[0], " ");
  717. if (G_number_of_tokens(tokens_coord) != 3) {
  718. G_free_tokens(tokens);
  719. G_free_tokens(tokens_coord);
  720. }
  721. bbox->W = atof(tokens_coord[0]);
  722. bbox->S = atof(tokens_coord[1]);
  723. bbox->B = atof(tokens_coord[2]);
  724. G_free_tokens(tokens_coord);
  725. /* parse bbox UR corner */
  726. tokens_coord = G_tokenize(tokens[1], " ");
  727. if (G_number_of_tokens(tokens_coord) != 3) {
  728. G_free_tokens(tokens);
  729. G_free_tokens(tokens_coord);
  730. }
  731. bbox->E = atof(tokens_coord[0]);
  732. bbox->N = atof(tokens_coord[1]);
  733. bbox->T = atof(tokens_coord[2]);
  734. G_free_tokens(tokens_coord);
  735. G_free_tokens(tokens);
  736. return 0;
  737. }
  738. /*!
  739. \brief Get number of records for given SQL statement
  740. \param stmt string buffer with SQL statement
  741. \return number of returned records
  742. \return -1 on error
  743. */
  744. int num_of_records(const struct Format_info_pg *pg_info,
  745. const char *stmt)
  746. {
  747. int result;
  748. PGresult *res;
  749. G_debug(2, "SQL: %s", stmt);
  750. res = PQexec(pg_info->conn, stmt);
  751. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  752. PQntuples(res) != 1) {
  753. G_warning(_("Unable to get number of records for:\n%s"), stmt);
  754. if (res)
  755. PQclear(res);
  756. return -1;
  757. }
  758. result = atoi(PQgetvalue(res, 0, 0));
  759. PQclear(res);
  760. return result;
  761. }
  762. /*!
  763. \brief Read P_node structure
  764. See dig_Rd_P_node() for reference.
  765. \param plus pointer to Plus_head structure
  766. \param n index (starts at 1)
  767. \param id node id (table "node")
  768. \param pg_info pointer to Format_info_pg sttucture
  769. \return 0 on success
  770. \return -1 on failure
  771. */
  772. int read_p_node(struct Plus_head *plus, int n, int id,
  773. struct Format_info_pg *pg_info)
  774. {
  775. int i, cnt;
  776. char stmt[DB_SQL_MAX];
  777. struct P_node *node;
  778. PGresult *res;
  779. /* get lines connected to the node */
  780. sprintf(stmt,
  781. "SELECT edge_id,'s' as node,"
  782. "ST_Azimuth(ST_StartPoint(geom), ST_PointN(geom, 2)) AS angle"
  783. " FROM \"%s\".edge WHERE start_node = %d UNION ALL "
  784. "SELECT edge_id,'e' as node,"
  785. "ST_Azimuth(ST_EndPoint(geom), ST_PointN(geom, ST_NumPoints(geom) - 1)) AS angle"
  786. " FROM \"%s\".edge WHERE end_node = %d"
  787. " ORDER BY angle DESC",
  788. pg_info->toposchema_name, id,
  789. pg_info->toposchema_name, id);
  790. G_debug(2, "SQL: %s", stmt);
  791. res = PQexec(pg_info->conn, stmt);
  792. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
  793. G_warning(_("Unable to read node %d"), id);
  794. if (res)
  795. PQclear(res);
  796. return -1;
  797. }
  798. cnt = PQntuples(res);
  799. if (cnt == 0) { /* dead ??? */
  800. plus->Node[n] = NULL;
  801. return 0;
  802. }
  803. node = dig_alloc_node();
  804. node->n_lines = cnt;
  805. G_debug(4, "read_p_node(): id = %d, n_lines = %d", id, cnt);
  806. if (dig_node_alloc_line(node, node->n_lines) == -1)
  807. return -1;
  808. /* lines / angles */
  809. for (i = 0; i < node->n_lines; i++) {
  810. node->lines[i] = atoi(PQgetvalue(res, i, 0));
  811. if (strcmp(PQgetvalue(res, i, 1), "s") != 0) {
  812. /* end node */
  813. node->lines[i] *= -1;
  814. }
  815. node->angles[i] = M_PI / 2 - atof(PQgetvalue(res, i, 2));
  816. /* angles range <-PI; PI> */
  817. if (node->angles[i] > M_PI)
  818. node->angles[i] = node->angles[i] - 2 * M_PI;
  819. if (node->angles[i] < -1.0 * M_PI)
  820. node->angles[i] = node->angles[i] + 2 * M_PI;
  821. G_debug(5, "\tline = %d angle = %f", node->lines[i],
  822. node->angles[i]);
  823. }
  824. PQclear(res);
  825. /* ???
  826. if (plus->with_z)
  827. if (0 >= dig__fread_port_P(&n_edges, 1, fp))
  828. */
  829. /* get node coordinates */
  830. sprintf(stmt,
  831. "SELECT ST_X(geom),ST_Y(geom),ST_Z(geom) FROM \"%s\".node "
  832. "WHERE node_id = %d",
  833. pg_info->toposchema_name, id);
  834. G_debug(2, "SQL: %s", stmt);
  835. res = PQexec(pg_info->conn, stmt);
  836. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  837. PQntuples(res) != 1) {
  838. G_warning(_("Unable to read node %d"), id);
  839. if (res)
  840. PQclear(res);
  841. return -1;
  842. }
  843. node->x = atof(PQgetvalue(res, 0, 0));
  844. node->y = atof(PQgetvalue(res, 0, 1));
  845. if (plus->with_z)
  846. node->z = atof(PQgetvalue(res, 0, 2));
  847. else
  848. node->z = 0;
  849. PQclear(res);
  850. plus->Node[n] = node;
  851. return 0;
  852. }
  853. /*!
  854. \brief Read P_line structure
  855. See dig_Rd_P_line() for reference.
  856. Supported feature types:
  857. - GV_POINT
  858. - GV_LINE
  859. - GV_BOUNDARY
  860. \param plus pointer to Plus_head structure
  861. \param n index (starts at 1)
  862. \param data edge data (id, start/end node, left/right face, ...)
  863. \param pg_info pointer to Format_info_pg sttucture
  864. \return 0 on success
  865. \return -1 on failure
  866. */
  867. int read_p_line(struct Plus_head *plus, int n,
  868. const struct edge_data *data)
  869. {
  870. int tp;
  871. struct P_line *line;
  872. if (data->start_node == 0 && data->end_node == 0) {
  873. if (data->left_face == 0)
  874. tp = GV_POINT;
  875. else
  876. tp = GV_CENTROID;
  877. }
  878. else if (data->left_face == 0 && data->right_face == 0) {
  879. tp = GV_LINE;
  880. }
  881. else {
  882. tp = GV_BOUNDARY;
  883. }
  884. if (tp == 0) { /* dead ??? */
  885. plus->Line[n] = NULL;
  886. return 0;
  887. }
  888. line = dig_alloc_line();
  889. /* type & offset ( = id) */
  890. line->type = tp;
  891. line->offset = data->id;
  892. G_debug(4, "read_p_line(): id/offset = %d type = %d", data->id, line->type);
  893. /* topo */
  894. if (line->type == GV_POINT) {
  895. line->topo = NULL;
  896. }
  897. else {
  898. line->topo = dig_alloc_topo(line->type);
  899. /* lines */
  900. if (line->type == GV_LINE) {
  901. struct P_topo_l *topo = (struct P_topo_l *)line->topo;
  902. topo->N1 = data->start_node;
  903. topo->N2 = data->end_node;
  904. }
  905. /* boundaries */
  906. else if (line->type == GV_BOUNDARY) {
  907. struct P_topo_b *topo = (struct P_topo_b *)line->topo;
  908. topo->N1 = data->start_node;
  909. topo->N2 = data->end_node;
  910. topo->left = data->left_face == 0 ? -1 : data->left_face;
  911. topo->right = data->right_face == 0 ? -1 : data->right_face;
  912. }
  913. /* centroids */
  914. else if (line->type == GV_CENTROID) {
  915. struct P_topo_c *topo = (struct P_topo_c *)line->topo;
  916. topo->area = data->left_face;
  917. }
  918. /* TODO: faces | kernels */
  919. }
  920. plus->Line[n] = line;
  921. return 0;
  922. }
  923. /*!
  924. \brief Read P_area structure
  925. See dig_Rd_P_area() for reference.
  926. \param plus pointer to Plus_head structure
  927. \param n index (starts at 1)
  928. \param face_id face id (see 'face' table)
  929. \param pg_info pointer to Format_info_pg sttucture
  930. \return 0 on success
  931. \return -1 on failure
  932. */
  933. int read_p_area(struct Plus_head *plus, int n, int face_id, struct Format_info_pg *pg_info)
  934. {
  935. int i, cnt;
  936. char stmt[DB_SQL_MAX];
  937. PGresult *res;
  938. struct P_area *area;
  939. sprintf(stmt,
  940. "SELECT edge from ST_GetFaceEdges('%s', %d)",
  941. pg_info->toposchema_name, face_id);
  942. G_debug(2, "SQL: %s", stmt);
  943. res = PQexec(pg_info->conn, stmt);
  944. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
  945. G_warning(_("Unable to read face %d"), face_id);
  946. if (res)
  947. PQclear(res);
  948. return -1;
  949. }
  950. cnt = PQntuples(res);
  951. if (cnt == 0) { /* dead */
  952. plus->Area[n] = NULL;
  953. return 0;
  954. }
  955. area = dig_alloc_area();
  956. /* boundaries */
  957. area->n_lines = cnt;
  958. if (dig_area_alloc_line(area, area->n_lines) == -1)
  959. return -1;
  960. area->lines = (plus_t *)G_realloc(area->lines, sizeof(plus_t) * area->n_lines);
  961. for (i = 0; i < area->n_lines; i++) {
  962. /* GRASS Topo model: lines in clockwise order
  963. PosTGIS Topo model: lines in counter clockwise order */
  964. area->lines[i] = -1 * atoi(PQgetvalue(res, i, 0));
  965. }
  966. /* isles */
  967. /* TODO */
  968. /* centroid */
  969. area->centroid = plus->n_lines - plus->n_clines + i;
  970. plus->Area[n] = area;
  971. PQclear(res);
  972. return 0;
  973. }
  974. /*!
  975. \brief Read topo (from PostGIS topology schema) header info only
  976. \param[in,out] plus pointer to Plus_head struct
  977. \return 0 on success
  978. \return -1 on error
  979. */
  980. int load_plus_head(struct Format_info_pg *pg_info, struct Plus_head *plus)
  981. {
  982. char stmt[DB_SQL_MAX];
  983. PGresult *res;
  984. plus->off_t_size = -1;
  985. /* get map bounding box */
  986. sprintf(stmt,
  987. "SELECT ST_3DExtent(%s) FROM \"%s\".\"%s\"",
  988. pg_info->topogeom_column, pg_info->schema_name, pg_info->table_name);
  989. G_debug(2, "SQL: %s", stmt);
  990. res = PQexec(pg_info->conn, stmt);
  991. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  992. PQntuples(res) != 1) {
  993. G_warning(_("Unable to get map bounding box from topology"));
  994. if (res)
  995. PQclear(res);
  996. return -1;
  997. }
  998. if (parse_bbox(PQgetvalue(res, 0, 0), &(plus->box), plus->with_z) != 0) {
  999. G_warning(_("Unable to parse map bounding box:\n%s"),
  1000. PQgetvalue(res, 0, 0));
  1001. return -1;
  1002. }
  1003. PQclear(res);
  1004. /* number of topological primitives */
  1005. /* nodes
  1006. note: isolated nodes are registered in GRASS Topology model */
  1007. sprintf(stmt,
  1008. "SELECT COUNT(DISTINCT node) FROM (SELECT start_node AS node "
  1009. "FROM \"%s\".edge GROUP BY start_node UNION ALL SELECT end_node "
  1010. "AS node FROM \"%s\".edge GROUP BY end_node) AS foo",
  1011. pg_info->toposchema_name, pg_info->toposchema_name);
  1012. plus->n_nodes = num_of_records(pg_info, stmt);
  1013. G_debug(3, "Vect_open_topo_pg(): n_nodes=%d", plus->n_nodes);
  1014. /* lines (edges in PostGIS Topology model) */
  1015. sprintf(stmt,
  1016. "SELECT COUNT(*) FROM \"%s\".edge",
  1017. pg_info->toposchema_name);
  1018. /* + isolated nodes as points
  1019. + centroids */
  1020. plus->n_lines = num_of_records(pg_info, stmt);
  1021. /* areas (faces in PostGIS Topology model) */
  1022. sprintf(stmt,
  1023. "SELECT COUNT(*) FROM \"%s\".face WHERE mbr IS NOT NULL",
  1024. pg_info->toposchema_name);
  1025. plus->n_areas = num_of_records(pg_info, stmt);
  1026. G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
  1027. /* TODO: n_isles | n_volumes | n_holes */
  1028. /* number of features group by type */
  1029. /* points */
  1030. sprintf(stmt,
  1031. "SELECT COUNT(*) FROM \"%s\".node WHERE node_id NOT IN "
  1032. "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
  1033. "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
  1034. "\"%s\".edge GROUP BY end_node) AS foo)",
  1035. pg_info->toposchema_name, pg_info->toposchema_name,
  1036. pg_info->toposchema_name);
  1037. plus->n_plines = num_of_records(pg_info, stmt);
  1038. G_debug(3, "Vect_open_topo_pg(): n_plines=%d", plus->n_plines);
  1039. /* lines */
  1040. sprintf(stmt,
  1041. "SELECT COUNT(*) FROM \"%s\".edge WHERE "
  1042. "left_face = 0 AND right_face = 0",
  1043. pg_info->toposchema_name);
  1044. plus->n_llines = num_of_records(pg_info, stmt);
  1045. G_debug(3, "Vect_open_topo_pg(): n_llines=%d", plus->n_llines);
  1046. /* boundaries */
  1047. sprintf(stmt,
  1048. "SELECT COUNT(*) FROM \"%s\".edge WHERE "
  1049. "left_face != 0 OR right_face != 0",
  1050. pg_info->toposchema_name);
  1051. plus->n_blines = num_of_records(pg_info, stmt);
  1052. G_debug(3, "Vect_open_topo_pg(): n_blines=%d", plus->n_blines);
  1053. /* centroids */
  1054. sprintf(stmt,
  1055. "SELECT COUNT(*) FROM \"%s\".face WHERE mbr IS NOT NULL",
  1056. pg_info->toposchema_name);
  1057. plus->n_clines = num_of_records(pg_info, stmt);
  1058. G_debug(3, "Vect_open_topo_pg(): n_clines=%d", plus->n_clines);
  1059. /* TODO: nflines | n_klines */
  1060. /* lines - register isolated nodes as points and centroids */
  1061. plus->n_lines += plus->n_plines + plus->n_clines;
  1062. G_debug(3, "Vect_open_topo_pg(): n_lines=%d", plus->n_lines);
  1063. return 0;
  1064. }
  1065. /*!
  1066. \brief Read topo info (from PostGIS topology schema)
  1067. \param pg_info pointer to Format_info_pg
  1068. \param[in,out] plus pointer to Plus_head struct
  1069. \param head_only TRUE to read only header info
  1070. \return 0 on success
  1071. \return -1 on error
  1072. */
  1073. int load_plus(struct Format_info_pg *pg_info, struct Plus_head *plus,
  1074. int head_only)
  1075. {
  1076. int i, id, ntuples;
  1077. char stmt[DB_SQL_MAX];
  1078. struct edge_data line_data;
  1079. PGresult *res;
  1080. if (load_plus_head(pg_info, plus) != 0)
  1081. return -1;
  1082. if (head_only)
  1083. return 0;
  1084. /* read nodes (GRASS Topo)
  1085. note: standalone nodes are ignored
  1086. */
  1087. sprintf(stmt,
  1088. "SELECT node_id FROM \"%s\".node WHERE node_id IN "
  1089. "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
  1090. "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
  1091. "\"%s\".edge GROUP BY end_node) AS foo)",
  1092. pg_info->toposchema_name, pg_info->toposchema_name,
  1093. pg_info->toposchema_name);
  1094. G_debug(2, "SQL: %s", stmt);
  1095. res = PQexec(pg_info->conn, stmt);
  1096. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  1097. PQntuples(res) != plus->n_nodes) {
  1098. G_warning(_("Unable to read nodes"));
  1099. if (res)
  1100. PQclear(res);
  1101. return -1;
  1102. }
  1103. G_debug(3, "load_plus(): n_nodes = %d", plus->n_nodes);
  1104. dig_alloc_nodes(plus, plus->n_nodes);
  1105. for (i = 1; i <= plus->n_nodes; i++) {
  1106. id = atoi(PQgetvalue(res, i - 1, 0));
  1107. read_p_node(plus, i, id, pg_info);
  1108. }
  1109. PQclear(res);
  1110. /* read lines (GRASS Topo)
  1111. - standalone nodes -> points
  1112. - edges -> lines/boundaries
  1113. */
  1114. G_debug(3, "load_plus(): n_lines = %d", plus->n_lines);
  1115. dig_alloc_lines(plus, plus->n_lines);
  1116. /* read PostGIS Topo standalone nodes */
  1117. sprintf(stmt,
  1118. "SELECT node_id FROM \"%s\".node WHERE node_id NOT IN "
  1119. "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
  1120. "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
  1121. "\"%s\".edge GROUP BY end_node) AS foo)",
  1122. pg_info->toposchema_name, pg_info->toposchema_name,
  1123. pg_info->toposchema_name);
  1124. G_debug(2, "SQL: %s", stmt);
  1125. res = PQexec(pg_info->conn, stmt);
  1126. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  1127. PQntuples(res) > plus->n_plines) {
  1128. G_warning(_("Unable to read lines"));
  1129. if (res)
  1130. PQclear(res);
  1131. return -1;
  1132. }
  1133. ntuples = PQntuples(res); /* plus->n_plines */
  1134. G_zero(&line_data, sizeof(struct edge_data));
  1135. for (i = 0; i < ntuples; i++) {
  1136. /* process standalone nodes (PostGIS Topo) */
  1137. line_data.id = atoi(PQgetvalue(res, i, 0));
  1138. read_p_line(plus, i + 1, &line_data);
  1139. }
  1140. PQclear(res);
  1141. /* read PostGIS Topo edges */
  1142. sprintf(stmt,
  1143. "SELECT edge_id,start_node,end_node,left_face,right_face "
  1144. "FROM \"%s\".edge",
  1145. pg_info->toposchema_name);
  1146. G_debug(2, "SQL: %s", stmt);
  1147. res = PQexec(pg_info->conn, stmt);
  1148. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  1149. PQntuples(res) > plus->n_lines) {
  1150. G_warning(_("Unable to read lines"));
  1151. if (res)
  1152. PQclear(res);
  1153. return -1;
  1154. }
  1155. ntuples = PQntuples(res);
  1156. for (i = 0; i < ntuples; i++) {
  1157. /* process edges (PostGIS Topo) */
  1158. line_data.id = atoi(PQgetvalue(res, i, 0));
  1159. line_data.start_node = atoi(PQgetvalue(res, i, 1));
  1160. line_data.end_node = atoi(PQgetvalue(res, i, 2));
  1161. line_data.left_face = atoi(PQgetvalue(res, i, 3));
  1162. line_data.right_face = atoi(PQgetvalue(res, i, 4));
  1163. read_p_line(plus, plus->n_plines + i + 1, &line_data);
  1164. }
  1165. PQclear(res);
  1166. /* read areas (GRASS Topo) */
  1167. sprintf(stmt,
  1168. "SELECT face_id from \"%s\".face WHERE mbr IS NOT NULL",
  1169. pg_info->toposchema_name);
  1170. G_debug(2, "SQL: %s", stmt);
  1171. res = PQexec(pg_info->conn, stmt);
  1172. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  1173. PQntuples(res) != plus->n_areas) {
  1174. G_warning(_("Unable to read areas"));
  1175. if (res)
  1176. PQclear(res);
  1177. return -1;
  1178. }
  1179. G_debug(3, "load_plus(): n_areas = %d", plus->n_areas);
  1180. dig_alloc_areas(plus, plus->n_areas);
  1181. G_zero(&line_data, sizeof(struct edge_data));
  1182. for (i = 1; i <= plus->n_areas; i++) {
  1183. line_data.id = line_data.left_face = atoi(PQgetvalue(res, i - 1, 0));
  1184. read_p_area(plus, i, line_data.id, pg_info);
  1185. /* add centroids */
  1186. read_p_line(plus, plus->n_lines - plus->n_clines + i, &line_data);
  1187. }
  1188. PQclear(res);
  1189. /* read isle (GRASS Topo) */
  1190. return 0;
  1191. }
  1192. #endif