build_pg.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. /*!
  2. \file lib/vector/Vlib/build_pg.c
  3. \brief Vector library - Building topology for PostGIS layers
  4. Higher level functions for reading/writing/manipulating vectors.
  5. Line offset (simple features only) is
  6. - centroids : FID
  7. - other types : index of the first record (which is FID) in offset array.
  8. (C) 2012-2013 by the GRASS Development Team
  9. This program is free software under the GNU General Public License
  10. (>=v2). Read the file COPYING that comes with GRASS for details.
  11. \author Martin Landa <landa.martin gmail.com>
  12. */
  13. #include <grass/vector.h>
  14. #include <grass/glocale.h>
  15. #include "local_proto.h"
  16. #ifdef HAVE_POSTGRES
  17. #include "pg_local_proto.h"
  18. static int build_topo(struct Map_info *, int);
  19. static int build_topogeom_stmt(const struct Format_info_pg *, int, int, int, char *);
  20. static int save_map_bbox(const struct Format_info_pg *, const struct bound_box*);
  21. static int create_topo_grass(const struct Format_info_pg *);
  22. static int has_topo_grass(const struct Format_info_pg *);
  23. static int write_nodes(const struct Plus_head *, const struct Format_info_pg *);
  24. static int write_lines(const struct Plus_head *, const struct Format_info_pg *);
  25. static int write_areas(const struct Plus_head *, const struct Format_info_pg *);
  26. static int write_isles(const struct Plus_head *, const struct Format_info_pg *);
  27. static void build_stmt_id(const void *, int, int, const struct Plus_head *, char **, size_t *);
  28. #endif
  29. /*!
  30. \brief Build topology for PostGIS layer
  31. Build levels:
  32. - GV_BUILD_NONE
  33. - GV_BUILD_BASE
  34. - GV_BUILD_ATTACH_ISLES
  35. - GV_BUILD_CENTROIDS
  36. - GV_BUILD_ALL
  37. \param Map pointer to Map_info structure
  38. \param build build level
  39. \return 1 on success
  40. \return 0 on error
  41. */
  42. int Vect_build_pg(struct Map_info *Map, int build)
  43. {
  44. #ifdef HAVE_POSTGRES
  45. struct Plus_head *plus;
  46. struct Format_info_pg *pg_info;
  47. plus = &(Map->plus);
  48. pg_info = &(Map->fInfo.pg);
  49. G_debug(1, "Vect_build_pg(): db='%s' table='%s', build=%d",
  50. pg_info->db_name, pg_info->table_name, build);
  51. /* commit transaction block (update mode only) */
  52. if (pg_info->inTransaction && Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
  53. return 0;
  54. pg_info->inTransaction = FALSE;
  55. if (pg_info->feature_type == SF_UNKNOWN)
  56. return 1;
  57. if (build == plus->built)
  58. return 1; /* do nothing */
  59. /* TODO move this init to better place (Vect_open_ ?), because in
  60. theory build may be reused on level2 */
  61. if (!pg_info->toposchema_name && build >= plus->built && build > GV_BUILD_BASE) {
  62. G_free(pg_info->offset.array);
  63. G_zero(&(pg_info->offset), sizeof(struct Format_info_offset));
  64. }
  65. if (!pg_info->conn) {
  66. G_warning(_("No DB connection"));
  67. return 0;
  68. }
  69. if (!pg_info->fid_column && !pg_info->toposchema_name) {
  70. G_warning(_("Feature table <%s> has no primary key defined"),
  71. pg_info->table_name);
  72. G_warning(_("Random read is not supported for this layer. "
  73. "Unable to build topology."));
  74. return 0;
  75. }
  76. if (build > GV_BUILD_NONE) {
  77. G_message(_("Using external data format '%s' (feature type '%s')"),
  78. Vect_get_finfo_format_info(Map),
  79. Vect_get_finfo_geometry_type(Map));
  80. if (!pg_info->toposchema_name)
  81. G_message(_("Building pseudo-topology over simple features..."));
  82. else
  83. G_message(_("Building topology from PostGIS topology schema <%s>..."),
  84. pg_info->toposchema_name);
  85. }
  86. if (!pg_info->toposchema_name) /* pseudo-topology for simple features */
  87. return Vect__build_sfa(Map, build);
  88. /* PostGIS Topology */
  89. return build_topo(Map, build);
  90. #else
  91. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  92. return 0;
  93. #endif
  94. }
  95. #ifdef HAVE_POSTGRES
  96. /*!
  97. \brief Build from PostGIS topology schema
  98. \todo Attach isles
  99. \param Map pointer to Map_info struct
  100. \param build build level
  101. \return 1 on success
  102. \return 0 on error
  103. */
  104. int build_topo(struct Map_info *Map, int build)
  105. {
  106. int line, type, s, n_nodes;
  107. int area, nareas, isle, nisles;
  108. int face[2];
  109. char stmt[DB_SQL_MAX];
  110. struct Plus_head *plus;
  111. struct Format_info_pg *pg_info;
  112. struct P_line *Line;
  113. struct P_area *Area;
  114. struct P_topo_b *topo_b;
  115. struct P_isle *Isle;
  116. plus = &(Map->plus);
  117. pg_info = &(Map->fInfo.pg);
  118. /* check if upgrade or downgrade */
  119. if (build < plus->built) {
  120. /* -> downgrade */
  121. Vect__build_downgrade(Map, build);
  122. return 1;
  123. }
  124. /* -> upgrade */
  125. if (build < GV_BUILD_BASE)
  126. return 1; /* nothing to print */
  127. /* cache features to speed-up random access (when attaching isles
  128. to areas) */
  129. if (build >= GV_BUILD_BASE) {
  130. /* clean-up GRASS topology tables in DB */
  131. if (!pg_info->topo_geo_only)
  132. Vect__clean_grass_db_topo(&(Map->fInfo.pg));
  133. if (Map->mode == GV_MODE_RW &&
  134. pg_info->cache.lines_num > 0) {
  135. /* read line cache from scratch when map is open in update
  136. * mode, before building native topology read nodes from
  137. * PostGIS Topology */
  138. /* clean-up spatial a category indeces */
  139. dig_free_plus(&(Map->plus));
  140. dig_init_plus(&(Map->plus));
  141. plus->Spidx_new = TRUE;
  142. plus->update_cidx = TRUE;
  143. /* reset cache for reading features */
  144. Vect__free_cache(&(pg_info->cache));
  145. }
  146. }
  147. if (plus->built < GV_BUILD_BASE) {
  148. /* force loading nodes from DB to get up-to-date node
  149. * offsets, see write_nodes() for details */
  150. Vect__free_offset(&(pg_info->offset));
  151. pg_info->cache.ctype = CACHE_FEATURE; /* do not cache nodes */
  152. n_nodes = Map->plus.n_nodes = Vect__load_map_nodes_pg(Map, TRUE);
  153. Vect__free_cache(&(pg_info->cache));
  154. }
  155. if (build > GV_BUILD_BASE)
  156. pg_info->cache.ctype = CACHE_MAP; /* cache all features */
  157. /* update TopoGeometry based on GRASS-like topology */
  158. Vect_build_nat(Map, build);
  159. if (n_nodes != Map->plus.n_nodes)
  160. G_warning(_("Inconsistency in topology: number of nodes %d (should be %d)"),
  161. Map->plus.n_nodes, n_nodes);
  162. /* store map boundig box in DB */
  163. save_map_bbox(pg_info, &(plus->box));
  164. /* begin transaction */
  165. if (Vect__execute_pg(pg_info->conn, "BEGIN"))
  166. return 0;
  167. Vect__execute_pg(pg_info->conn, "SET CONSTRAINTS ALL DEFERRED");
  168. /* write full node topo info to DB if requested */
  169. if (!pg_info->topo_geo_only) {
  170. write_nodes(plus, pg_info);
  171. write_lines(plus, pg_info);
  172. }
  173. /* update faces from GRASS Topology */
  174. if (build >= GV_BUILD_AREAS) {
  175. /* do clean up (1-3)
  176. insert new faces (4)
  177. update edges (5)
  178. */
  179. G_message(_("Cleaning-up topology schema..."));
  180. /* 1) reset centroids to '0' (universal face) */
  181. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 WHERE "
  182. "containing_face IS NOT NULL", pg_info->toposchema_name);
  183. G_debug(2, "SQL: %s", stmt);
  184. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  185. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  186. return 0;
  187. }
  188. /* 2) reset left|right edges */
  189. sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0, right_face = 0",
  190. pg_info->toposchema_name);
  191. G_debug(2, "SQL: %s", stmt);
  192. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  193. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  194. return 0;
  195. }
  196. /* 3) delete faces (areas/isles) */
  197. sprintf(stmt, "DELETE FROM \"%s\".face WHERE "
  198. "face_id != 0", pg_info->toposchema_name);
  199. G_debug(2, "SQL: %s", stmt);
  200. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  201. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  202. return 0;
  203. }
  204. if (!pg_info->topo_geo_only) {
  205. sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_AREA);
  206. G_debug(2, "SQL: %s", stmt);
  207. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  208. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  209. return 0;
  210. }
  211. sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_ISLE);
  212. G_debug(2, "SQL: %s", stmt);
  213. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  214. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  215. return 0;
  216. }
  217. }
  218. /* 4) insert faces & update nodes (containing_face) based on
  219. * GRASS topology */
  220. G_message(_("Updating faces..."));
  221. nareas = Vect_get_num_areas(Map);
  222. for (area = 1; area <= nareas; area++) {
  223. G_percent(area, nareas, 5);
  224. if (0 == Vect__insert_face_pg(Map, area)) {
  225. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  226. return 0;
  227. }
  228. if (build < GV_BUILD_CENTROIDS)
  229. continue;
  230. /* update centroids (node -> containing_face) */
  231. Area = plus->Area[area];
  232. if (Area->centroid < 1) {
  233. G_debug(3, "Area %d without centroid, skipped", area);
  234. continue;
  235. }
  236. Line = plus->Line[Area->centroid];
  237. sprintf(stmt, "UPDATE \"%s\".node SET "
  238. "containing_face = %d WHERE node_id = %d",
  239. pg_info->toposchema_name, area, (int)Line->offset);
  240. G_debug(2, "SQL: %s", stmt);
  241. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  242. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  243. return 0;
  244. }
  245. }
  246. /* 5) update edges (left and right face) */
  247. G_message(_("Updating edges..."));
  248. for (line = 1; line <= plus->n_lines; line++) {
  249. G_percent(line, plus->n_lines, 5);
  250. type = Vect_read_line(Map, NULL, NULL, line);
  251. if (type != GV_BOUNDARY)
  252. continue;
  253. Line = Map->plus.Line[line];
  254. if (!Line) {
  255. G_warning(_("Inconsistency in topology detected. "
  256. "Dead line found."));
  257. return 0;
  258. }
  259. topo_b = (struct P_topo_b *) Line->topo;
  260. for (s = 0; s < 2; s++) { /* for both sides */
  261. face[s] = s == 0 ? topo_b->left : topo_b->right;
  262. if (face[s] < 0) {
  263. /* isle */
  264. Isle = plus->Isle[abs(face[s])];
  265. face[s] = Isle->area;
  266. }
  267. }
  268. G_debug(3, "update edge %d: left_face = %d, right_face = %d",
  269. (int)Line->offset, face[0], face[1]);
  270. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  271. "left_face = %d, right_face = %d "
  272. "WHERE edge_id = %d", pg_info->toposchema_name,
  273. face[0], face[1], (int) Line->offset);
  274. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  275. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  276. return 0;
  277. }
  278. }
  279. /* write full area topo info to DB if requested */
  280. if (!pg_info->topo_geo_only) {
  281. write_areas(plus, pg_info);
  282. }
  283. } /* build >= GV_BUILD_AREAS */
  284. if (build >= GV_BUILD_ATTACH_ISLES) {
  285. /* insert isles as faces with negative face_id */
  286. nisles = Vect_get_num_islands(Map);
  287. for(isle = 1; isle <= nisles; isle++) {
  288. Isle = plus->Isle[isle];
  289. Vect__insert_face_pg(Map, -isle);
  290. }
  291. /* write full isles topo info to DB if requested */
  292. if (!pg_info->topo_geo_only) {
  293. write_isles(plus, pg_info);
  294. }
  295. } /* build >= GV_BUILD_ISLES */
  296. if (pg_info->feature_type == SF_POLYGON) {
  297. int centroid;
  298. struct P_line *Line;
  299. G_message(_("Updating TopoGeometry data..."));
  300. for (area = 1; area <= plus->n_areas; area++) {
  301. G_percent(area, plus->n_areas, 5);
  302. centroid = Vect_get_area_centroid(Map, area);
  303. if (centroid < 1)
  304. continue;
  305. Line = plus->Line[centroid];
  306. if (!Line)
  307. continue;
  308. /* update topogeometry object: centroid -> face */
  309. if (build_topogeom_stmt(pg_info, GV_CENTROID, area, (int) Line->offset, stmt) &&
  310. Vect__execute_pg(pg_info->conn, stmt) == -1) {
  311. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  312. return 0;
  313. }
  314. }
  315. }
  316. if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
  317. return 0;
  318. return 1;
  319. }
  320. /*!
  321. \brief Build UPDATE statement for topo geometry element stored in
  322. feature table
  323. \param pg_info so pointer to Format_info_pg
  324. \param type feature type (GV_POINT, ...)
  325. \param topo_id topology element id
  326. \param fid feature id
  327. \param[out] stmt string buffer
  328. \return 1 on success
  329. \return 0 on failure
  330. */
  331. int build_topogeom_stmt(const struct Format_info_pg *pg_info,
  332. int type, int topo_id, int fid, char *stmt)
  333. {
  334. int topogeom_type;
  335. switch(type) {
  336. case GV_POINT:
  337. topogeom_type = 1;
  338. break;
  339. case GV_LINE:
  340. case GV_BOUNDARY:
  341. topogeom_type = 2;
  342. break;
  343. case GV_CENTROID:
  344. topogeom_type = 3;
  345. break;
  346. default:
  347. G_warning(_("Unsupported topo geometry type %d"), type);
  348. return 0;
  349. }
  350. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
  351. "'(%d, 1, %d, %d)'::topology.TopoGeometry "
  352. "WHERE (%s).id = %d",
  353. pg_info->schema_name, pg_info->table_name,
  354. pg_info->topogeom_column, pg_info->toposchema_id,
  355. topo_id, topogeom_type, pg_info->topogeom_column, fid);
  356. return 1;
  357. }
  358. /*!
  359. \brief Store map bounding box in DB head table
  360. \param pg_info pointer to Format_info_pg struct
  361. \param box pointer to bounding box
  362. \return 1 on success
  363. \return 0 on failure
  364. */
  365. int save_map_bbox(const struct Format_info_pg *pg_info, const struct bound_box *box)
  366. {
  367. char stmt[DB_SQL_MAX];
  368. /* create if not exists */
  369. if (create_topo_grass(pg_info) == -1) {
  370. G_warning(_("Unable to create <%s.%s>"), TOPO_SCHEMA, TOPO_TABLE);
  371. return 0;
  372. }
  373. /* update bbox */
  374. if (has_topo_grass(pg_info)) {
  375. /* -> update */
  376. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
  377. "'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d WHERE %s = %d",
  378. TOPO_SCHEMA, TOPO_TABLE, TOPO_BBOX,
  379. box->W, box->S, box->B, box->E, box->N, box->T,
  380. TOPO_ID, pg_info->toposchema_id);
  381. }
  382. else {
  383. /* -> insert */
  384. sprintf(stmt, "INSERT INTO \"%s\".\"%s\" (%s, %s) "
  385. "VALUES(%d, 'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d)",
  386. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX, pg_info->toposchema_id,
  387. box->W, box->S, box->B, box->E, box->N, box->T);
  388. }
  389. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  390. return -1;
  391. }
  392. return 1;
  393. }
  394. /*!
  395. \brief Creates 'topology.grass' table if not exists
  396. \return 0 table already exists
  397. \return 1 table successfully added
  398. \return -1 on error
  399. */
  400. int create_topo_grass(const struct Format_info_pg *pg_info)
  401. {
  402. char stmt[DB_SQL_MAX];
  403. PGresult *result;
  404. /* check if table exists */
  405. sprintf(stmt, "SELECT COUNT(*) FROM information_schema.tables "
  406. "WHERE table_schema = '%s' AND table_name = '%s'",
  407. TOPO_SCHEMA, TOPO_TABLE);
  408. result = PQexec(pg_info->conn, stmt);
  409. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  410. PQclear(result);
  411. return -1;
  412. }
  413. if (atoi(PQgetvalue(result, 0, 0)) == 1) {
  414. /* table already exists */
  415. PQclear(result);
  416. return 1;
  417. }
  418. PQclear(result);
  419. G_debug(1, "<%s.%s> created", TOPO_SCHEMA, TOPO_TABLE);
  420. /* create table */
  421. sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s INTEGER, %s box3d)",
  422. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX);
  423. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  424. return -1;
  425. }
  426. /* add primary key */
  427. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD PRIMARY KEY (%s)",
  428. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID);
  429. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  430. return -1;
  431. }
  432. /* add constraint */
  433. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD CONSTRAINT \"%s_%s_fkey\" "
  434. "FOREIGN KEY (%s) REFERENCES topology.topology(id) ON DELETE CASCADE",
  435. TOPO_SCHEMA, TOPO_TABLE, TOPO_TABLE, TOPO_ID, TOPO_ID);
  436. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  437. return -1;
  438. }
  439. return 1;
  440. }
  441. /*!
  442. \brief Check if 'topology_id' exists in 'topology.grass'
  443. \param pg_info pointer to Format_info_pg struct
  444. \return TRUE if exists
  445. \return FALSE otherwise
  446. \return -1 on error
  447. */
  448. int has_topo_grass(const struct Format_info_pg *pg_info)
  449. {
  450. int has_topo;
  451. char stmt[DB_SQL_MAX];
  452. PGresult *result;
  453. sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".\"%s\" "
  454. "WHERE %s = %d",
  455. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, pg_info->toposchema_id);
  456. result = PQexec(pg_info->conn, stmt);
  457. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  458. PQclear(result);
  459. return -1;
  460. }
  461. has_topo = FALSE;
  462. if (atoi(PQgetvalue(result, 0, 0)) == 1) {
  463. /* table already exists */
  464. has_topo = TRUE;
  465. }
  466. PQclear(result);
  467. return has_topo;
  468. }
  469. /*!
  470. \brief Insert node into 'node_grass' table
  471. Writes (see P_node struct):
  472. - lines
  473. - angles
  474. Already stored in Topo-Geo:
  475. - x,y,z (geom)
  476. \param plus pointer to Plus_head struct
  477. \param pg_info pointer to Format_info_pg struct
  478. \return 0 on success
  479. \return -1 on error
  480. */
  481. int write_nodes(const struct Plus_head *plus,
  482. const struct Format_info_pg *pg_info)
  483. {
  484. int i, node_id;
  485. size_t stmt_lines_size, stmt_angles_size, stmt_size;
  486. char *stmt_lines, *stmt_angles, *stmt;
  487. const struct P_node *Node;
  488. const struct Format_info_offset *offset;
  489. offset = &(pg_info->offset);
  490. if (offset->array_num < 1) /* nothing to write */
  491. return 0;
  492. if (plus->n_nodes != offset->array_num) {
  493. G_warning(_("Unable to write nodes, offset array mismatch"));
  494. return -1;
  495. }
  496. stmt_size = 2 * DB_SQL_MAX + 512;
  497. stmt = (char *) G_malloc(stmt_size);
  498. stmt_lines = stmt_angles = NULL;
  499. for (i = 1; i <= plus->n_nodes; i++) {
  500. Node = plus->Node[i];
  501. if (!Node)
  502. continue; /* should not happen */
  503. node_id = offset->array[i-1];
  504. /* 'lines' array */
  505. build_stmt_id(Node->lines, Node->n_lines, TRUE, plus, &stmt_lines, &stmt_lines_size);
  506. /* 'angle' array */
  507. build_stmt_id(Node->angles, Node->n_lines, FALSE, NULL, &stmt_angles, &stmt_angles_size);
  508. /* build SQL statement to add new node into 'node_grass' */
  509. if (stmt_lines_size + stmt_angles_size + 512 > stmt_size) {
  510. stmt_size = stmt_lines_size + stmt_angles_size + 512;
  511. stmt = (char *) G_realloc(stmt, stmt_size);
  512. }
  513. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  514. "%d, '{%s}', '{%s}')", pg_info->toposchema_name, TOPO_TABLE_NODE,
  515. node_id, stmt_lines, stmt_angles);
  516. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  517. G_warning(_("Unable to write nodes"));
  518. return -1;
  519. }
  520. }
  521. G_free(stmt_lines);
  522. G_free(stmt_angles);
  523. G_free(stmt);
  524. return 0;
  525. }
  526. /*!
  527. \brief Insert lines into 'line_grass' table
  528. Writes (see P_line struct) - only for boundaries:
  529. - left, right area
  530. Already stored in Topo-Geo:
  531. - edge_id, left_face, right_face
  532. \param plus pointer to Plus_head struct
  533. \param pg_info pointer to Format_info_pg struct
  534. \return 0 on success
  535. \return -1 on error
  536. */
  537. int write_lines(const struct Plus_head *plus,
  538. const struct Format_info_pg *pg_info)
  539. {
  540. int i, row, offset;
  541. char stmt[DB_SQL_MAX];
  542. const struct P_line *Line;
  543. const struct P_topo_b *topo;
  544. PGresult *res;
  545. sprintf(stmt, "SELECT edge_id FROM \"%s\".edge_data WHERE "
  546. "left_face != 0 OR right_face != 0 ORDER BY edge_id",
  547. pg_info->toposchema_name);
  548. G_debug(2, "SQL: %s", stmt);
  549. res = PQexec(pg_info->conn, stmt);
  550. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  551. (PQntuples(res) > 0 && PQntuples(res) != plus->n_blines)) {
  552. G_warning(_("Inconsistency in topology: number of "
  553. "boundaries %d (should be %d)"),
  554. PQntuples(res), plus->n_blines);
  555. if (res)
  556. PQclear(res);
  557. return -1;
  558. }
  559. for (row = 0, i = 1; i <= plus->n_lines; i++) {
  560. Line = plus->Line[i];
  561. if (!Line || Line->type != GV_BOUNDARY)
  562. continue;
  563. if (Line->offset == 0L)
  564. offset = atoi(PQgetvalue(res, row++, 0));
  565. else
  566. offset = (int)Line->offset;
  567. topo = (struct P_topo_b *)Line->topo;
  568. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  569. "%d, %d, %d)", pg_info->toposchema_name, TOPO_TABLE_LINE,
  570. offset, topo->left, topo->right);
  571. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  572. G_warning(_("Unable to write lines"));
  573. return -1;
  574. }
  575. }
  576. return 0;
  577. }
  578. /*!
  579. \brief Insert area into 'area_grass' table
  580. Writes (see P_area struct):
  581. - lines
  582. - centroid
  583. - isles
  584. \param plus pointer to Plus_head struct
  585. \param pg_info pointer to Format_info_pg struct
  586. \return 0 on success
  587. \return -1 on error
  588. */
  589. int write_areas(const struct Plus_head *plus,
  590. const struct Format_info_pg *pg_info)
  591. {
  592. int area, centroid;
  593. size_t stmt_lines_size, stmt_isles_size, stmt_size;
  594. char *stmt_lines, *stmt_isles, *stmt;
  595. const struct P_line *Line;
  596. const struct P_area *Area;
  597. stmt_size = 2 * DB_SQL_MAX + 512;
  598. stmt = (char *) G_malloc(stmt_size);
  599. stmt_lines = stmt_isles = NULL;
  600. for (area = 1; area <= plus->n_areas; area++) {
  601. Area = plus->Area[area];
  602. if (!Area) {
  603. G_debug(3, "Area %d skipped (dead)", area);
  604. continue; /* should not happen */
  605. }
  606. /* 'lines' array */
  607. build_stmt_id(Area->lines, Area->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
  608. /* 'isles' array */
  609. build_stmt_id(Area->isles, Area->n_isles, TRUE, NULL, &stmt_isles, &stmt_isles_size);
  610. if (Area->centroid != 0) {
  611. Line = plus->Line[Area->centroid];
  612. if (!Line) {
  613. G_warning(_("Topology for centroid %d not available. Area %d skipped"),
  614. Area->centroid, area);
  615. continue;
  616. }
  617. centroid = (int) Line->offset;
  618. }
  619. else {
  620. centroid = 0;
  621. }
  622. /* build SQL statement to add new node into 'node_grass' */
  623. if (stmt_lines_size + stmt_isles_size + 512 > stmt_size) {
  624. stmt_size = stmt_lines_size + stmt_isles_size + 512;
  625. stmt = (char *) G_realloc(stmt, stmt_size);
  626. }
  627. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  628. "%d, '{%s}', %d, '{%s}')", pg_info->toposchema_name, TOPO_TABLE_AREA,
  629. area, stmt_lines, centroid, stmt_isles);
  630. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  631. return -1;
  632. }
  633. }
  634. G_free(stmt_lines);
  635. G_free(stmt_isles);
  636. G_free(stmt);
  637. return 0;
  638. }
  639. /*!
  640. \brief Insert isle into 'isle_grass' table
  641. Writes (see P_isle struct):
  642. - lines
  643. - area
  644. \param plus pointer to Plus_head struct
  645. \param pg_info pointer to Format_info_pg struct
  646. \return 0 on success
  647. \return -1 on error
  648. */
  649. int write_isles(const struct Plus_head *plus,
  650. const struct Format_info_pg *pg_info)
  651. {
  652. int isle;
  653. size_t stmt_lines_size, stmt_size;
  654. char *stmt_lines, *stmt;
  655. const struct P_isle *Isle;
  656. stmt_size = DB_SQL_MAX + 512;
  657. stmt = (char *) G_malloc(stmt_size);
  658. stmt_lines = NULL;
  659. for (isle = 1; isle <= plus->n_isles; isle++) {
  660. Isle = plus->Isle[isle];
  661. if (!Isle)
  662. continue; /* should not happen */
  663. /* 'lines' array */
  664. build_stmt_id(Isle->lines, Isle->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
  665. /* build SQL statement to add new node into 'node_grass' */
  666. if (stmt_lines_size + 512 > stmt_size) {
  667. stmt_size = stmt_lines_size + 512;
  668. stmt = (char *) G_realloc(stmt, stmt_size);
  669. }
  670. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  671. "%d, '{%s}', %d)", pg_info->toposchema_name, TOPO_TABLE_ISLE,
  672. isle, stmt_lines, Isle->area);
  673. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  674. return -1;
  675. }
  676. }
  677. G_free(stmt_lines);
  678. G_free(stmt);
  679. return 0;
  680. }
  681. /*!
  682. \brief Create PG-like array for int/float array
  683. \param array array of items
  684. \param nitems number of items in the array
  685. \param is_int TRUE for array of integers otherwise floats
  686. \param plus pointer to Plus_head struct
  687. \param[in,out] output buffer (re-used)
  688. \param[in,out] buffer size
  689. */
  690. void build_stmt_id(const void *array, int nitems, int is_int, const struct Plus_head *plus,
  691. char **stmt, size_t *stmt_size)
  692. {
  693. int i, ivalue;
  694. int *iarray;
  695. float *farray;
  696. size_t stmt_id_size;
  697. char *stmt_id, buf_id[128];
  698. struct P_line *Line;
  699. if (is_int)
  700. iarray = (int *) array;
  701. else
  702. farray = (float *) array;
  703. if (!(*stmt)) {
  704. stmt_id_size = DB_SQL_MAX;
  705. stmt_id = (char *) G_malloc(stmt_id_size);
  706. }
  707. else {
  708. stmt_id_size = *stmt_size;
  709. stmt_id = *stmt;
  710. }
  711. /* reset array */
  712. stmt_id[0] = '\0';
  713. for (i = 0; i < nitems; i++) {
  714. /* realloc array if needed */
  715. if (strlen(stmt_id) + 100 > stmt_id_size) {
  716. stmt_id_size = strlen(stmt_id) + DB_SQL_MAX;
  717. stmt_id = (char *) G_realloc(stmt_id, stmt_id_size);
  718. }
  719. if (is_int) {
  720. if (plus) {
  721. Line = plus->Line[abs(iarray[i])];
  722. ivalue = (int) Line->offset;
  723. if (iarray[i] < 0)
  724. ivalue *= -1;
  725. }
  726. else {
  727. ivalue = iarray[i];
  728. }
  729. sprintf(buf_id, "%d", ivalue);
  730. }
  731. else {
  732. sprintf(buf_id, "%f", farray[i]);
  733. }
  734. if (i > 0)
  735. strcat(stmt_id, ",");
  736. strcat(stmt_id, buf_id);
  737. }
  738. *stmt = stmt_id;
  739. *stmt_size = stmt_id_size;
  740. }
  741. /*!
  742. \brief Clean-up GRASS Topology tables
  743. \param pg_info pointer to Format_info_pg pg_info
  744. \return 0 on success
  745. \return -1 on error
  746. */
  747. int Vect__clean_grass_db_topo(struct Format_info_pg *pg_info)
  748. {
  749. char stmt[DB_SQL_MAX];
  750. sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
  751. pg_info->toposchema_name, TOPO_TABLE_NODE);
  752. if (-1 == Vect__execute_pg(pg_info->conn, stmt))
  753. return -1;
  754. sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
  755. pg_info->toposchema_name, TOPO_TABLE_LINE);
  756. if (-1 == Vect__execute_pg(pg_info->conn, stmt))
  757. return -1;
  758. sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
  759. pg_info->toposchema_name, TOPO_TABLE_AREA);
  760. if (-1 == Vect__execute_pg(pg_info->conn, stmt))
  761. return -1;
  762. sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
  763. pg_info->toposchema_name, TOPO_TABLE_ISLE);
  764. if (-1 == Vect__execute_pg(pg_info->conn, stmt))
  765. return -1;
  766. return 0;
  767. }
  768. #endif