write_pg.c 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346
  1. /*!
  2. \file lib/vector/Vlib/write_pg.c
  3. \brief Vector library - write vector feature (PostGIS format)
  4. Higher level functions for reading/writing/manipulating vectors.
  5. Inspired by OGR PostgreSQL driver.
  6. \todo PostGIS version of V2__add_line_to_topo_nat()
  7. \todo OGR version of V2__delete_area_cats_from_cidx_nat()
  8. \todo function to delete corresponding entry in fidx
  9. \todo OGR version of V2__add_area_cats_to_cidx_nat
  10. \todo OGR version of V2__add_line_to_topo_nat
  11. (C) 2012 by Martin Landa, and the GRASS Development Team
  12. This program is free software under the GNU General Public License
  13. (>=v2). Read the file COPYING that comes with GRASS for details.
  14. \author Martin Landa <landa.martin gmail.com>
  15. */
  16. #include <string.h>
  17. #include <grass/vector.h>
  18. #include <grass/glocale.h>
  19. #include "local_proto.h"
  20. #ifdef HAVE_POSTGRES
  21. #include "pg_local_proto.h"
  22. #define WKBSRIDFLAG 0x20000000
  23. /*! Use SQL statements from PostGIS Topology extension (this options
  24. is quite slow. By default are used simple SQL statements (INSERT, UPDATE)
  25. */
  26. #define USE_TOPO_STMT 0
  27. static off_t write_line_sf(struct Map_info *, int,
  28. const struct line_pnts **, int,
  29. const struct line_cats *);
  30. static off_t write_line_tp(struct Map_info *, int, int,
  31. const struct line_pnts *,
  32. const struct line_cats *);
  33. static char *binary_to_hex(int, const unsigned char *);
  34. static unsigned char *point_to_wkb(int, const struct line_pnts *, int, int *);
  35. static unsigned char *linestring_to_wkb(int, const struct line_pnts *,
  36. int, int *);
  37. static unsigned char *polygon_to_wkb(int, const struct line_pnts **, int,
  38. int, int *);
  39. static int write_feature(struct Map_info *,
  40. int, const struct line_pnts **, int, int,
  41. const struct P_line *,
  42. int, const struct field_info *);
  43. static char *build_insert_stmt(const struct Format_info_pg *, const char *,
  44. int, int, const struct field_info *);
  45. static char *build_topo_stmt(struct Map_info *, int,
  46. const struct P_line *, const char *);
  47. static int execute_topo(PGconn *, const char *);
  48. static int update_next_line(struct Map_info*, int, int, int*, int *);
  49. #endif
  50. /*!
  51. \brief Writes feature on level 1 (PostGIS interface)
  52. Note:
  53. - centroids are not supported in PostGIS, pseudotopo holds virtual
  54. centroids
  55. - boundaries are not supported in PostGIS, pseudotopo treats polygons
  56. as boundaries
  57. \param Map pointer to Map_info structure
  58. \param type feature type (GV_POINT, GV_LINE, ...)
  59. \param points pointer to line_pnts structure (feature geometry)
  60. \param cats pointer to line_cats structure (feature categories)
  61. \return feature offset into file
  62. \return -1 on error
  63. */
  64. off_t V1_write_line_pg(struct Map_info *Map, int type,
  65. const struct line_pnts *points,
  66. const struct line_cats *cats)
  67. {
  68. #ifdef HAVE_POSTGRES
  69. struct Format_info_pg *pg_info;
  70. pg_info = &(Map->fInfo.pg);
  71. if (pg_info->feature_type == SF_UNKNOWN) {
  72. /* create PostGIS table if doesn't exist */
  73. if (V2_open_new_pg(Map, type) < 0)
  74. return -1;
  75. }
  76. if (pg_info->toposchema_name) {
  77. G_warning(_("PostGIS topology not supported on level 1"));
  78. return -1;
  79. }
  80. return write_line_sf(Map, type, &points, 1, cats);
  81. #else
  82. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  83. return -1;
  84. #endif
  85. }
  86. /*!
  87. \brief Writes feature on level 2 (PostGIS interface)
  88. \param Map pointer to Map_info structure
  89. \param type feature type (GV_POINT, GV_LINE, ...)
  90. \param points pointer to line_pnts structure (feature geometry)
  91. \param cats pointer to line_cats structure (feature categories)
  92. \return feature offset into file
  93. \return -1 on error
  94. */
  95. off_t V2_write_line_pg(struct Map_info *Map, int type,
  96. const struct line_pnts *points,
  97. const struct line_cats *cats)
  98. {
  99. #ifdef HAVE_POSTGRES
  100. struct Format_info_pg *pg_info;
  101. pg_info = &(Map->fInfo.pg);
  102. if (!pg_info->toposchema_name) { /* pseudo-topology */
  103. return V2_write_line_sfa(Map, type, points, cats);
  104. }
  105. else { /* PostGIS topology */
  106. if (Map->plus.built < GV_BUILD_BASE)
  107. Map->plus.built = GV_BUILD_BASE;
  108. return write_line_tp(Map, type, FALSE, points, cats);
  109. }
  110. #else
  111. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  112. return -1;
  113. #endif
  114. }
  115. /*!
  116. \brief Rewrites feature at the given offset (level 1) (PostGIS interface)
  117. \param Map pointer to Map_info structure
  118. \param offset feature offset
  119. \param type feature type (GV_POINT, GV_LINE, ...)
  120. \param points feature geometry
  121. \param cats feature categories
  122. \return feature offset (rewriten feature)
  123. \return -1 on error
  124. */
  125. off_t V1_rewrite_line_pg(struct Map_info * Map,
  126. int line, int type, off_t offset,
  127. const struct line_pnts * points,
  128. const struct line_cats * cats)
  129. {
  130. G_debug(3, "V1_rewrite_line_pg(): line=%d type=%d offset=%lu",
  131. line, type, offset);
  132. #ifdef HAVE_POSTGRES
  133. if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
  134. G_warning(_("Unable to rewrite feature (incompatible feature types)"));
  135. return -1;
  136. }
  137. /* delete old */
  138. V1_delete_line_pg(Map, offset);
  139. return V1_write_line_pg(Map, type, points, cats);
  140. #else
  141. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  142. return -1;
  143. #endif
  144. }
  145. /*!
  146. \brief Deletes feature at the given offset (level 1)
  147. \param Map pointer Map_info structure
  148. \param offset feature offset
  149. \return 0 on success
  150. \return -1 on error
  151. */
  152. int V1_delete_line_pg(struct Map_info *Map, off_t offset)
  153. {
  154. #ifdef HAVE_POSTGRES
  155. long fid;
  156. char stmt[DB_SQL_MAX];
  157. struct Format_info_pg *pg_info;
  158. pg_info = &(Map->fInfo.pg);
  159. if (!pg_info->conn || !pg_info->table_name) {
  160. G_warning(_("No connection defined"));
  161. return -1;
  162. }
  163. if (offset >= pg_info->offset.array_num) {
  164. G_warning(_("Invalid offset (%d)"), offset);
  165. return -1;
  166. }
  167. fid = pg_info->offset.array[offset];
  168. G_debug(3, "V1_delete_line_pg(), offset = %lu -> fid = %ld",
  169. (unsigned long)offset, fid);
  170. if (!pg_info->inTransaction) {
  171. /* start transaction */
  172. pg_info->inTransaction = TRUE;
  173. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
  174. return -1;
  175. }
  176. sprintf(stmt, "DELETE FROM %s WHERE %s = %ld",
  177. pg_info->table_name, pg_info->fid_column, fid);
  178. G_debug(2, "SQL: %s", stmt);
  179. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  180. G_warning(_("Unable to delete feature"));
  181. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  182. return -1;
  183. }
  184. return 0;
  185. #else
  186. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  187. return -1;
  188. #endif
  189. }
  190. /*!
  191. \brief Writes node on level 2 (PostGIS Topology interface) - internal use only
  192. \param Map pointer to Map_info structure
  193. \param points pointer to line_pnts structure
  194. \return 0 on success
  195. \return -1 on error
  196. */
  197. off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
  198. {
  199. #ifdef HAVE_POSTGRES
  200. struct Format_info_pg *pg_info;
  201. pg_info = &(Map->fInfo.pg);
  202. if (!pg_info->toposchema_name)
  203. return -1; /* PostGIS Topology required */
  204. return write_line_tp(Map, GV_POINT, TRUE, points, NULL);
  205. #else
  206. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  207. return -1;
  208. #endif
  209. }
  210. /*!
  211. \brief Writes area on level 2 (PostGIS Simple Features interface) - internal use only
  212. \param Map pointer to Map_info structure
  213. \param type feature type (GV_POINT, GV_LINE, ...)
  214. \param points pointer to line_pnts structure (boundary geometry)
  215. \param cats pointer to line_cats structure (feature categories)
  216. \param ipoints pointer to line_pnts structure (isles geometry)
  217. \param nisles number of isles
  218. \return feature offset into file
  219. \return -1 on error
  220. */
  221. off_t V2__write_area_pg(struct Map_info *Map,
  222. const struct line_pnts *bpoints,
  223. const struct line_cats *cats,
  224. const struct line_pnts **ipoints, int nisles)
  225. {
  226. #ifdef HAVE_POSTGRES
  227. int i;
  228. off_t ret;
  229. const struct line_pnts **points;
  230. if (nisles > 0) {
  231. points = (const struct line_pnts **) G_calloc(nisles + 1, sizeof(struct line_pnts *));
  232. points[0] = bpoints;
  233. for (i = 0; i < nisles; i++)
  234. points[i + 1] = ipoints[i];
  235. }
  236. else {
  237. points = &bpoints;
  238. }
  239. ret = write_line_sf(Map, GV_BOUNDARY, points, nisles + 1, cats);
  240. if (nisles > 0)
  241. G_free(points);
  242. return ret;
  243. #else
  244. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  245. return -1;
  246. #endif
  247. }
  248. #ifdef HAVE_POSTGRES
  249. /*!
  250. \brief Write vector features as PostGIS simple feature element
  251. \return 0 on success
  252. \return -1 on error
  253. */
  254. off_t write_line_sf(struct Map_info *Map, int type,
  255. const struct line_pnts **points, int nparts,
  256. const struct line_cats *cats)
  257. {
  258. int cat;
  259. off_t offset;
  260. SF_FeatureType sf_type;
  261. struct field_info *Fi;
  262. struct Format_info_pg *pg_info;
  263. struct Format_info_offset *offset_info;
  264. pg_info = &(Map->fInfo.pg);
  265. offset_info = &(pg_info->offset);
  266. /* check required PG settings */
  267. if (!pg_info->conn) {
  268. G_warning(_("No connection defined"));
  269. return -1;
  270. }
  271. if (!pg_info->table_name) {
  272. G_warning(_("PostGIS feature table not defined"));
  273. return -1;
  274. }
  275. /* create PostGIS table if doesn't exist */
  276. if (pg_info->feature_type == SF_UNKNOWN) {
  277. if (V2_open_new_pg(Map, type) < 0)
  278. return -1;
  279. }
  280. Fi = NULL; /* no attributes to be written */
  281. cat = -1;
  282. if (cats->n_cats > 0 && Vect_get_num_dblinks(Map) > 0) {
  283. /* check for attributes */
  284. Fi = Vect_get_dblink(Map, 0);
  285. if (Fi) {
  286. if (!Vect_cat_get(cats, Fi->number, &cat))
  287. G_warning(_("No category defined for layer %d"), Fi->number);
  288. if (cats->n_cats > 1) {
  289. G_warning(_("Feature has more categories, using "
  290. "category %d (from layer %d)"),
  291. cat, cats->field[0]);
  292. }
  293. }
  294. }
  295. sf_type = pg_info->feature_type;
  296. /* determine matching PostGIS feature geometry type */
  297. if (type & (GV_POINT | GV_KERNEL)) {
  298. if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
  299. G_warning(_("Feature is not a point. Skipping."));
  300. return -1;
  301. }
  302. }
  303. else if (type & GV_LINE) {
  304. if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
  305. G_warning(_("Feature is not a line. Skipping."));
  306. return -1;
  307. }
  308. }
  309. else if (type & GV_BOUNDARY || type & GV_CENTROID) {
  310. if (sf_type != SF_POLYGON) {
  311. G_warning(_("Feature is not a polygon. Skipping."));
  312. return -1;
  313. }
  314. }
  315. else if (type & GV_FACE) {
  316. if (sf_type != SF_POLYGON25D) {
  317. G_warning(_("Feature is not a face. Skipping."));
  318. return -1;
  319. }
  320. }
  321. else {
  322. G_warning(_("Unsupported feature type (%d)"), type);
  323. return -1;
  324. }
  325. G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d",
  326. type, points[0]->n_points, cat);
  327. if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
  328. /* skip this check when writing PostGIS topology */
  329. int part, npoints;
  330. for (part = 0; part < nparts; part++) {
  331. npoints = points[part]->n_points - 1;
  332. if (points[part]->x[0] != points[part]->x[npoints] ||
  333. points[part]->y[0] != points[part]->y[npoints] ||
  334. points[part]->z[0] != points[part]->z[npoints]) {
  335. G_warning(_("Boundary is not closed. Skipping."));
  336. return -1;
  337. }
  338. }
  339. }
  340. /* write feature's geometry and fid */
  341. if (-1 == write_feature(Map, type, points, nparts,
  342. Vect_is_3d(Map) ? WITH_Z : WITHOUT_Z, NULL, cat, Fi)) {
  343. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  344. return -1;
  345. }
  346. /* update offset array */
  347. if (offset_info->array_num >= offset_info->array_alloc) {
  348. offset_info->array_alloc += 1000;
  349. offset_info->array = (int *)G_realloc(offset_info->array,
  350. offset_info->array_alloc *
  351. sizeof(int));
  352. }
  353. offset = offset_info->array_num;
  354. offset_info->array[offset_info->array_num++] = cat;
  355. if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
  356. /* register first part in offset array */
  357. offset_info->array[offset_info->array_num++] = 0;
  358. }
  359. G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
  360. (unsigned long)offset, offset_info->array_num, cat);
  361. return offset;
  362. }
  363. /*!
  364. \brief Write vector feature in PostGIS topology schema and
  365. updates internal topology structures
  366. \param Map vector map
  367. \param type feature type to be written
  368. \param points feature geometry
  369. \param is_node TRUE for nodes (written as points)
  370. \return 0 on success
  371. \return -1 on error
  372. */
  373. off_t write_line_tp(struct Map_info *Map, int type, int is_node,
  374. const struct line_pnts *points,
  375. const struct line_cats *cats)
  376. {
  377. int cat;
  378. struct field_info *Fi;
  379. struct Format_info_pg *pg_info;
  380. struct Plus_head *plus;
  381. struct P_line *Line;
  382. pg_info = &(Map->fInfo.pg);
  383. plus = &(Map->plus);
  384. /* check type for nodes */
  385. if (is_node && type != GV_POINT) {
  386. G_warning(_("Invalid type (%d) for nodes"), type);
  387. return -1;
  388. }
  389. /* check required PG settings */
  390. if (!pg_info->conn) {
  391. G_warning(_("No connection defined"));
  392. return -1;
  393. }
  394. if (!pg_info->table_name) {
  395. G_warning(_("PostGIS feature table not defined"));
  396. return -1;
  397. }
  398. if (!pg_info->toposchema_name) {
  399. G_warning(_("PostGIS topology schema not defined"));
  400. return -1;
  401. }
  402. /* create PostGIS table if doesn't exist */
  403. if (pg_info->feature_type == SF_UNKNOWN) {
  404. if (V2_open_new_pg(Map, type) < 0)
  405. return -1;
  406. }
  407. G_debug(3, "write_line_pg(): type = %d n_points = %d",
  408. type, points->n_points);
  409. Fi = NULL; /* no attributes to be written */
  410. cat = -1;
  411. if (cats && cats->n_cats > 0) {
  412. if (Vect_get_num_dblinks(Map) > 0) {
  413. /* check for attributes */
  414. Fi = Vect_get_dblink(Map, 0);
  415. if (Fi) {
  416. if (!Vect_cat_get(cats, Fi->number, &cat))
  417. G_warning(_("No category defined for layer %d"), Fi->number);
  418. if (cats->n_cats > 1) {
  419. G_warning(_("Feature has more categories, using "
  420. "category %d (from layer %d)"),
  421. cat, cats->field[0]);
  422. }
  423. }
  424. }
  425. /* assume layer=1 */
  426. Vect_cat_get(cats, 1, &cat);
  427. }
  428. /* update topology before writing feature */
  429. Line = NULL;
  430. if (is_node) {
  431. dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
  432. }
  433. else {
  434. int line;
  435. struct bound_box box;
  436. dig_line_box(points, &box);
  437. line = dig_add_line(plus, type, points, &box, 0); /* offset ? */
  438. G_debug(3, " line added to topo with id = %d", line);
  439. if (line == 1)
  440. Vect_box_copy(&(plus->box), &box);
  441. else
  442. Vect_box_extend(&(plus->box), &box);
  443. V2__add_line_to_topo_nat(Map, line, points, NULL); /* cats ? */
  444. Line = plus->Line[line];
  445. }
  446. /* write feature geometry and fid */
  447. if (-1 == write_feature(Map, type, &points, 1,
  448. Vect_is_3d(Map) ? WITH_Z : WITHOUT_Z, Line,
  449. cat, Fi)) {
  450. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  451. return -1;
  452. }
  453. return 0;
  454. }
  455. /*!
  456. \brief Binary data to HEX
  457. Allocated buffer should be freed by G_free().
  458. \param nbytes number of bytes to allocate
  459. \param wkb_data WKB data
  460. \return allocated buffer with HEX data
  461. */
  462. char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
  463. {
  464. char *hex_data;
  465. int i, nlow, nhigh;
  466. static const char ach_hex[] = "0123456789ABCDEF";
  467. hex_data = (char *)G_malloc(nbytes * 2 + 1);
  468. hex_data[nbytes * 2] = '\0';
  469. for (i = 0; i < nbytes; i++) {
  470. nlow = wkb_data[i] & 0x0f;
  471. nhigh = (wkb_data[i] & 0xf0) >> 4;
  472. hex_data[i * 2] = ach_hex[nhigh];
  473. hex_data[i * 2 + 1] = ach_hex[nlow];
  474. }
  475. return hex_data;
  476. }
  477. /*!
  478. \bried Write point into WKB buffer
  479. See OGRPoint::exportToWkb from GDAL/OGR library
  480. \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
  481. \param points feature geometry
  482. \param with_z WITH_Z for 3D data
  483. \param[out] nsize buffer size
  484. \return allocated WKB buffer
  485. \return NULL on error
  486. */
  487. unsigned char *point_to_wkb(int byte_order,
  488. const struct line_pnts *points, int with_z,
  489. int *nsize)
  490. {
  491. unsigned char *wkb_data;
  492. unsigned int sf_type;
  493. if (points->n_points != 1)
  494. return NULL;
  495. /* allocate buffer */
  496. *nsize = with_z ? 29 : 21;
  497. wkb_data = G_malloc(*nsize);
  498. G_zero(wkb_data, *nsize);
  499. G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
  500. /* set the byte order */
  501. if (byte_order == ENDIAN_LITTLE)
  502. wkb_data[0] = '\001';
  503. else
  504. wkb_data[0] = '\000';
  505. /* set the geometry feature type */
  506. sf_type = with_z ? SF_POINT25D : SF_POINT;
  507. if (byte_order == ENDIAN_LITTLE)
  508. sf_type = LSBWORD32(sf_type);
  509. else
  510. sf_type = MSBWORD32(sf_type);
  511. memcpy(wkb_data + 1, &sf_type, 4);
  512. /* copy in the raw data */
  513. memcpy(wkb_data + 5, &(points->x[0]), 8);
  514. memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
  515. if (with_z) {
  516. memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
  517. }
  518. /* swap if needed */
  519. if (byte_order == ENDIAN_BIG) {
  520. SWAPDOUBLE(wkb_data + 5);
  521. SWAPDOUBLE(wkb_data + 5 + 8);
  522. if (with_z)
  523. SWAPDOUBLE(wkb_data + 5 + 16);
  524. }
  525. return wkb_data;
  526. }
  527. /*!
  528. \bried Write linestring into WKB buffer
  529. See OGRLineString::exportToWkb from GDAL/OGR library
  530. \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
  531. \param points feature geometry
  532. \param with_z WITH_Z for 3D data
  533. \param[out] nsize buffer size
  534. \return allocated WKB buffer
  535. \return NULL on error
  536. */
  537. unsigned char *linestring_to_wkb(int byte_order,
  538. const struct line_pnts *points, int with_z,
  539. int *nsize)
  540. {
  541. int i, point_size;
  542. unsigned char *wkb_data;
  543. unsigned int sf_type;
  544. if (points->n_points < 1)
  545. return NULL;
  546. /* allocate buffer */
  547. point_size = 8 * (with_z ? 3 : 2);
  548. *nsize = 5 + 4 + points->n_points * point_size;
  549. wkb_data = G_malloc(*nsize);
  550. G_zero(wkb_data, *nsize);
  551. G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
  552. /* set the byte order */
  553. if (byte_order == ENDIAN_LITTLE)
  554. wkb_data[0] = '\001';
  555. else
  556. wkb_data[0] = '\000';
  557. /* set the geometry feature type */
  558. sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
  559. if (byte_order == ENDIAN_LITTLE)
  560. sf_type = LSBWORD32(sf_type);
  561. else
  562. sf_type = MSBWORD32(sf_type);
  563. memcpy(wkb_data + 1, &sf_type, 4);
  564. /* copy in the data count */
  565. memcpy(wkb_data + 5, &(points->n_points), 4);
  566. /* copy in the raw data */
  567. for (i = 0; i < points->n_points; i++) {
  568. memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
  569. memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
  570. if (with_z) {
  571. memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
  572. }
  573. }
  574. /* swap if needed */
  575. if (byte_order == ENDIAN_BIG) {
  576. int npoints, nitems;
  577. npoints = SWAP32(points->n_points);
  578. memcpy(wkb_data + 5, &npoints, 4);
  579. nitems = (with_z ? 3 : 2) * points->n_points;
  580. for (i = 0; i < nitems; i++) {
  581. SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
  582. }
  583. }
  584. return wkb_data;
  585. }
  586. /*!
  587. \bried Write polygon into WKB buffer
  588. See OGRPolygon::exportToWkb from GDAL/OGR library
  589. \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
  590. \param ipoints list of ring geometries (first is outer ring)
  591. \param nrings number of rings
  592. \param with_z WITH_Z for 3D data
  593. \param[out] nsize buffer size
  594. \return allocated WKB buffer
  595. \return NULL on error
  596. */
  597. unsigned char *polygon_to_wkb(int byte_order,
  598. const struct line_pnts** points, int nrings,
  599. int with_z, int *nsize)
  600. {
  601. int i, ring, point_size, offset;
  602. unsigned char *wkb_data;
  603. unsigned int sf_type;
  604. /* check data validity */
  605. if (nrings < 1)
  606. return NULL;
  607. for (ring = 0; ring < nrings; ring++) {
  608. if (points[ring]->n_points < 3)
  609. return NULL;
  610. }
  611. /* allocate buffer */
  612. point_size = 8 * (with_z ? 3 : 2);
  613. *nsize = 9;
  614. for (ring = 0; ring < nrings; ring++)
  615. *nsize += 4 + point_size * points[ring]->n_points;
  616. wkb_data = G_malloc(*nsize);
  617. G_zero(wkb_data, *nsize);
  618. G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
  619. /* set the byte order */
  620. if (byte_order == ENDIAN_LITTLE)
  621. wkb_data[0] = '\001';
  622. else
  623. wkb_data[0] = '\000';
  624. /* set the geometry feature type */
  625. sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
  626. if (byte_order == ENDIAN_LITTLE)
  627. sf_type = LSBWORD32(sf_type);
  628. else
  629. sf_type = MSBWORD32(sf_type);
  630. memcpy(wkb_data + 1, &sf_type, 4);
  631. /* copy in the raw data */
  632. if (byte_order == ENDIAN_BIG) {
  633. int ncount;
  634. ncount = SWAP32(nrings);
  635. memcpy(wkb_data + 5, &ncount, 4);
  636. }
  637. else {
  638. memcpy(wkb_data + 5, &nrings, 4);
  639. }
  640. /* serialize rings */
  641. offset = 9;
  642. for (ring = 0; ring < nrings; ring++) {
  643. memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
  644. for (i = 0; i < points[ring]->n_points; i++) {
  645. memcpy(wkb_data + offset +
  646. 4 + point_size * i, &(points[ring]->x[i]), 8);
  647. memcpy(wkb_data + offset +
  648. 4 + 8 + point_size * i, &(points[ring]->y[i]), 8);
  649. if (with_z) {
  650. memcpy(wkb_data + offset +
  651. 4 + 16 + point_size * i, &(points[ring]->z[i]), 8);
  652. }
  653. }
  654. offset += 4 + point_size * points[ring]->n_points;
  655. /* swap if needed */
  656. if (byte_order == ENDIAN_BIG) {
  657. int npoints, nitems;
  658. npoints = SWAP32(points[ring]->n_points);
  659. memcpy(wkb_data + 5, &npoints, 4);
  660. nitems = (with_z ? 3 : 2) * points[ring]->n_points;
  661. for (i = 0; i < nitems; i++) {
  662. SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
  663. }
  664. }
  665. }
  666. return wkb_data;
  667. }
  668. /*!
  669. \brief Insert feature into table
  670. \param Map pointer to Map_info structure
  671. \param type feature type (GV_POINT, GV_LINE, ...)
  672. \param points pointer to line_pnts struct
  673. \param nparts number of parts (rings for polygon)
  674. \param with_z WITH_Z for 3D data
  675. \param cat category number (-1 for no category)
  676. \param Fi pointer to field_info (attributes to copy, NULL for no attributes)
  677. \return -1 on error
  678. \retirn 0 on success
  679. */
  680. int write_feature(struct Map_info *Map, int type,
  681. const struct line_pnts **points, int nparts, int with_z,
  682. const struct P_line *Line,
  683. int cat, const struct field_info *Fi)
  684. {
  685. int fid;
  686. int byte_order, nbytes, nsize;
  687. unsigned int sf_type;
  688. unsigned char *wkb_data;
  689. char *stmt, *text_data, *text_data_p, *hex_data;
  690. struct Format_info_pg *pg_info;
  691. pg_info = &(Map->fInfo.pg);
  692. if (with_z && pg_info->coor_dim != 3) {
  693. G_warning(_("Trying to insert 3D data into feature table "
  694. "which store 2D data only"));
  695. return -1;
  696. }
  697. if (!with_z && pg_info->coor_dim != 2) {
  698. G_warning(_("Trying to insert 2D data into feature table "
  699. "which store 3D data only"));
  700. return -1;
  701. }
  702. byte_order = dig__byte_order_out();
  703. /* get wkb data */
  704. nbytes = -1;
  705. wkb_data = NULL;
  706. if (type == GV_POINT || type == GV_CENTROID)
  707. wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
  708. else if (type == GV_LINE)
  709. wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
  710. else if (type == GV_BOUNDARY) {
  711. if (!pg_info->toposchema_name) {
  712. /* PostGIS simple feature access */
  713. wkb_data = polygon_to_wkb(byte_order, points, nparts,
  714. with_z, &nbytes);
  715. }
  716. else {
  717. /* PostGIS topology access */
  718. wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
  719. }
  720. }
  721. if (!wkb_data || nbytes < 1) {
  722. G_warning(_("Unsupported feature type %d"), type);
  723. return -1;
  724. }
  725. /* When converting to hex, each byte takes 2 hex characters. In
  726. addition we add in 8 characters to represent the SRID integer
  727. in hex, and one for a null terminator */
  728. nsize = nbytes * 2 + 8 + 1;
  729. text_data = text_data_p = (char *)G_malloc(nsize);
  730. /* convert the 1st byte, which is the endianess flag, to hex */
  731. hex_data = binary_to_hex(1, wkb_data);
  732. strcpy(text_data_p, hex_data);
  733. G_free(hex_data);
  734. text_data_p += 2;
  735. /* get the geom type which is bytes 2 through 5 */
  736. memcpy(&sf_type, wkb_data + 1, 4);
  737. /* add the SRID flag if an SRID is provided */
  738. if (pg_info->srid > 0) {
  739. unsigned int srs_flag;
  740. /* change the flag to little endianess */
  741. srs_flag = LSBWORD32(WKBSRIDFLAG);
  742. /* apply the flag */
  743. sf_type = sf_type | srs_flag;
  744. }
  745. /* write the geom type which is 4 bytes */
  746. hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
  747. strcpy(text_data_p, hex_data);
  748. G_free(hex_data);
  749. text_data_p += 8;
  750. /* include SRID if provided */
  751. if (pg_info->srid > 0) {
  752. unsigned int srs_id;
  753. /* force the srsid to little endianess */
  754. srs_id = LSBWORD32(pg_info->srid);
  755. hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
  756. strcpy(text_data_p, hex_data);
  757. G_free(hex_data);
  758. text_data_p += 8;
  759. }
  760. /* copy the rest of the data over - subtract 5 since we already
  761. copied 5 bytes above */
  762. hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
  763. strcpy(text_data_p, hex_data);
  764. G_free(hex_data);
  765. /* build INSERT statement
  766. simple feature geometry + attributes
  767. */
  768. fid = -1;
  769. if (pg_info->toposchema_name) {
  770. /* use defined fid (=line) for topological access */
  771. fid = Vect_get_num_primitives(Map, type); /* write next fid */
  772. }
  773. stmt = build_insert_stmt(pg_info, text_data, fid, cat, Fi);
  774. G_debug(2, "SQL: %s", stmt);
  775. if (!pg_info->inTransaction) {
  776. /* start transaction */
  777. pg_info->inTransaction = TRUE;
  778. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
  779. return -1;
  780. }
  781. /* stmt can NULL when writing PostGIS topology with no attributes
  782. * attached */
  783. if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
  784. /* rollback transaction */
  785. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  786. return -1;
  787. }
  788. G_free(stmt);
  789. /* write feature in PostGIS topology schema if enabled */
  790. if (pg_info->toposchema_name) {
  791. /* insert feature into topology schema (node or edge) */
  792. stmt = build_topo_stmt(Map, type, Line, text_data);
  793. #if USE_TOPO_STMT
  794. if (stmt && execute_topo(pg_info->conn, stmt) == -1) {
  795. #else
  796. if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
  797. #endif
  798. /* rollback transaction */
  799. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  800. return -1;
  801. }
  802. G_free(stmt);
  803. }
  804. G_free(wkb_data);
  805. G_free(text_data);
  806. return 0;
  807. }
  808. /*!
  809. \brief Build INSERT statement to add new feature to the feature
  810. table
  811. Note: Allocated string should be freed.
  812. \param pg_info pointer to Format_info_pg structure
  813. \param geom_data geometry data
  814. \param fid feature id (=line)
  815. \param cat category number (or -1 for no category)
  816. \param Fi pointer to field_info structure (NULL for no attributes)
  817. \return allocated string with INSERT statement
  818. */
  819. char *build_insert_stmt(const struct Format_info_pg *pg_info,
  820. const char *geom_data, int fid,
  821. int cat, const struct field_info *Fi)
  822. {
  823. char *stmt, buf[DB_SQL_MAX];
  824. stmt = NULL;
  825. if (Fi && cat > -1) { /* write attributes (simple features and topology elements) */
  826. int col, ncol, more;
  827. int sqltype, ctype;
  828. char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
  829. char *str_val;
  830. const char *colname;
  831. dbString dbstmt;
  832. dbCursor cursor;
  833. dbTable *table;
  834. dbColumn *column;
  835. dbValue *value;
  836. db_init_string(&dbstmt);
  837. buf_val[0] = '\0';
  838. /* read & set attributes */
  839. sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
  840. cat);
  841. G_debug(4, "SQL: %s", buf);
  842. db_set_string(&dbstmt, buf);
  843. /* prepare INSERT statement */
  844. sprintf(buf, "INSERT INTO \"%s\".\"%s\" (",
  845. pg_info->schema_name, pg_info->table_name);
  846. /* select data */
  847. if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
  848. &cursor, DB_SEQUENTIAL) != DB_OK) {
  849. G_warning(_("Unable to select attributes for category %d"), cat);
  850. }
  851. else {
  852. if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
  853. G_warning(_("Unable to fetch data from table <%s>"),
  854. Fi->table);
  855. }
  856. if (!more) {
  857. G_warning(_("No database record for category %d, "
  858. "no attributes will be written"), cat);
  859. }
  860. else {
  861. table = db_get_cursor_table(&cursor);
  862. ncol = db_get_table_number_of_columns(table);
  863. for (col = 0; col < ncol; col++) {
  864. column = db_get_table_column(table, col);
  865. colname = db_get_column_name(column);
  866. /* skip fid column */
  867. if (strcmp(pg_info->fid_column, colname) == 0)
  868. continue;
  869. /* -> columns */
  870. sprintf(buf_tmp, "%s", colname);
  871. strcat(buf, buf_tmp);
  872. if (col < ncol - 1)
  873. strcat(buf, ",");
  874. /* -> values */
  875. value = db_get_column_value(column);
  876. /* for debug only */
  877. db_convert_column_value_to_string(column, &dbstmt);
  878. G_debug(2, "col %d : val = %s", col,
  879. db_get_string(&dbstmt));
  880. sqltype = db_get_column_sqltype(column);
  881. ctype = db_sqltype_to_Ctype(sqltype);
  882. /* prevent writing NULL values */
  883. if (!db_test_value_isnull(value)) {
  884. switch (ctype) {
  885. case DB_C_TYPE_INT:
  886. sprintf(buf_tmp, "%d", db_get_value_int(value));
  887. break;
  888. case DB_C_TYPE_DOUBLE:
  889. sprintf(buf_tmp, "%.14f",
  890. db_get_value_double(value));
  891. break;
  892. case DB_C_TYPE_STRING:
  893. str_val = G_store(db_get_value_string(value));
  894. G_str_to_sql(str_val);
  895. sprintf(buf_tmp, "'%s'", str_val);
  896. G_free(str_val);
  897. break;
  898. case DB_C_TYPE_DATETIME:
  899. db_convert_column_value_to_string(column,
  900. &dbstmt);
  901. sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
  902. break;
  903. default:
  904. G_warning(_("Unsupported column type %d"), ctype);
  905. sprintf(buf_tmp, "NULL");
  906. break;
  907. }
  908. }
  909. else {
  910. sprintf(buf_tmp, "NULL");
  911. }
  912. strcat(buf_val, buf_tmp);
  913. if (col < ncol - 1)
  914. strcat(buf_val, ",");
  915. }
  916. if (!pg_info->toposchema_name) {
  917. /* simple feature access */
  918. G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)",
  919. buf, pg_info->geom_column, buf_val, geom_data);
  920. }
  921. else {
  922. /* PostGIS topology access, write geometry in
  923. * topology schema, skip geometry at this point */
  924. G_asprintf(&stmt, "%s,%s) VALUES (%s,%d)",
  925. buf, pg_info->fid_column, buf_val, fid);
  926. }
  927. }
  928. }
  929. }
  930. else {
  931. /* no attributes */
  932. if (!pg_info->toposchema_name) {
  933. /* no attributes (simple features access) */
  934. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
  935. "('%s'::GEOMETRY)",
  936. pg_info->schema_name, pg_info->table_name,
  937. pg_info->geom_column, geom_data);
  938. }
  939. else if (cat > 0)
  940. /* no attributes (topology elements) */
  941. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES (%d)",
  942. pg_info->schema_name, pg_info->table_name,
  943. pg_info->fid_column, fid);
  944. }
  945. return stmt;
  946. }
  947. /*!
  948. \brief Build SELECT statement to insert new element into PostGIS
  949. topology schema
  950. \param Map pointer to Map_info struct
  951. \param Line pointer to P_line struct (topo)
  952. \param geom_data geometry in wkb
  953. \return pointer to allocated string buffer with SQL statement
  954. \return NULL on error
  955. */
  956. char *build_topo_stmt(struct Map_info *Map, int type,
  957. const struct P_line *Line, const char *geom_data)
  958. {
  959. char *stmt;
  960. struct Format_info_pg *pg_info;
  961. pg_info = &(Map->fInfo.pg);
  962. stmt = NULL;
  963. switch(type) {
  964. case GV_POINT: {
  965. #if USE_TOPO_STMT
  966. G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
  967. pg_info->toposchema_name, geom_data);
  968. #else
  969. G_asprintf(&stmt, "INSERT INTO \"%s\".node (geom) VALUES ('%s'::GEOMETRY)",
  970. pg_info->toposchema_name, geom_data);
  971. #endif
  972. break;
  973. }
  974. case GV_LINE: {
  975. int line;
  976. int n1, n2;
  977. int nle, nre; /* next left | right edge */
  978. /*
  979. * isolated lines
  980. next left edge: -fid
  981. next right edge: fid
  982. * connected lines
  983. next left edge: next line or -fid
  984. next right edge: next line or fid
  985. */
  986. if (!Line) {
  987. G_warning(_("Topology not available. Unable to insert new edge."));
  988. return NULL;
  989. }
  990. struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
  991. #if USE_TOPO_STMT
  992. G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
  993. pg_info->toposchema_name, geom_data);
  994. #else
  995. line = Vect_get_num_lines(Map);
  996. /* get number of lines for each node */
  997. n1 = Vect_get_node_n_lines(Map, topo->N1);
  998. n2 = Vect_get_node_n_lines(Map, topo->N2);
  999. /* assuming isolated lines */
  1000. nle = -line;
  1001. nre = line;
  1002. /* check for line connection */
  1003. if (n1 > 1) {
  1004. update_next_line(Map, n1, line, &nle, &nre);
  1005. }
  1006. if (n2 > 1) {
  1007. update_next_line(Map, n2, -line, &nle, &nre);
  1008. }
  1009. G_debug(3, "build_topo_stmt(): line=%d type=line nle=%d nre=%d", line, nle, nre);
  1010. G_asprintf(&stmt, "INSERT INTO \"%s\".edge_data (geom, start_node, end_node, "
  1011. "next_left_edge, abs_next_left_edge, next_right_edge, abs_next_right_edge, "
  1012. "left_face, right_face) "
  1013. "VALUES ('%s'::GEOMETRY, %d, %d, %d, %d, %d, %d, 0, 0)",
  1014. pg_info->toposchema_name, geom_data, topo->N1, topo->N2, nle, abs(nle),
  1015. nre, abs(nre));
  1016. #endif
  1017. break;
  1018. }
  1019. case GV_CENTROID: {
  1020. /* TopoGeo_AddPoint ? */
  1021. G_asprintf(&stmt, "SELECT AddNode('%s', '%s'::GEOMETRY)",
  1022. pg_info->toposchema_name, geom_data);
  1023. break;
  1024. }
  1025. case GV_BOUNDARY: {
  1026. /* TopoGeo_AddLineString ? */
  1027. G_asprintf(&stmt, "SELECT AddEdge('%s', '%s'::GEOMETRY)",
  1028. pg_info->toposchema_name, geom_data);
  1029. break;
  1030. }
  1031. default:
  1032. G_warning(_("Unsupported feature type %d"), type);
  1033. break;
  1034. }
  1035. return stmt;
  1036. }
  1037. /*!
  1038. \brief Execute SQL topo select statement
  1039. \param conn pointer to PGconn
  1040. \param stmt query
  1041. \return value on success
  1042. \return -1 on error
  1043. */
  1044. int execute_topo(PGconn *conn, const char *stmt)
  1045. {
  1046. int ret;
  1047. PGresult *result;
  1048. result = NULL;
  1049. G_debug(3, "execute_topo(): %s", stmt);
  1050. result = PQexec(conn, stmt);
  1051. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
  1052. PQntuples(result) != 1) {
  1053. PQclear(result);
  1054. G_warning(_("Execution failed: %s"), PQerrorMessage(conn));
  1055. return -1;
  1056. }
  1057. ret = atoi(PQgetvalue(result, 0, 0));
  1058. PQclear(result);
  1059. return ret;
  1060. }
  1061. /*!
  1062. \brief Find next line (topo only)
  1063. \param Map pointer to Map_info struct
  1064. \param nlines number of lines
  1065. \param line current line (negative for backward direction - ie. end node)
  1066. \param[out] left left line
  1067. \param[out] right right line
  1068. \return line id (left or right)
  1069. \return 0 on success
  1070. \return -1 on failure
  1071. */
  1072. int update_next_line(struct Map_info* Map, int nlines, int line,
  1073. int *left, int *right)
  1074. {
  1075. int next_line;
  1076. char stmt[DB_SQL_MAX];
  1077. struct Format_info_pg *pg_info;
  1078. pg_info = &(Map->fInfo.pg);
  1079. /* update left line side */
  1080. next_line = dig_angle_next_line(&(Map->plus), line,
  1081. line < 0 ? GV_LEFT : GV_RIGHT, GV_LINE, NULL);
  1082. if (line > 0)
  1083. *right = next_line;
  1084. else
  1085. *left = next_line;
  1086. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
  1087. "abs_next_left_edge = %d WHERE edge_id = %d",
  1088. pg_info->toposchema_name, line, abs(line), abs(next_line));
  1089. G_debug(4, "build_topo_stmt(): line=%d nle=%d | line=%d nle=%d",
  1090. line, next_line, abs(next_line), line);
  1091. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1092. /* rollback transaction */
  1093. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1094. return -1;
  1095. }
  1096. if (nlines > 2) {
  1097. /* update right line side */
  1098. next_line = dig_angle_next_line(&(Map->plus), line,
  1099. line < 0 ? GV_RIGHT : GV_LEFT, GV_LINE, NULL);
  1100. if (line > 0)
  1101. *right = next_line;
  1102. else
  1103. *left = next_line;
  1104. G_debug(4, "line=%d nre=%d", line, next_line);
  1105. /*
  1106. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
  1107. "abs_next_right_edge = %d WHERE edge_id = %d",
  1108. pg_info->toposchema_name, line, abs(line), abs(next_line));
  1109. G_debug(0, "build_topo_stmt(): line=%d nre=%d", abs(next_line), line);
  1110. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1111. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1112. return -1;
  1113. }
  1114. */
  1115. }
  1116. return 0;
  1117. }
  1118. #endif