build_pg.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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;
  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. pg_info->cache.ctype = CACHE_MAP;
  131. if (Map->mode == GV_MODE_RW &&
  132. pg_info->cache.lines_num > 0) {
  133. /* read line cache from scratch when map is open in update
  134. * mode, before building native topology read nodes from
  135. * PostGIS Topology */
  136. /* clean-up spatial a category indeces */
  137. dig_free_plus(&(Map->plus));
  138. dig_init_plus(&(Map->plus));
  139. plus->Spidx_new = TRUE;
  140. plus->update_cidx = TRUE;
  141. /* reset cache for reading features */
  142. Vect__free_cache(&(pg_info->cache));
  143. /* force loading nodes from DB to get up-to-date node
  144. * offsets */
  145. Vect__free_offset(&(pg_info->offset));
  146. Map->plus.n_nodes = Vect__load_map_nodes_pg(Map, TRUE);
  147. }
  148. }
  149. /* update TopoGeometry based on GRASS-like topology */
  150. Vect_build_nat(Map, build);
  151. /* store map boundig box in DB */
  152. save_map_bbox(pg_info, &(plus->box));
  153. /* begin transaction */
  154. if (Vect__execute_pg(pg_info->conn, "BEGIN"))
  155. return 0;
  156. Vect__execute_pg(pg_info->conn, "SET CONSTRAINTS ALL DEFERRED");
  157. /* write full node topo info to DB if requested */
  158. if (!pg_info->topo_geo_only) {
  159. write_nodes(plus, pg_info);
  160. write_lines(plus, pg_info);
  161. }
  162. /* update faces from GRASS Topology */
  163. if (build >= GV_BUILD_AREAS) {
  164. /* do clean up (1-3)
  165. insert new faces (4)
  166. update edges (5)
  167. */
  168. G_message(_("Cleaning-up topology schema..."));
  169. /* 1) reset centroids to '0' (universal face) */
  170. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 WHERE "
  171. "containing_face IS NOT NULL", pg_info->toposchema_name);
  172. G_debug(2, "SQL: %s", stmt);
  173. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  174. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  175. return 0;
  176. }
  177. /* 2) reset left|right edges */
  178. sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0, right_face = 0",
  179. pg_info->toposchema_name);
  180. G_debug(2, "SQL: %s", stmt);
  181. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  182. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  183. return 0;
  184. }
  185. /* 3) delete faces (areas/isles) */
  186. sprintf(stmt, "DELETE FROM \"%s\".face WHERE "
  187. "face_id != 0", pg_info->toposchema_name);
  188. G_debug(2, "SQL: %s", stmt);
  189. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  190. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  191. return 0;
  192. }
  193. if (!pg_info->topo_geo_only) {
  194. sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_AREA);
  195. G_debug(2, "SQL: %s", stmt);
  196. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  197. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  198. return 0;
  199. }
  200. sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_ISLE);
  201. G_debug(2, "SQL: %s", stmt);
  202. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  203. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  204. return 0;
  205. }
  206. }
  207. /* 4) insert faces & update nodes (containing_face) based on
  208. * GRASS topology */
  209. G_message(_("Updating faces..."));
  210. nareas = Vect_get_num_areas(Map);
  211. for (area = 1; area <= nareas; area++) {
  212. G_percent(area, nareas, 5);
  213. if (0 == Vect__insert_face_pg(Map, area)) {
  214. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  215. return 0;
  216. }
  217. if (build < GV_BUILD_CENTROIDS)
  218. continue;
  219. /* update centroids (node -> containing_face) */
  220. Area = plus->Area[area];
  221. if (Area->centroid < 1) {
  222. G_debug(3, "Area %d without centroid, skipped", area);
  223. continue;
  224. }
  225. Line = plus->Line[Area->centroid];
  226. sprintf(stmt, "UPDATE \"%s\".node SET "
  227. "containing_face = %d WHERE node_id = %d",
  228. pg_info->toposchema_name, area, (int)Line->offset);
  229. G_debug(2, "SQL: %s", stmt);
  230. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  231. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  232. return 0;
  233. }
  234. }
  235. /* 5) update edges (left and right face) */
  236. G_message(_("Updating edges..."));
  237. for (line = 1; line <= plus->n_lines; line++) {
  238. G_percent(line, plus->n_lines, 5);
  239. type = Vect_read_line(Map, NULL, NULL, line);
  240. if (type != GV_BOUNDARY)
  241. continue;
  242. Line = Map->plus.Line[line];
  243. if (!Line) {
  244. G_warning(_("Inconsistency in topology detected. "
  245. "Dead line found."));
  246. return 0;
  247. }
  248. topo_b = (struct P_topo_b *) Line->topo;
  249. for (s = 0; s < 2; s++) { /* for both sides */
  250. face[s] = s == 0 ? topo_b->left : topo_b->right;
  251. if (face[s] < 0) {
  252. /* isle */
  253. Isle = plus->Isle[abs(face[s])];
  254. face[s] = Isle->area;
  255. }
  256. }
  257. G_debug(3, "update edge %d: left_face = %d, right_face = %d",
  258. (int)Line->offset, face[0], face[1]);
  259. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  260. "left_face = %d, right_face = %d "
  261. "WHERE edge_id = %d", pg_info->toposchema_name,
  262. face[0], face[1], (int) Line->offset);
  263. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  264. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  265. return 0;
  266. }
  267. }
  268. /* write full area topo info to DB if requested */
  269. if (!pg_info->topo_geo_only) {
  270. write_areas(plus, pg_info);
  271. }
  272. } /* build >= GV_BUILD_AREAS */
  273. if (build >= GV_BUILD_ATTACH_ISLES) {
  274. /* insert isles as faces with negative face_id */
  275. nisles = Vect_get_num_islands(Map);
  276. for(isle = 1; isle <= nisles; isle++) {
  277. Isle = plus->Isle[isle];
  278. Vect__insert_face_pg(Map, -isle);
  279. }
  280. /* write full isles topo info to DB if requested */
  281. if (!pg_info->topo_geo_only) {
  282. write_isles(plus, pg_info);
  283. }
  284. } /* build >= GV_BUILD_ISLES */
  285. if (pg_info->feature_type == SF_POLYGON) {
  286. int centroid;
  287. struct P_line *Line;
  288. G_message(_("Updating TopoGeometry data..."));
  289. for (area = 1; area <= plus->n_areas; area++) {
  290. G_percent(area, plus->n_areas, 5);
  291. centroid = Vect_get_area_centroid(Map, area);
  292. if (centroid < 1)
  293. continue;
  294. Line = plus->Line[centroid];
  295. if (!Line)
  296. continue;
  297. /* update topogeometry object: centroid -> face */
  298. if (build_topogeom_stmt(pg_info, GV_CENTROID, area, (int) Line->offset, stmt) &&
  299. Vect__execute_pg(pg_info->conn, stmt) == -1) {
  300. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  301. return 0;
  302. }
  303. }
  304. }
  305. if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
  306. return 0;
  307. return 1;
  308. }
  309. /*!
  310. \brief Build UPDATE statement for topo geometry element stored in
  311. feature table
  312. \param pg_info so pointer to Format_info_pg
  313. \param type feature type (GV_POINT, ...)
  314. \param topo_id topology element id
  315. \param fid feature id
  316. \param[out] stmt string buffer
  317. \return 1 on success
  318. \return 0 on failure
  319. */
  320. int build_topogeom_stmt(const struct Format_info_pg *pg_info,
  321. int type, int topo_id, int fid, char *stmt)
  322. {
  323. int topogeom_type;
  324. switch(type) {
  325. case GV_POINT:
  326. topogeom_type = 1;
  327. break;
  328. case GV_LINE:
  329. case GV_BOUNDARY:
  330. topogeom_type = 2;
  331. break;
  332. case GV_CENTROID:
  333. topogeom_type = 3;
  334. break;
  335. default:
  336. G_warning(_("Unsupported topo geometry type %d"), type);
  337. return 0;
  338. }
  339. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
  340. "'(%d, 1, %d, %d)'::topology.TopoGeometry "
  341. "WHERE (%s).id = %d",
  342. pg_info->schema_name, pg_info->table_name,
  343. pg_info->topogeom_column, pg_info->toposchema_id,
  344. topo_id, topogeom_type, pg_info->topogeom_column, fid);
  345. return 1;
  346. }
  347. /*!
  348. \brief Store map bounding box in DB head table
  349. \param pg_info pointer to Format_info_pg struct
  350. \param box pointer to bounding box
  351. \return 1 on success
  352. \return 0 on failure
  353. */
  354. int save_map_bbox(const struct Format_info_pg *pg_info, const struct bound_box *box)
  355. {
  356. char stmt[DB_SQL_MAX];
  357. /* create if not exists */
  358. if (create_topo_grass(pg_info) == -1) {
  359. G_warning(_("Unable to create <%s.%s>"), TOPO_SCHEMA, TOPO_TABLE);
  360. return 0;
  361. }
  362. /* update bbox */
  363. if (has_topo_grass(pg_info)) {
  364. /* -> update */
  365. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
  366. "'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d WHERE %s = %d",
  367. TOPO_SCHEMA, TOPO_TABLE, TOPO_BBOX,
  368. box->W, box->S, box->B, box->E, box->N, box->T,
  369. TOPO_ID, pg_info->toposchema_id);
  370. }
  371. else {
  372. /* -> insert */
  373. sprintf(stmt, "INSERT INTO \"%s\".\"%s\" (%s, %s) "
  374. "VALUES(%d, 'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d)",
  375. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX, pg_info->toposchema_id,
  376. box->W, box->S, box->B, box->E, box->N, box->T);
  377. }
  378. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  379. return -1;
  380. }
  381. return 1;
  382. }
  383. /*!
  384. \brief Creates 'topology.grass' table if not exists
  385. \return 0 table already exists
  386. \return 1 table successfully added
  387. \return -1 on error
  388. */
  389. int create_topo_grass(const struct Format_info_pg *pg_info)
  390. {
  391. char stmt[DB_SQL_MAX];
  392. PGresult *result;
  393. /* check if table exists */
  394. sprintf(stmt, "SELECT COUNT(*) FROM information_schema.tables "
  395. "WHERE table_schema = '%s' AND table_name = '%s'",
  396. TOPO_SCHEMA, TOPO_TABLE);
  397. result = PQexec(pg_info->conn, stmt);
  398. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  399. PQclear(result);
  400. return -1;
  401. }
  402. if (atoi(PQgetvalue(result, 0, 0)) == 1) {
  403. /* table already exists */
  404. PQclear(result);
  405. return 1;
  406. }
  407. PQclear(result);
  408. G_debug(1, "<%s.%s> created", TOPO_SCHEMA, TOPO_TABLE);
  409. /* create table */
  410. sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s INTEGER, %s box3d)",
  411. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX);
  412. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  413. return -1;
  414. }
  415. /* add primary key */
  416. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD PRIMARY KEY (%s)",
  417. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID);
  418. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  419. return -1;
  420. }
  421. /* add constraint */
  422. sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD CONSTRAINT \"%s_%s_fkey\" "
  423. "FOREIGN KEY (%s) REFERENCES topology.topology(id) ON DELETE CASCADE",
  424. TOPO_SCHEMA, TOPO_TABLE, TOPO_TABLE, TOPO_ID, TOPO_ID);
  425. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  426. return -1;
  427. }
  428. return 1;
  429. }
  430. /*!
  431. \brief Check if 'topology_id' exists in 'topology.grass'
  432. \param pg_info pointer to Format_info_pg struct
  433. \return TRUE if exists
  434. \return FALSE otherwise
  435. \return -1 on error
  436. */
  437. int has_topo_grass(const struct Format_info_pg *pg_info)
  438. {
  439. int has_topo;
  440. char stmt[DB_SQL_MAX];
  441. PGresult *result;
  442. sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".\"%s\" "
  443. "WHERE %s = %d",
  444. TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, pg_info->toposchema_id);
  445. result = PQexec(pg_info->conn, stmt);
  446. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  447. PQclear(result);
  448. return -1;
  449. }
  450. has_topo = FALSE;
  451. if (atoi(PQgetvalue(result, 0, 0)) == 1) {
  452. /* table already exists */
  453. has_topo = TRUE;
  454. }
  455. PQclear(result);
  456. return has_topo;
  457. }
  458. /*!
  459. \brief Insert node into 'node_grass' table
  460. Writes (see P_node struct):
  461. - lines
  462. - angles
  463. Already stored in Topo-Geo:
  464. - x,y,z (geom)
  465. \param plus pointer to Plus_head struct
  466. \param pg_info pointer to Format_info_pg struct
  467. \return 0 on success
  468. \return -1 on error
  469. */
  470. int write_nodes(const struct Plus_head *plus,
  471. const struct Format_info_pg *pg_info)
  472. {
  473. int i, node_id;
  474. size_t stmt_lines_size, stmt_angles_size, stmt_size;
  475. char *stmt_lines, *stmt_angles, *stmt;
  476. const struct P_node *Node;
  477. const struct Format_info_offset *offset;
  478. offset = &(pg_info->offset);
  479. if (offset->array_num < 1) /* nothing to write */
  480. return 0;
  481. if (plus->n_nodes != offset->array_num) {
  482. G_warning(_("Unable to write nodes, offset array mismatch"));
  483. return -1;
  484. }
  485. stmt_size = 2 * DB_SQL_MAX + 512;
  486. stmt = (char *) G_malloc(stmt_size);
  487. stmt_lines = stmt_angles = NULL;
  488. for (i = 1; i <= plus->n_nodes; i++) {
  489. Node = plus->Node[i];
  490. if (!Node)
  491. continue; /* should not happen */
  492. node_id = offset->array[i-1];
  493. /* 'lines' array */
  494. build_stmt_id(Node->lines, Node->n_lines, TRUE, plus, &stmt_lines, &stmt_lines_size);
  495. /* 'angle' array */
  496. build_stmt_id(Node->angles, Node->n_lines, FALSE, NULL, &stmt_angles, &stmt_angles_size);
  497. /* build SQL statement to add new node into 'node_grass' */
  498. if (stmt_lines_size + stmt_angles_size + 512 > stmt_size) {
  499. stmt_size = stmt_lines_size + stmt_angles_size + 512;
  500. stmt = (char *) G_realloc(stmt, stmt_size);
  501. }
  502. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  503. "%d, '{%s}', '{%s}')", pg_info->toposchema_name, TOPO_TABLE_NODE,
  504. node_id, stmt_lines, stmt_angles);
  505. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  506. G_warning(_("Unable to write nodes"));
  507. return -1;
  508. }
  509. }
  510. G_free(stmt_lines);
  511. G_free(stmt_angles);
  512. G_free(stmt);
  513. return 0;
  514. }
  515. /*!
  516. \brief Insert lines into 'line_grass' table
  517. Writes (see P_line struct) - only for boundaries:
  518. - left, right area
  519. Already stored in Topo-Geo:
  520. - edge_id, left_face, right_face
  521. \param plus pointer to Plus_head struct
  522. \param pg_info pointer to Format_info_pg struct
  523. \return 0 on success
  524. \return -1 on error
  525. */
  526. int write_lines(const struct Plus_head *plus,
  527. const struct Format_info_pg *pg_info)
  528. {
  529. int i, row, offset;
  530. char stmt[DB_SQL_MAX];
  531. const struct P_line *Line;
  532. const struct P_topo_b *topo;
  533. PGresult *res;
  534. sprintf(stmt, "SELECT edge_id FROM \"%s\".edge_data WHERE "
  535. "left_face != 0 OR right_face != 0 ORDER BY edge_id",
  536. pg_info->toposchema_name);
  537. G_debug(2, "SQL: %s", stmt);
  538. res = PQexec(pg_info->conn, stmt);
  539. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  540. (PQntuples(res) > 0 && PQntuples(res) != plus->n_blines)) {
  541. G_warning(_("Inconsistency in topology: number of "
  542. "boundaries %d (should be %d)"),
  543. PQntuples(res), plus->n_blines);
  544. if (res)
  545. PQclear(res);
  546. return -1;
  547. }
  548. for (row = 0, i = 1; i <= plus->n_lines; i++) {
  549. Line = plus->Line[i];
  550. if (!Line || Line->type != GV_BOUNDARY)
  551. continue;
  552. if (Line->offset == 0L)
  553. offset = atoi(PQgetvalue(res, row++, 0));
  554. else
  555. offset = (int)Line->offset;
  556. topo = (struct P_topo_b *)Line->topo;
  557. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  558. "%d, %d, %d)", pg_info->toposchema_name, TOPO_TABLE_LINE,
  559. offset, topo->left, topo->right);
  560. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  561. G_warning(_("Unable to write lines"));
  562. return -1;
  563. }
  564. }
  565. return 0;
  566. }
  567. /*!
  568. \brief Insert area into 'area_grass' table
  569. Writes (see P_area struct):
  570. - lines
  571. - centroid
  572. - isles
  573. \param plus pointer to Plus_head struct
  574. \param pg_info pointer to Format_info_pg struct
  575. \return 0 on success
  576. \return -1 on error
  577. */
  578. int write_areas(const struct Plus_head *plus,
  579. const struct Format_info_pg *pg_info)
  580. {
  581. int area, centroid;
  582. size_t stmt_lines_size, stmt_isles_size, stmt_size;
  583. char *stmt_lines, *stmt_isles, *stmt;
  584. const struct P_line *Line;
  585. const struct P_area *Area;
  586. stmt_size = 2 * DB_SQL_MAX + 512;
  587. stmt = (char *) G_malloc(stmt_size);
  588. stmt_lines = stmt_isles = NULL;
  589. for (area = 1; area <= plus->n_areas; area++) {
  590. Area = plus->Area[area];
  591. if (!Area) {
  592. G_debug(3, "Area %d skipped (dead)", area);
  593. continue; /* should not happen */
  594. }
  595. /* 'lines' array */
  596. build_stmt_id(Area->lines, Area->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
  597. /* 'isles' array */
  598. build_stmt_id(Area->isles, Area->n_isles, TRUE, NULL, &stmt_isles, &stmt_isles_size);
  599. if (Area->centroid != 0) {
  600. Line = plus->Line[Area->centroid];
  601. if (!Line) {
  602. G_warning(_("Topology for centroid %d not available. Area %d skipped"),
  603. Area->centroid, area);
  604. continue;
  605. }
  606. centroid = (int) Line->offset;
  607. }
  608. else {
  609. centroid = 0;
  610. }
  611. /* build SQL statement to add new node into 'node_grass' */
  612. if (stmt_lines_size + stmt_isles_size + 512 > stmt_size) {
  613. stmt_size = stmt_lines_size + stmt_isles_size + 512;
  614. stmt = (char *) G_realloc(stmt, stmt_size);
  615. }
  616. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  617. "%d, '{%s}', %d, '{%s}')", pg_info->toposchema_name, TOPO_TABLE_AREA,
  618. area, stmt_lines, centroid, stmt_isles);
  619. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  620. return -1;
  621. }
  622. }
  623. G_free(stmt_lines);
  624. G_free(stmt_isles);
  625. G_free(stmt);
  626. return 0;
  627. }
  628. /*!
  629. \brief Insert isle into 'isle_grass' table
  630. Writes (see P_isle struct):
  631. - lines
  632. - area
  633. \param plus pointer to Plus_head struct
  634. \param pg_info pointer to Format_info_pg struct
  635. \return 0 on success
  636. \return -1 on error
  637. */
  638. int write_isles(const struct Plus_head *plus,
  639. const struct Format_info_pg *pg_info)
  640. {
  641. int isle;
  642. size_t stmt_lines_size, stmt_size;
  643. char *stmt_lines, *stmt;
  644. const struct P_isle *Isle;
  645. stmt_size = DB_SQL_MAX + 512;
  646. stmt = (char *) G_malloc(stmt_size);
  647. stmt_lines = NULL;
  648. for (isle = 1; isle <= plus->n_isles; isle++) {
  649. Isle = plus->Isle[isle];
  650. if (!Isle)
  651. continue; /* should not happen */
  652. /* 'lines' array */
  653. build_stmt_id(Isle->lines, Isle->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
  654. /* build SQL statement to add new node into 'node_grass' */
  655. if (stmt_lines_size + 512 > stmt_size) {
  656. stmt_size = stmt_lines_size + 512;
  657. stmt = (char *) G_realloc(stmt, stmt_size);
  658. }
  659. sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
  660. "%d, '{%s}', %d)", pg_info->toposchema_name, TOPO_TABLE_ISLE,
  661. isle, stmt_lines, Isle->area);
  662. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  663. return -1;
  664. }
  665. }
  666. G_free(stmt_lines);
  667. G_free(stmt);
  668. return 0;
  669. }
  670. /*!
  671. \brief Create PG-like array for int/float array
  672. \param array array of items
  673. \param nitems number of items in the array
  674. \param is_int TRUE for array of integers otherwise floats
  675. \param plus pointer to Plus_head struct
  676. \param[in,out] output buffer (re-used)
  677. \param[in,out] buffer size
  678. */
  679. void build_stmt_id(const void *array, int nitems, int is_int, const struct Plus_head *plus,
  680. char **stmt, size_t *stmt_size)
  681. {
  682. int i, ivalue;
  683. int *iarray;
  684. float *farray;
  685. size_t stmt_id_size;
  686. char *stmt_id, buf_id[128];
  687. struct P_line *Line;
  688. if (is_int)
  689. iarray = (int *) array;
  690. else
  691. farray = (float *) array;
  692. if (!(*stmt)) {
  693. stmt_id_size = DB_SQL_MAX;
  694. stmt_id = (char *) G_malloc(stmt_id_size);
  695. }
  696. else {
  697. stmt_id_size = *stmt_size;
  698. stmt_id = *stmt;
  699. }
  700. /* reset array */
  701. stmt_id[0] = '\0';
  702. for (i = 0; i < nitems; i++) {
  703. /* realloc array if needed */
  704. if (strlen(stmt_id) + 100 > stmt_id_size) {
  705. stmt_id_size = strlen(stmt_id) + DB_SQL_MAX;
  706. stmt_id = (char *) G_realloc(stmt_id, stmt_id_size);
  707. }
  708. if (is_int) {
  709. if (plus) {
  710. Line = plus->Line[abs(iarray[i])];
  711. ivalue = (int) Line->offset;
  712. if (iarray[i] < 0)
  713. ivalue *= -1;
  714. }
  715. else {
  716. ivalue = iarray[i];
  717. }
  718. sprintf(buf_id, "%d", ivalue);
  719. }
  720. else {
  721. sprintf(buf_id, "%f", farray[i]);
  722. }
  723. if (i > 0)
  724. strcat(stmt_id, ",");
  725. strcat(stmt_id, buf_id);
  726. }
  727. *stmt = stmt_id;
  728. *stmt_size = stmt_id_size;
  729. }
  730. #endif