build_pg.c 23 KB

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