open_pg.c 40 KB

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