write_pg.c 91 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963
  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. Write subroutine inspired by OGR PostgreSQL driver.
  6. \todo PostGIS version of V2__delete_area_cats_from_cidx_nat()
  7. \todo function to delete corresponding entry in fidx
  8. \todo PostGIS version of V2__add_area_cats_to_cidx_nat
  9. (C) 2012-2014 by Martin Landa, and the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. \author Martin Landa <landa.martin gmail.com>
  13. */
  14. #include <string.h>
  15. #include <grass/vector.h>
  16. #include <grass/glocale.h>
  17. #include "local_proto.h"
  18. #ifdef HAVE_POSTGRES
  19. #include "pg_local_proto.h"
  20. #define WKBSRIDFLAG 0x20000000
  21. #define TOPOGEOM_COLUMN "topo"
  22. /*! Use SQL statements from PostGIS Topology extension (this options
  23. is quite slow. By default are used simple SQL statements (INSERT, UPDATE)
  24. */
  25. #define USE_TOPO_STMT 0
  26. static int create_table(struct Format_info_pg *);
  27. static int check_schema(const struct Format_info_pg *);
  28. static int create_topo_schema(struct Format_info_pg *, int);
  29. static int create_pg_layer(struct Map_info *, int);
  30. static char *get_sftype(SF_FeatureType);
  31. static off_t write_line_sf(struct Map_info *, int,
  32. const struct line_pnts **, int,
  33. const struct line_cats *);
  34. static off_t write_line_tp(struct Map_info *, int, int,
  35. const struct line_pnts *,
  36. const struct line_cats *);
  37. static char *binary_to_hex(int, const unsigned char *);
  38. static unsigned char *point_to_wkb(int, const struct line_pnts *, int, int *);
  39. static unsigned char *linestring_to_wkb(int, const struct line_pnts *,
  40. int, int *);
  41. static unsigned char *polygon_to_wkb(int, const struct line_pnts **, int,
  42. int, int *);
  43. static char *line_to_wkb(struct Format_info_pg *, const struct line_pnts **,
  44. int, int, int);
  45. static int write_feature(struct Map_info *, int, int,
  46. const struct line_pnts **, int, int);
  47. static char *build_insert_stmt(const struct Format_info_pg *, const char *, int, int);
  48. static int insert_topo_element(struct Map_info *, int, int, const char *);
  49. static int type_to_topogeom(const struct Format_info_pg *);
  50. static int update_next_edge(struct Map_info*, int, int);
  51. static int delete_face(const struct Map_info *, int);
  52. static int update_topo_edge(struct Map_info *, int);
  53. static int update_topo_face(struct Map_info *, int);
  54. static int add_line_to_topo_pg(struct Map_info *, off_t, int, const struct line_pnts *);
  55. static int delete_line_from_topo_pg(struct Map_info *, int, int, const struct line_pnts *);
  56. static int set_constraint_to_deferrable(struct Format_info_pg *, const char *, const char *,
  57. const char *, const char *, const char *);
  58. static dbDriver *open_db(struct Format_info_pg *);
  59. #endif
  60. static struct line_pnts *Points;
  61. /*!
  62. \brief Writes feature on level 1 (PostGIS interface)
  63. Notes for simple feature access:
  64. - centroids are not supported in PostGIS, pseudotopo holds virtual
  65. centroids
  66. - boundaries are not supported in PostGIS, pseudotopo treats polygons
  67. as boundaries
  68. Notes for PostGIS Topology access:
  69. - centroids are stored as isolated nodes
  70. - boundaries are stored as edges
  71. \param Map pointer to Map_info structure
  72. \param type feature type (GV_POINT, GV_LINE, ...)
  73. \param points pointer to line_pnts structure (feature geometry)
  74. \param cats pointer to line_cats structure (feature categories)
  75. \return feature offset into file
  76. \return -1 on error
  77. */
  78. off_t V1_write_line_pg(struct Map_info *Map, int type,
  79. const struct line_pnts *points,
  80. const struct line_cats *cats)
  81. {
  82. #ifdef HAVE_POSTGRES
  83. struct Format_info_pg *pg_info;
  84. pg_info = &(Map->fInfo.pg);
  85. if (pg_info->feature_type == SF_GEOMETRY) {
  86. /* create PostGIS table if doesn't exist */
  87. if (create_pg_layer(Map, type) < 0)
  88. return -1;
  89. }
  90. if (!points)
  91. return 0;
  92. if (!pg_info->toposchema_name) { /* simple features access */
  93. return write_line_sf(Map, type, &points, 1, cats);
  94. }
  95. /* PostGIS Topology access */
  96. return write_line_tp(Map, type, FALSE, points, cats);
  97. #else
  98. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  99. return -1;
  100. #endif
  101. }
  102. /*!
  103. \brief Writes feature on topological level (PostGIS interface)
  104. Calls V2_write_line_sfa() for simple features access.
  105. \param Map pointer to Map_info structure
  106. \param type feature type (GV_POINT, GV_LINE, ...)
  107. \param points pointer to line_pnts structure (feature geometry)
  108. \param cats pointer to line_cats structure (feature categories)
  109. \return feature offset into file
  110. \return -1 on error
  111. */
  112. off_t V2_write_line_pg(struct Map_info *Map, int type,
  113. const struct line_pnts *points,
  114. const struct line_cats *cats)
  115. {
  116. #ifdef HAVE_POSTGRES
  117. struct Format_info_pg *pg_info;
  118. pg_info = &(Map->fInfo.pg);
  119. if (!pg_info->toposchema_name) { /* pseudo-topology */
  120. return V2_write_line_sfa(Map, type, points, cats);
  121. }
  122. /* PostGIS Topology */
  123. return write_line_tp(Map, type, FALSE, points, cats);
  124. #else
  125. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  126. return -1;
  127. #endif
  128. }
  129. /*!
  130. \brief Rewrites feature at the given offset (level 1) (PostGIS interface, internal use only)
  131. Only for simple feature access. PostGIS Topology requires level 2.
  132. \todo Use UPDATE statement ?
  133. \param Map pointer to Map_info structure
  134. \param offset feature offset
  135. \param type feature type (GV_POINT, GV_LINE, ...)
  136. \param points feature geometry
  137. \param cats feature categories
  138. \return feature offset (rewritten feature)
  139. \return -1 on error
  140. */
  141. off_t V1_rewrite_line_pg(struct Map_info * Map,
  142. off_t offset, int type,
  143. const struct line_pnts * points,
  144. const struct line_cats * cats)
  145. {
  146. G_debug(3, "V1_rewrite_line_pg(): type=%d offset=%" PRI_OFF_T,
  147. type, offset);
  148. #ifdef HAVE_POSTGRES
  149. if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
  150. G_warning(_("Unable to rewrite feature (incompatible feature types)"));
  151. return -1;
  152. }
  153. /* delete old */
  154. V1_delete_line_pg(Map, offset);
  155. return V1_write_line_pg(Map, type, points, cats);
  156. #else
  157. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  158. return -1;
  159. #endif
  160. }
  161. /*!
  162. \brief Rewrites feature at topological level (PostGIS interface, internal use only)
  163. Note: Topology must be built at level >= GV_BUILD_BASE
  164. \todo Handle also categories
  165. \todo Store original geometry in tmp table for restore
  166. \param Map pointer to Map_info structure
  167. \param line feature id
  168. \param type feature type (GV_POINT, GV_LINE, ...)
  169. \param points feature geometry
  170. \param cats feature categories
  171. \return offset where feature was rewritten
  172. \return -1 on error
  173. */
  174. off_t V2_rewrite_line_pg(struct Map_info *Map, off_t line, int type,
  175. const struct line_pnts *points, const struct line_cats *cats)
  176. {
  177. G_debug(3, "V2_rewrite_line_pg(): line=%d type=%d",
  178. (int)line, type);
  179. #ifdef HAVE_POSTGRES
  180. const char *schema_name, *table_name, *keycolumn;
  181. char *stmt, *geom_data;
  182. struct Format_info_pg *pg_info;
  183. struct P_line *Line;
  184. off_t offset;
  185. geom_data = NULL;
  186. stmt = NULL;
  187. pg_info = &(Map->fInfo.pg);
  188. if (line < 1 || line > Map->plus.n_lines) {
  189. G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
  190. return -1;
  191. }
  192. Line = Map->plus.Line[line];
  193. if (Line == NULL) {
  194. G_warning(_("Attempt to access dead feature %d"), (int)line);
  195. return -1;
  196. }
  197. offset = Line->offset;
  198. if (!(Map->plus.update_cidx)) {
  199. Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
  200. }
  201. if (!Points)
  202. Points = Vect_new_line_struct();
  203. if (type != V2_read_line_pg(Map, Points, NULL, line)) {
  204. G_warning(_("Unable to rewrite feature (incompatible feature types)"));
  205. return -1;
  206. }
  207. /* remove line from topology */
  208. if (0 != delete_line_from_topo_pg(Map, line, type, Points))
  209. return -1;
  210. if (pg_info->toposchema_name) { /* PostGIS Topology */
  211. schema_name = pg_info->toposchema_name;
  212. if (type & GV_POINTS) {
  213. table_name = keycolumn = "node";
  214. }
  215. else {
  216. table_name = "edge_data";
  217. keycolumn = "edge";
  218. }
  219. }
  220. else { /* simple features access */
  221. schema_name = pg_info->schema_name;
  222. table_name = pg_info->table_name;
  223. keycolumn = pg_info->fid_column;
  224. }
  225. geom_data = line_to_wkb(pg_info, &points, 1, type, Map->head.with_z);
  226. G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET geom = '%s'::GEOMETRY WHERE %s_id = %" PRI_OFF_T,
  227. schema_name, table_name, geom_data, keycolumn, line);
  228. G_free(geom_data);
  229. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  230. G_warning(_("Unable to rewrite feature %d"), (int)line);
  231. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  232. return -1;
  233. }
  234. /* update topology
  235. note: offset is not changed */
  236. return add_line_to_topo_pg(Map, offset, type, points);
  237. #else
  238. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  239. return -1;
  240. #endif
  241. }
  242. /*!
  243. \brief Deletes feature at the given offset (level 1)
  244. Only for simple feature access. PostGIS Topology requires level 2.
  245. \param Map pointer Map_info structure
  246. \param offset feature offset
  247. \return 0 on success
  248. \return -1 on error
  249. */
  250. int V1_delete_line_pg(struct Map_info *Map, off_t offset)
  251. {
  252. #ifdef HAVE_POSTGRES
  253. long fid;
  254. char stmt[DB_SQL_MAX];
  255. struct Format_info_pg *pg_info;
  256. pg_info = &(Map->fInfo.pg);
  257. if (!pg_info->conn || !pg_info->table_name) {
  258. G_warning(_("No connection defined"));
  259. return -1;
  260. }
  261. if (offset >= pg_info->offset.array_num) {
  262. G_warning(_("Invalid offset (%" PRI_OFF_T ")"), offset);
  263. return -1;
  264. }
  265. fid = pg_info->offset.array[offset];
  266. G_debug(3, "V1_delete_line_pg(): offset = %lu -> fid = %ld",
  267. (unsigned long)offset, fid);
  268. if (!pg_info->inTransaction) {
  269. /* start transaction */
  270. pg_info->inTransaction = TRUE;
  271. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
  272. return -1;
  273. }
  274. sprintf(stmt, "DELETE FROM %s WHERE %s = %ld",
  275. pg_info->table_name, pg_info->fid_column, fid);
  276. G_debug(3, "SQL: %s", stmt);
  277. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  278. G_warning(_("Unable to delete feature"));
  279. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  280. return -1;
  281. }
  282. return 0;
  283. #else
  284. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  285. return -1;
  286. #endif
  287. }
  288. /*!
  289. \brief Deletes feature on topological level (PostGIS interface)
  290. Note: Topology must be built at level >= GV_BUILD_BASE
  291. Calls V2_delete_line_sfa() for simple feature access.
  292. \param Map pointer to Map_info structure
  293. \param line feature id to be deleted
  294. \return 0 on success
  295. \return -1 on error
  296. */
  297. int V2_delete_line_pg(struct Map_info *Map, off_t line)
  298. {
  299. #ifdef HAVE_POSTGRES
  300. int ret;
  301. struct Format_info_pg *pg_info;
  302. pg_info = &(Map->fInfo.pg);
  303. if (line < 1 || line > Map->plus.n_lines) {
  304. G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
  305. return -1;
  306. }
  307. if (!pg_info->toposchema_name) { /* pseudo-topology */
  308. return V2_delete_line_sfa(Map, line);
  309. }
  310. else { /* PostGIS topology */
  311. int type;
  312. char stmt[DB_SQL_MAX];
  313. const char *table_name, *keycolumn;
  314. struct P_line *Line;
  315. if (line < 1 || line > Map->plus.n_lines) {
  316. G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
  317. return -1;
  318. }
  319. Line = Map->plus.Line[line];
  320. if (!Line) {
  321. G_warning(_("Attempt to access dead feature %d"), (int)line);
  322. return -1;
  323. }
  324. if (!(Map->plus.update_cidx)) {
  325. Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
  326. }
  327. Vect__execute_pg(pg_info->conn, "BEGIN");
  328. if (Line->type & GV_POINTS) {
  329. table_name = keycolumn = "node";
  330. }
  331. else {
  332. table_name = "edge_data";
  333. keycolumn = "edge";
  334. /* first remove references to this edge */
  335. /* (1) left next edge */
  336. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_left_edge = edge_id, "
  337. "next_left_edge = -edge_id WHERE abs_next_left_edge = %d",
  338. pg_info->toposchema_name, table_name, (int)Line->offset);
  339. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  340. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  341. return -1;
  342. }
  343. /* (2) right next edge */
  344. sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_right_edge = edge_id, "
  345. "next_right_edge = edge_id WHERE abs_next_right_edge = %d",
  346. pg_info->toposchema_name, table_name, (int)Line->offset);
  347. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  348. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  349. return -1;
  350. }
  351. }
  352. /* read the line */
  353. if (!Points)
  354. Points = Vect_new_line_struct();
  355. type = V2_read_line_pg(Map, Points, NULL, line);
  356. if (type < 0)
  357. return -1;
  358. /* delete record from topology table */
  359. sprintf(stmt, "DELETE FROM \"%s\".\"%s\" WHERE %s_id = %d",
  360. pg_info->toposchema_name, table_name, keycolumn, (int)Line->offset);
  361. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  362. G_warning(_("Unable to delete feature (%s) %d"), keycolumn,
  363. (int)line);
  364. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  365. return -1;
  366. }
  367. if (pg_info->cache.ctype == CACHE_MAP) {
  368. /* delete from cache */
  369. Vect_destroy_line_struct(pg_info->cache.lines[line-1]);
  370. pg_info->cache.lines[line-1] = NULL;
  371. pg_info->cache.lines_types[line-1] = 0;
  372. pg_info->cache.lines_cats[line-1] = 0;
  373. }
  374. /* update topology */
  375. ret = delete_line_from_topo_pg(Map, line, type, Points);
  376. if (ret == 0)
  377. Vect__execute_pg(pg_info->conn, "COMMIT");
  378. return ret;
  379. }
  380. #else
  381. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  382. return -1;
  383. #endif
  384. }
  385. #ifdef HAVE_POSTGRES
  386. /*!
  387. \brief Writes node on topological level (PostGIS Topology
  388. interface, internal use only)
  389. The vector map must be open on level 2 at least with
  390. GV_BUILD_BASE. PostGIS Topology schema must be defined.
  391. \param Map pointer to Map_info structure
  392. \param node node id (starts at 1)
  393. \param points pointer to line_pnts structure
  394. \return 0 on success
  395. \return -1 on error
  396. */
  397. off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
  398. {
  399. struct Format_info_pg *pg_info;
  400. pg_info = &(Map->fInfo.pg);
  401. if (!pg_info->toposchema_name)
  402. return -1; /* PostGIS Topology required */
  403. return write_line_tp(Map, GV_POINT, TRUE, points, NULL);
  404. }
  405. /*!
  406. \brief Writes area on topological level (PostGIS Simple Features
  407. interface, internal use only)
  408. \param Map pointer to Map_info structure
  409. \param points feature geometry (exterior + interior rings)
  410. \param nparts number of parts including exterior ring
  411. \param cats feature categories
  412. \return feature offset
  413. \return -1 on error
  414. */
  415. off_t V2__write_area_pg(struct Map_info *Map,
  416. const struct line_pnts **points, int nparts,
  417. const struct line_cats *cats)
  418. {
  419. return write_line_sf(Map, GV_BOUNDARY, points, nparts, cats);
  420. }
  421. /*!
  422. \brief Updates simple features geometry from GRASS-like topo
  423. \param Map pointer to Map_info structure
  424. \param points feature geometry (exterior + interior rings)
  425. \param nparts number of parts including exterior ring
  426. \param cat area category
  427. \return 0 on success
  428. \return -1 on error
  429. */
  430. int V2__update_area_pg(struct Map_info *Map,
  431. const struct line_pnts **points, int nparts,
  432. int cat)
  433. {
  434. int part, npoints;
  435. char *stmt, *geom_data;
  436. struct Format_info_pg *pg_info;
  437. pg_info = &(Map->fInfo.pg);
  438. for (part = 0; part < nparts; part++) {
  439. npoints = points[part]->n_points - 1;
  440. if (points[part]->x[0] != points[part]->x[npoints] ||
  441. points[part]->y[0] != points[part]->y[npoints] ||
  442. points[part]->z[0] != points[part]->z[npoints]) {
  443. G_warning(_("Boundary is not closed. Skipping."));
  444. return -1;
  445. }
  446. }
  447. geom_data = line_to_wkb(pg_info, points, nparts, GV_AREA, Vect_is_3d(Map));
  448. if (!geom_data)
  449. return -1;
  450. stmt = NULL;
  451. G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET %s = '%s'::GEOMETRY WHERE %s = %d",
  452. pg_info->schema_name, pg_info->table_name, pg_info->geom_column,
  453. geom_data, pg_info->fid_column,
  454. cat);
  455. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  456. /* rollback transaction */
  457. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  458. G_free(geom_data);
  459. G_free(stmt);
  460. return -1;
  461. }
  462. G_free(geom_data);
  463. G_free(stmt);
  464. return 0;
  465. }
  466. /*!
  467. \brief Create new feature table
  468. \param pg_info pointer to Format_info_pg
  469. \return -1 on error
  470. \return 0 on success
  471. */
  472. int create_table(struct Format_info_pg *pg_info)
  473. {
  474. int spatial_index, primary_key;
  475. char stmt[DB_SQL_MAX];
  476. char *geom_type, *def_file;
  477. struct field_info *Fi;
  478. PGresult *result;
  479. def_file = getenv("GRASS_VECTOR_PGFILE");
  480. /* by default create spatial index & add primary key */
  481. spatial_index = primary_key = TRUE;
  482. if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
  483. FILE *fp;
  484. const char *p;
  485. struct Key_Value *key_val;
  486. fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
  487. if (!fp) {
  488. G_warning(_("Unable to open PG file"));
  489. }
  490. else {
  491. key_val = G_fread_key_value(fp);
  492. fclose(fp);
  493. /* disable spatial index ? */
  494. p = G_find_key_value("spatial_index", key_val);
  495. if (p && G_strcasecmp(p, "no") == 0)
  496. spatial_index = FALSE;
  497. /* disable primary key ? */
  498. p = G_find_key_value("primary_key", key_val);
  499. if (p && G_strcasecmp(p, "no") == 0)
  500. primary_key = FALSE;
  501. G_free_key_value(key_val);
  502. }
  503. }
  504. /* create schema if not exists */
  505. if (G_strcasecmp(pg_info->schema_name, "public") != 0) {
  506. if (check_schema(pg_info) != 0)
  507. return -1;
  508. }
  509. /* prepare CREATE TABLE statement */
  510. sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL%s, %s INTEGER",
  511. pg_info->schema_name, pg_info->table_name, pg_info->fid_column,
  512. primary_key ? " PRIMARY KEY" : "", GV_KEY_COLUMN);
  513. Fi = pg_info->fi;
  514. if (Fi) {
  515. /* append attributes */
  516. int col, ncols, sqltype, length;
  517. char stmt_col[DB_SQL_MAX];
  518. const char *colname;
  519. dbString dbtable_name;
  520. dbDriver *driver;
  521. dbTable *table;
  522. dbColumn *column;
  523. db_init_string(&dbtable_name);
  524. driver = open_db(pg_info);
  525. if (driver == NULL)
  526. return -1;
  527. /* describe table */
  528. db_set_string(&dbtable_name, Fi->table);
  529. if (db_describe_table(driver, &dbtable_name, &table) != DB_OK) {
  530. G_warning(_("Unable to describe table <%s>"),
  531. Fi->table);
  532. db_close_database_shutdown_driver(driver);
  533. pg_info->dbdriver = NULL;
  534. return -1;
  535. }
  536. ncols = db_get_table_number_of_columns(table);
  537. G_debug(3,
  538. "copying attributes: driver = %s database = %s table = %s cols = %d",
  539. Fi->driver, Fi->database, Fi->table, ncols);
  540. for (col = 0; col < ncols; col++) {
  541. column = db_get_table_column(table, col);
  542. colname = db_get_column_name(column);
  543. sqltype = db_get_column_sqltype(column);
  544. length = db_get_column_length(column);
  545. G_debug(3, "\tcolumn = %d name = %s type = %d length = %d",
  546. col, colname, sqltype, length);
  547. if (G_strcasecmp(pg_info->fid_column, colname) == 0 ||
  548. G_strcasecmp(GV_KEY_COLUMN, colname) == 0) {
  549. /* skip fid column if exists */
  550. G_debug(3, "\t%s skipped", colname);
  551. continue;
  552. }
  553. /* append column */
  554. sprintf(stmt_col, ",\"%s\" %s", colname, db_sqltype_name(sqltype));
  555. strcat(stmt, stmt_col);
  556. if (sqltype == DB_SQL_TYPE_CHARACTER) {
  557. /* length only for string columns */
  558. sprintf(stmt_col, "(%d)", length);
  559. strcat(stmt, stmt_col);
  560. }
  561. }
  562. db_free_string(&dbtable_name);
  563. }
  564. strcat(stmt, ")"); /* close CREATE TABLE statement */
  565. /* begin transaction (create table) */
  566. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
  567. return -1;
  568. }
  569. /* create table */
  570. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  571. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  572. return -1;
  573. }
  574. /* determine geometry type (string) */
  575. switch (pg_info->feature_type) {
  576. case (SF_POINT):
  577. geom_type = "POINT";
  578. break;
  579. case (SF_LINESTRING):
  580. geom_type = "LINESTRING";
  581. break;
  582. case (SF_POLYGON):
  583. geom_type = "POLYGON";
  584. break;
  585. case (SF_POLYGON25D):
  586. geom_type = "POLYGONZ";
  587. break;
  588. case (SF_GEOMETRY):
  589. geom_type = "GEOMETRY";
  590. break;
  591. default:
  592. G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
  593. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  594. return -1;
  595. }
  596. /* add geometry column */
  597. sprintf(stmt, "SELECT AddGeometryColumn('%s', '%s', "
  598. "'%s', %d, '%s', %d)",
  599. pg_info->schema_name, pg_info->table_name,
  600. pg_info->geom_column, pg_info->srid,
  601. geom_type, pg_info->coor_dim);
  602. G_debug(2, "SQL: %s", stmt);
  603. result = PQexec(pg_info->conn, stmt);
  604. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  605. G_warning("%s", PQresultErrorMessage(result));
  606. PQclear(result);
  607. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  608. return -1;
  609. }
  610. /* create indices
  611. - GV_KEY_COLUMN
  612. - geometry column
  613. */
  614. sprintf(stmt,
  615. "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" (%s)",
  616. pg_info->table_name, GV_KEY_COLUMN,
  617. pg_info->schema_name, pg_info->table_name,
  618. GV_KEY_COLUMN);
  619. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  620. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  621. return -1;
  622. }
  623. if (spatial_index) {
  624. G_verbose_message(_("Building spatial index on <%s>..."),
  625. pg_info->geom_column);
  626. sprintf(stmt,
  627. "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" USING GIST (%s)",
  628. pg_info->table_name, pg_info->geom_column,
  629. pg_info->schema_name, pg_info->table_name,
  630. pg_info->geom_column);
  631. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  632. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  633. return -1;
  634. }
  635. }
  636. /* close transaction (create table) */
  637. if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
  638. return -1;
  639. }
  640. return 0;
  641. }
  642. /*!
  643. \brief Creates new schema for feature table if not exists
  644. \param pg_info pointer to Format_info_pg
  645. \return -1 on error
  646. \return 0 on success
  647. */
  648. int check_schema(const struct Format_info_pg *pg_info)
  649. {
  650. int i, found, nschema;
  651. char stmt[DB_SQL_MAX];
  652. PGresult *result;
  653. if (!pg_info->conn || !pg_info->table_name) {
  654. G_warning(_("No connection defined"));
  655. return -1;
  656. }
  657. /* add geometry column */
  658. sprintf(stmt, "SELECT nspname FROM pg_namespace");
  659. G_debug(2, "SQL: %s", stmt);
  660. result = PQexec(pg_info->conn, stmt);
  661. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
  662. PQclear(result);
  663. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  664. return -1;
  665. }
  666. found = FALSE;
  667. nschema = PQntuples(result);
  668. for (i = 0; i < nschema && !found; i++) {
  669. if (strcmp(pg_info->schema_name, PQgetvalue(result, i, 0)) == 0)
  670. found = TRUE;
  671. }
  672. PQclear(result);
  673. if (!found) {
  674. sprintf(stmt, "CREATE SCHEMA %s", pg_info->schema_name);
  675. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  676. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  677. return -1;
  678. }
  679. G_warning(_("Schema <%s> doesn't exist, created"),
  680. pg_info->schema_name);
  681. }
  682. return 0;
  683. }
  684. /*!
  685. \brief Create new PostGIS topology schema
  686. - create topology schema
  687. - add topology column to the feature table
  688. \todo Add constraints for grass-like tables
  689. \param pg_info pointer to Format_info_pg
  690. \return 0 on success
  691. \return 1 topology disable, nothing to do
  692. \return -1 on failure
  693. */
  694. int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
  695. {
  696. double tolerance;
  697. char stmt[DB_SQL_MAX];
  698. char *def_file;
  699. def_file = getenv("GRASS_VECTOR_PGFILE");
  700. /* read default values from PG file*/
  701. tolerance = 0.;
  702. if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
  703. FILE *fp;
  704. const char *p;
  705. struct Key_Value *key_val;
  706. fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
  707. if (!fp) {
  708. G_fatal_error(_("Unable to open PG file"));
  709. }
  710. key_val = G_fread_key_value(fp);
  711. fclose(fp);
  712. /* tolerance */
  713. p = G_find_key_value("topo_tolerance", key_val);
  714. if (p)
  715. tolerance = atof(p);
  716. G_debug(1, "PG: tolerance: %f", tolerance);
  717. /* topogeom column */
  718. p = G_find_key_value("topogeom_name", key_val);
  719. if (p)
  720. pg_info->topogeom_column = G_store(p);
  721. else
  722. pg_info->topogeom_column = G_store(TOPOGEOM_COLUMN);
  723. G_debug(1, "PG: topogeom_column :%s", pg_info->topogeom_column);
  724. /* topo-geo only (default: no) */
  725. p = G_find_key_value("topo_geo_only", key_val);
  726. if (p && G_strcasecmp(p, "yes") == 0)
  727. pg_info->topo_geo_only = TRUE;
  728. G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
  729. /* build simple features from topogeometry data */
  730. p = G_find_key_value("simple_feature", key_val);
  731. if (p && G_strcasecmp(p, "yes") == 0)
  732. pg_info->topo_geo_only = TRUE;
  733. G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
  734. G_free_key_value(key_val);
  735. }
  736. /* begin transaction (create topo schema) */
  737. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
  738. return -1;
  739. }
  740. /* create topology schema */
  741. G_verbose_message(_("Creating topology schema <%s>..."),
  742. pg_info->toposchema_name);
  743. sprintf(stmt, "SELECT topology.createtopology('%s', "
  744. "find_srid('%s', '%s', '%s'), %f, '%s')",
  745. pg_info->toposchema_name, pg_info->schema_name,
  746. pg_info->table_name, pg_info->geom_column, tolerance,
  747. with_z == WITH_Z ? "t" : "f");
  748. pg_info->toposchema_id = Vect__execute_get_value_pg(pg_info->conn, stmt);
  749. if (pg_info->toposchema_id == -1) {
  750. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  751. return -1;
  752. }
  753. /* add topo column to the feature table */
  754. G_verbose_message(_("Adding new topology column <%s>..."),
  755. pg_info->topogeom_column);
  756. sprintf(stmt, "SELECT topology.AddTopoGeometryColumn('%s', '%s', '%s', "
  757. "'%s', '%s')", pg_info->toposchema_name, pg_info->schema_name,
  758. pg_info->table_name, pg_info->topogeom_column,
  759. get_sftype(pg_info->feature_type));
  760. if (-1 == Vect__execute_get_value_pg(pg_info->conn, stmt)) {
  761. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  762. return -1;
  763. }
  764. /* create index on topo column */
  765. sprintf(stmt, "CREATE INDEX \"%s_%s_%s_idx\" ON \"%s\".\"%s\" (((%s).id))",
  766. pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
  767. pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column);
  768. if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
  769. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  770. return -1;
  771. }
  772. /* change constraints to deferrable initially deferred */
  773. if (!pg_info->topo_geo_only) {
  774. if (-1 == set_constraint_to_deferrable(pg_info, "node", "face_exists",
  775. "containing_face", "face", "face_id") ||
  776. -1 == set_constraint_to_deferrable(pg_info, "edge_data", "end_node_exists",
  777. "end_node", "node", "node_id") ||
  778. -1 == set_constraint_to_deferrable(pg_info, "edge_data", "left_face_exists",
  779. "left_face", "face", "face_id") ||
  780. -1 == set_constraint_to_deferrable(pg_info, "edge_data", "right_face_exists",
  781. "right_face", "face", "face_id") ||
  782. -1 == set_constraint_to_deferrable(pg_info, "edge_data", "start_node_exists",
  783. "start_node", "node", "node_id"))
  784. return -1;
  785. }
  786. /* create additional tables in topological schema to store
  787. GRASS topology in DB */
  788. if (!pg_info->topo_geo_only) {
  789. /* (1) create 'node_grass' (see P_node struct)
  790. todo: add constraints for lines and angles
  791. */
  792. sprintf(stmt, "CREATE TABLE \"%s\".%s (node_id SERIAL PRIMARY KEY, "
  793. "lines integer[], angles float[])", pg_info->toposchema_name, TOPO_TABLE_NODE);
  794. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  795. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  796. return -1;
  797. }
  798. sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT node_exists "
  799. "FOREIGN KEY (node_id) REFERENCES \"%s\".node (node_id) "
  800. "DEFERRABLE INITIALLY DEFERRED",
  801. pg_info->toposchema_name, TOPO_TABLE_NODE, pg_info->toposchema_name);
  802. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  803. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  804. return -1;
  805. }
  806. /* (2) create 'line_grass' (see P_line struct)
  807. */
  808. sprintf(stmt, "CREATE TABLE \"%s\".%s (line_id SERIAL PRIMARY KEY, "
  809. "left_area integer, right_area integer)",
  810. pg_info->toposchema_name, TOPO_TABLE_LINE);
  811. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  812. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  813. return -1;
  814. }
  815. sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT line_exists "
  816. "FOREIGN KEY (line_id) REFERENCES \"%s\".edge_data (edge_id) "
  817. "DEFERRABLE INITIALLY DEFERRED",
  818. pg_info->toposchema_name, TOPO_TABLE_LINE, pg_info->toposchema_name);
  819. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  820. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  821. return -1;
  822. }
  823. /* (3) create 'area_grass' (see P_area struct)
  824. todo: add constraints for lines, centtroid and isles
  825. */
  826. sprintf(stmt, "CREATE TABLE \"%s\".%s (area_id SERIAL PRIMARY KEY, "
  827. "lines integer[], centroid integer, isles integer[])",
  828. pg_info->toposchema_name, TOPO_TABLE_AREA);
  829. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  830. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  831. return -1;
  832. }
  833. /* (4) create 'isle_grass' (see P_isle struct)
  834. todo: add constraints for lines and area
  835. */
  836. sprintf(stmt, "CREATE TABLE \"%s\".%s (isle_id SERIAL PRIMARY KEY, "
  837. "lines integer[], area integer)",
  838. pg_info->toposchema_name, TOPO_TABLE_ISLE);
  839. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  840. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  841. return -1;
  842. }
  843. }
  844. /* close transaction (create topo schema) */
  845. if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
  846. return -1;
  847. }
  848. return 0;
  849. }
  850. /*!
  851. \brief Create new PostGIS layer in given database (internal use only)
  852. V1_open_new_pg() must be called before this function.
  853. List of currently supported types:
  854. - GV_POINT (SF_POINT)
  855. - GV_LINE (SF_LINESTRING)
  856. - GV_BOUNDARY (SF_POLYGON)
  857. When PostGIS Topology the map level is updated to topological level
  858. and build level set to GV_BUILD_BASE.
  859. \param[in,out] Map pointer to Map_info structure
  860. \param type feature type (GV_POINT, GV_LINE, ...)
  861. \return 0 success
  862. \return -1 error
  863. */
  864. int create_pg_layer(struct Map_info *Map, int type)
  865. {
  866. int ndblinks;
  867. struct Format_info_pg *pg_info;
  868. pg_info = &(Map->fInfo.pg);
  869. if (!pg_info->conninfo) {
  870. G_warning(_("Connection string not defined"));
  871. return -1;
  872. }
  873. if (!pg_info->table_name) {
  874. G_warning(_("PostGIS feature table not defined"));
  875. return -1;
  876. }
  877. G_debug(1, "Vect__open_new_pg(): conninfo='%s' table='%s' -> type = %d",
  878. pg_info->conninfo, pg_info->table_name, type);
  879. /* determine geometry type */
  880. switch (type) {
  881. case GV_POINT:
  882. case GV_CENTROID:
  883. pg_info->feature_type = SF_POINT;
  884. break;
  885. case GV_LINE:
  886. case GV_BOUNDARY:
  887. pg_info->feature_type = SF_LINESTRING;
  888. break;
  889. case GV_AREA:
  890. pg_info->feature_type = SF_POLYGON;
  891. break;
  892. case GV_FACE:
  893. pg_info->feature_type = SF_POLYGON25D;
  894. break;
  895. case -2:
  896. pg_info->feature_type = SF_GEOMETRY;
  897. break;
  898. default:
  899. G_warning(_("Unsupported geometry type (%d)"), type);
  900. return -1;
  901. }
  902. /* coordinate dimension */
  903. pg_info->coor_dim = Vect_is_3d(Map) ? 3 : 2;
  904. /* create new PostGIS table */
  905. ndblinks = Vect_get_num_dblinks(Map);
  906. if (ndblinks > 0) {
  907. pg_info->fi = Vect_get_dblink(Map, 0); /* TODO: support more layers */
  908. if (pg_info->fi) {
  909. if (ndblinks > 1)
  910. G_warning(_("More layers defined, using driver <%s> and "
  911. "database <%s>"),
  912. pg_info->fi->driver, pg_info->fi->database);
  913. }
  914. else {
  915. G_warning(_("Database connection not defined. "
  916. "Unable to write attributes."));
  917. }
  918. }
  919. /* create new feature table */
  920. if (create_table(pg_info) == -1) {
  921. G_warning(_("Unable to create new PostGIS feature table"));
  922. return -1;
  923. }
  924. /* create new topology schema (if PostGIS topology support is enabled) */
  925. if (pg_info->toposchema_name) {
  926. /* force topological level */
  927. Map->level = LEVEL_2;
  928. Map->plus.built = GV_BUILD_BASE;
  929. /* track updated features, used in V2__add_line_to_topo_nat() */
  930. Vect_set_updated(Map, TRUE);
  931. if (create_topo_schema(pg_info, Vect_is_3d(Map)) == -1) {
  932. G_warning(_("Unable to create new PostGIS topology schema"));
  933. return -1;
  934. }
  935. }
  936. return 0;
  937. }
  938. /*!
  939. \brief Get simple feature type as a string
  940. Used for AddTopoGeometryColumn().
  941. Valid types:
  942. - SF_POINT
  943. - SF_LINESTRING
  944. - SF_POLYGON
  945. \return string with feature type
  946. \return empty string
  947. */
  948. char *get_sftype(SF_FeatureType sftype)
  949. {
  950. if (sftype == SF_POINT)
  951. return "POINT";
  952. else if (sftype == SF_LINESTRING)
  953. return "LINE";
  954. else if (sftype == SF_POLYGON)
  955. return "POLYGON";
  956. else if (sftype == SF_GEOMETRY || sftype == SF_GEOMETRYCOLLECTION)
  957. return "COLLECTION";
  958. else
  959. G_warning(_("Unsupported feature type %d"), sftype);
  960. return "";
  961. }
  962. /*!
  963. \brief Write vector features as PostGIS simple feature element
  964. \param Map pointer to Map_info structure
  965. \param type feature type (GV_POINT, GV_LINE, ...)
  966. \param points feature geometry (exterior + interior rings for polygonsx)
  967. \param nparts number of parts
  968. \param cats feature categories
  969. \return feature offset
  970. \return -1 on error
  971. */
  972. off_t write_line_sf(struct Map_info *Map, int type,
  973. const struct line_pnts **points, int nparts,
  974. const struct line_cats *cats)
  975. {
  976. int cat;
  977. off_t offset;
  978. SF_FeatureType sf_type;
  979. struct Format_info_pg *pg_info;
  980. struct Format_info_offset *offset_info;
  981. pg_info = &(Map->fInfo.pg);
  982. offset_info = &(pg_info->offset);
  983. if (nparts < 1)
  984. return -1;
  985. /* check required PG settings */
  986. if (!pg_info->conn) {
  987. G_warning(_("No connection defined"));
  988. return -1;
  989. }
  990. if (!pg_info->table_name) {
  991. G_warning(_("PostGIS feature table not defined"));
  992. return -1;
  993. }
  994. /* create PostGIS table if doesn't exist */
  995. if (pg_info->feature_type == SF_GEOMETRY) {
  996. if (create_pg_layer(Map, type) < 0)
  997. return -1;
  998. }
  999. /* get category & check for attributes */
  1000. cat = -1;
  1001. if (cats->n_cats > 0) {
  1002. int field;
  1003. if (pg_info->fi)
  1004. field = pg_info->fi->number;
  1005. else
  1006. field = 1;
  1007. if (!Vect_cat_get(cats, field, &cat))
  1008. G_warning(_("No category defined for layer %d"), field);
  1009. if (cats->n_cats > 1) {
  1010. G_warning(_("Feature has more categories, using "
  1011. "category %d (from layer %d)"),
  1012. cat, field);
  1013. }
  1014. }
  1015. sf_type = pg_info->feature_type;
  1016. /* determine matching PostGIS feature geometry type */
  1017. if (type & (GV_POINT | GV_KERNEL)) {
  1018. if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
  1019. G_warning(_("Point skipped (output feature type: %s)"),
  1020. Vect_get_finfo_geometry_type(Map));
  1021. return 0;
  1022. }
  1023. }
  1024. else if (type & GV_LINE) {
  1025. if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
  1026. G_warning(_("Line skipped (output feature type: %s)"),
  1027. Vect_get_finfo_geometry_type(Map));
  1028. return 0;
  1029. }
  1030. }
  1031. else if (type & GV_CENTROID) {
  1032. if (sf_type != SF_POLYGON && sf_type != SF_POINT) {
  1033. G_warning(_("Centroid skipped (output feature type: %s)"),
  1034. Vect_get_finfo_geometry_type(Map));
  1035. return 0;
  1036. }
  1037. }
  1038. else if (type & GV_BOUNDARY) {
  1039. if (sf_type != SF_POLYGON && sf_type != SF_LINESTRING) {
  1040. G_warning(_("Boundary skipped (output feature type: %s)"),
  1041. Vect_get_finfo_geometry_type(Map));
  1042. return 0;
  1043. }
  1044. }
  1045. else if (type & GV_FACE) {
  1046. if (sf_type != SF_POLYGON25D) {
  1047. G_warning(_("Face skipped (output feature type: %s)"),
  1048. Vect_get_finfo_geometry_type(Map));
  1049. return 0;
  1050. }
  1051. }
  1052. else {
  1053. G_warning(_("Unsupported feature type %d"), type);
  1054. return -1;
  1055. }
  1056. G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d",
  1057. type, points[0]->n_points, cat);
  1058. if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
  1059. /* skip this check when writing PostGIS topology */
  1060. int part, npoints;
  1061. for (part = 0; part < nparts; part++) {
  1062. npoints = points[part]->n_points - 1;
  1063. if (points[part]->x[0] != points[part]->x[npoints] ||
  1064. points[part]->y[0] != points[part]->y[npoints] ||
  1065. points[part]->z[0] != points[part]->z[npoints]) {
  1066. G_warning(_("Boundary is not closed. Skipping."));
  1067. return -1;
  1068. }
  1069. }
  1070. }
  1071. /* write feature's geometry and fid */
  1072. if (-1 == write_feature(Map, -1, type, points, nparts, cat)) {
  1073. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1074. return -1;
  1075. }
  1076. /* update offset array */
  1077. if (offset_info->array_num >= offset_info->array_alloc) {
  1078. offset_info->array_alloc += 1000;
  1079. offset_info->array = (int *)G_realloc(offset_info->array,
  1080. offset_info->array_alloc *
  1081. sizeof(int));
  1082. }
  1083. offset = offset_info->array_num;
  1084. offset_info->array[offset_info->array_num++] = cat;
  1085. if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
  1086. /* register first part in offset array */
  1087. offset_info->array[offset_info->array_num++] = 0;
  1088. }
  1089. G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
  1090. (unsigned long)offset, offset_info->array_num, cat);
  1091. return offset;
  1092. }
  1093. /*!
  1094. \brief Write vector feature in PostGIS topology schema and
  1095. updates internal topology structures
  1096. \param Map vector map
  1097. \param type feature type to be written
  1098. \param points feature geometry
  1099. \param is_node TRUE for nodes (written as points)
  1100. \return feature id (build level >= GV_BUILD_BASE otherwise 0)
  1101. \return 0 for nodes
  1102. \return -1 on error
  1103. */
  1104. off_t write_line_tp(struct Map_info *Map, int type, int is_node,
  1105. const struct line_pnts *points,
  1106. const struct line_cats *cats)
  1107. {
  1108. int line, cat, line_id;
  1109. struct Format_info_pg *pg_info;
  1110. struct Plus_head *plus;
  1111. struct field_info *Fi;
  1112. pg_info = &(Map->fInfo.pg);
  1113. plus = &(Map->plus);
  1114. if (!(plus->update_cidx)) {
  1115. plus->cidx_up_to_date = FALSE; /* category index will be outdated */
  1116. }
  1117. /* check type for nodes */
  1118. if (is_node && type != GV_POINT) {
  1119. G_warning(_("Invalid feature type (%d) for nodes"), type);
  1120. return -1;
  1121. }
  1122. /* check required PG settings */
  1123. if (!pg_info->conn) {
  1124. G_warning(_("No connection defined"));
  1125. return -1;
  1126. }
  1127. if (!pg_info->table_name) {
  1128. G_warning(_("PostGIS feature table not defined"));
  1129. return -1;
  1130. }
  1131. if (!pg_info->toposchema_name) {
  1132. G_warning(_("PostGIS topology schema not defined"));
  1133. return -1;
  1134. }
  1135. /* create PostGIS table if doesn't exist */
  1136. if (pg_info->feature_type == SF_GEOMETRY) {
  1137. if (create_pg_layer(Map, type) < 0)
  1138. return -1;
  1139. }
  1140. if (!points)
  1141. return 0;
  1142. G_debug(3, "write_line_pg(): type = %d n_points = %d",
  1143. type, points->n_points);
  1144. Fi = pg_info->fi;
  1145. cat = -1;
  1146. if (cats && cats->n_cats > 0) {
  1147. if (Fi) {
  1148. if (!pg_info->dbdriver)
  1149. open_db(pg_info);
  1150. if (!Vect_cat_get(cats, Fi->number, &cat))
  1151. G_warning(_("No category defined for layer %d"), Fi->number);
  1152. if (cats->n_cats > 1) {
  1153. G_warning(_("Feature has more categories, using "
  1154. "category %d (from layer %d)"),
  1155. cat, cats->field[0]);
  1156. }
  1157. }
  1158. /* assume layer=1 */
  1159. Vect_cat_get(cats, 1, &cat);
  1160. }
  1161. /* update GRASS topology before writing PostGIS feature */
  1162. line = 0;
  1163. if (plus->built >= GV_BUILD_BASE) {
  1164. if (is_node) {
  1165. /* nodes are given with negative id */
  1166. line = -1 * dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
  1167. }
  1168. else {
  1169. off_t offset;
  1170. /* better is probably to check nextval directly */
  1171. if (type & GV_POINTS) {
  1172. offset = Vect_get_num_primitives(Map, GV_POINTS) + 1; /* next */
  1173. offset += Vect_get_num_nodes(Map); /* nodes are also stored in 'node' table */
  1174. }
  1175. else { /* LINES */
  1176. offset = Vect_get_num_primitives(Map, GV_LINES) + 1; /* next */
  1177. }
  1178. line = add_line_to_topo_pg(Map, offset, type, points);
  1179. }
  1180. }
  1181. /* write new feature to PostGIS
  1182. - feature table for simple features
  1183. - feature table and topo schema for topological access
  1184. */
  1185. line_id = write_feature(Map, line, type, &points, 1, cat);
  1186. if (line_id < 0) {
  1187. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1188. return -1;
  1189. }
  1190. if (pg_info->cache.ctype == CACHE_MAP) {
  1191. /* add line to the cache */
  1192. Vect__reallocate_cache(&(pg_info->cache), 1, TRUE);
  1193. pg_info->cache.lines[line-1] = Vect_new_line_struct();
  1194. pg_info->cache.lines_types[line-1] = type;
  1195. pg_info->cache.lines_cats[line-1] = cat;
  1196. }
  1197. /* update offset array for nodes */
  1198. if (is_node) {
  1199. int node;
  1200. struct Format_info_offset *offset;
  1201. offset = &(pg_info->offset);
  1202. node = abs(line);
  1203. if (node > offset->array_alloc) {
  1204. offset->array_alloc += 1000;
  1205. offset->array = (int *) G_realloc (offset->array, offset->array_alloc * sizeof(int));
  1206. }
  1207. offset->array_num = node;
  1208. offset->array[node-1] = (int) line_id; /* node id starts at 1 */
  1209. }
  1210. /* update PostGIS-line topo */
  1211. if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
  1212. update_topo_face(Map, line); /* TODO: avoid extra statements */
  1213. return !is_node ? line : 0;
  1214. }
  1215. /*!
  1216. \brief Binary data to HEX
  1217. Allocated buffer should be freed by G_free().
  1218. \param nbytes number of bytes to allocate
  1219. \param wkb_data WKB data
  1220. \return allocated buffer with HEX data
  1221. */
  1222. char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
  1223. {
  1224. char *hex_data;
  1225. int i, nlow, nhigh;
  1226. static const char ach_hex[] = "0123456789ABCDEF";
  1227. hex_data = (char *)G_malloc(nbytes * 2 + 1);
  1228. hex_data[nbytes * 2] = '\0';
  1229. for (i = 0; i < nbytes; i++) {
  1230. nlow = wkb_data[i] & 0x0f;
  1231. nhigh = (wkb_data[i] & 0xf0) >> 4;
  1232. hex_data[i * 2] = ach_hex[nhigh];
  1233. hex_data[i * 2 + 1] = ach_hex[nlow];
  1234. }
  1235. return hex_data;
  1236. }
  1237. /*!
  1238. \brief Write point into WKB buffer
  1239. See OGRPoint::exportToWkb from GDAL/OGR library
  1240. \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
  1241. \param points feature geometry
  1242. \param with_z WITH_Z for 3D data
  1243. \param[out] nsize buffer size
  1244. \return allocated WKB buffer
  1245. \return NULL on error
  1246. */
  1247. unsigned char *point_to_wkb(int byte_order,
  1248. const struct line_pnts *points, int with_z,
  1249. int *nsize)
  1250. {
  1251. unsigned char *wkb_data;
  1252. unsigned int sf_type;
  1253. if (points->n_points != 1)
  1254. return NULL;
  1255. /* allocate buffer */
  1256. *nsize = with_z ? 29 : 21;
  1257. wkb_data = G_malloc(*nsize);
  1258. G_zero(wkb_data, *nsize);
  1259. G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
  1260. /* set the byte order */
  1261. if (byte_order == ENDIAN_LITTLE)
  1262. wkb_data[0] = '\001';
  1263. else
  1264. wkb_data[0] = '\000';
  1265. /* set the geometry feature type */
  1266. sf_type = with_z ? SF_POINT25D : SF_POINT;
  1267. if (byte_order == ENDIAN_LITTLE)
  1268. sf_type = LSBWORD32(sf_type);
  1269. else
  1270. sf_type = MSBWORD32(sf_type);
  1271. memcpy(wkb_data + 1, &sf_type, 4);
  1272. /* copy in the raw data */
  1273. memcpy(wkb_data + 5, &(points->x[0]), 8);
  1274. memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
  1275. if (with_z) {
  1276. memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
  1277. }
  1278. /* swap if needed */
  1279. if (byte_order == ENDIAN_BIG) {
  1280. SWAPDOUBLE(wkb_data + 5);
  1281. SWAPDOUBLE(wkb_data + 5 + 8);
  1282. if (with_z)
  1283. SWAPDOUBLE(wkb_data + 5 + 16);
  1284. }
  1285. return wkb_data;
  1286. }
  1287. /*!
  1288. \bried Write linestring into WKB buffer
  1289. See OGRLineString::exportToWkb from GDAL/OGR library
  1290. \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
  1291. \param points feature geometry
  1292. \param with_z WITH_Z for 3D data
  1293. \param[out] nsize buffer size
  1294. \return allocated WKB buffer
  1295. \return NULL on error
  1296. */
  1297. unsigned char *linestring_to_wkb(int byte_order,
  1298. const struct line_pnts *points, int with_z,
  1299. int *nsize)
  1300. {
  1301. int i, point_size;
  1302. unsigned char *wkb_data;
  1303. unsigned int sf_type;
  1304. if (points->n_points < 1)
  1305. return NULL;
  1306. /* allocate buffer */
  1307. point_size = 8 * (with_z ? 3 : 2);
  1308. *nsize = 5 + 4 + points->n_points * point_size;
  1309. wkb_data = G_malloc(*nsize);
  1310. G_zero(wkb_data, *nsize);
  1311. G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
  1312. /* set the byte order */
  1313. if (byte_order == ENDIAN_LITTLE)
  1314. wkb_data[0] = '\001';
  1315. else
  1316. wkb_data[0] = '\000';
  1317. /* set the geometry feature type */
  1318. sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
  1319. if (byte_order == ENDIAN_LITTLE)
  1320. sf_type = LSBWORD32(sf_type);
  1321. else
  1322. sf_type = MSBWORD32(sf_type);
  1323. memcpy(wkb_data + 1, &sf_type, 4);
  1324. /* copy in the data count */
  1325. memcpy(wkb_data + 5, &(points->n_points), 4);
  1326. /* copy in the raw data */
  1327. for (i = 0; i < points->n_points; i++) {
  1328. memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
  1329. memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
  1330. if (with_z) {
  1331. memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
  1332. }
  1333. }
  1334. /* swap if needed */
  1335. if (byte_order == ENDIAN_BIG) {
  1336. int npoints, nitems;
  1337. npoints = SWAP32(points->n_points);
  1338. memcpy(wkb_data + 5, &npoints, 4);
  1339. nitems = (with_z ? 3 : 2) * points->n_points;
  1340. for (i = 0; i < nitems; i++) {
  1341. SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
  1342. }
  1343. }
  1344. return wkb_data;
  1345. }
  1346. /*!
  1347. \bried Write polygon into WKB buffer
  1348. See OGRPolygon::exportToWkb from GDAL/OGR library
  1349. \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
  1350. \param ipoints list of ring geometries (first is outer ring)
  1351. \param nrings number of rings
  1352. \param with_z WITH_Z for 3D data
  1353. \param[out] nsize buffer size
  1354. \return allocated WKB buffer
  1355. \return NULL on error
  1356. */
  1357. unsigned char *polygon_to_wkb(int byte_order,
  1358. const struct line_pnts** points, int nrings,
  1359. int with_z, int *nsize)
  1360. {
  1361. int i, ring, point_size, offset;
  1362. unsigned char *wkb_data;
  1363. unsigned int sf_type;
  1364. /* check data validity */
  1365. if (nrings < 1)
  1366. return NULL;
  1367. for (ring = 0; ring < nrings; ring++) {
  1368. if (points[ring]->n_points < 3)
  1369. return NULL;
  1370. }
  1371. /* allocate buffer */
  1372. point_size = 8 * (with_z ? 3 : 2);
  1373. *nsize = 9;
  1374. for (ring = 0; ring < nrings; ring++)
  1375. *nsize += 4 + point_size * points[ring]->n_points;
  1376. wkb_data = G_malloc(*nsize);
  1377. G_zero(wkb_data, *nsize);
  1378. G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
  1379. /* set the byte order */
  1380. if (byte_order == ENDIAN_LITTLE)
  1381. wkb_data[0] = '\001';
  1382. else
  1383. wkb_data[0] = '\000';
  1384. /* set the geometry feature type */
  1385. sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
  1386. if (byte_order == ENDIAN_LITTLE)
  1387. sf_type = LSBWORD32(sf_type);
  1388. else
  1389. sf_type = MSBWORD32(sf_type);
  1390. memcpy(wkb_data + 1, &sf_type, 4);
  1391. /* copy in the raw data */
  1392. if (byte_order == ENDIAN_BIG) {
  1393. int ncount;
  1394. ncount = SWAP32(nrings);
  1395. memcpy(wkb_data + 5, &ncount, 4);
  1396. }
  1397. else {
  1398. memcpy(wkb_data + 5, &nrings, 4);
  1399. }
  1400. /* serialize rings */
  1401. offset = 9;
  1402. for (ring = 0; ring < nrings; ring++) {
  1403. memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
  1404. for (i = 0; i < points[ring]->n_points; i++) {
  1405. memcpy(wkb_data + offset +
  1406. 4 + point_size * i, &(points[ring]->x[i]), 8);
  1407. memcpy(wkb_data + offset +
  1408. 4 + 8 + point_size * i, &(points[ring]->y[i]), 8);
  1409. if (with_z) {
  1410. memcpy(wkb_data + offset +
  1411. 4 + 16 + point_size * i, &(points[ring]->z[i]), 8);
  1412. }
  1413. }
  1414. offset += 4 + point_size * points[ring]->n_points;
  1415. /* swap if needed */
  1416. if (byte_order == ENDIAN_BIG) {
  1417. int npoints, nitems;
  1418. npoints = SWAP32(points[ring]->n_points);
  1419. memcpy(wkb_data + 5, &npoints, 4);
  1420. nitems = (with_z ? 3 : 2) * points[ring]->n_points;
  1421. for (i = 0; i < nitems; i++) {
  1422. SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
  1423. }
  1424. }
  1425. }
  1426. return wkb_data;
  1427. }
  1428. /*!
  1429. \brief Write feature to WKB buffer
  1430. Allocated string buffer should be freed by G_free().
  1431. \param pg_info pointer to Format_info_pg struct
  1432. \param points array of geometries which form feature
  1433. \param nparts number of geometries in array
  1434. \param type feature type (GV_POINT, GV_LINE, ...)
  1435. \param with_z WITH_Z for 3D data
  1436. \return allocated string buffer
  1437. \return NULL on error
  1438. */
  1439. char *line_to_wkb(struct Format_info_pg *pg_info,
  1440. const struct line_pnts **points, int nparts, int type, int with_z)
  1441. {
  1442. int byte_order, nbytes, nsize;
  1443. unsigned int sf_type;
  1444. unsigned char *wkb_data;
  1445. char *text_data, *text_data_p, *hex_data;
  1446. byte_order = dig__byte_order_out();
  1447. /* get wkb data */
  1448. nbytes = -1;
  1449. wkb_data = NULL;
  1450. if (type & GV_POINTS) /* point or centroid */
  1451. wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
  1452. else if (type == GV_LINE ||
  1453. (type == GV_BOUNDARY && pg_info->feature_type == SF_LINESTRING))
  1454. wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
  1455. else if (type & (GV_BOUNDARY | GV_FACE | GV_AREA)) {
  1456. if (!pg_info->toposchema_name || type == GV_AREA) {
  1457. /* PostGIS simple feature access */
  1458. wkb_data = polygon_to_wkb(byte_order, points, nparts,
  1459. with_z, &nbytes);
  1460. }
  1461. else {
  1462. /* PostGIS topology access */
  1463. wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
  1464. }
  1465. }
  1466. if (!wkb_data || nbytes < 1) {
  1467. G_warning(_("Unsupported feature type %d"), type);
  1468. return NULL;
  1469. }
  1470. /* When converting to hex, each byte takes 2 hex characters. In
  1471. addition we add in 8 characters to represent the SRID integer
  1472. in hex, and one for a null terminator */
  1473. nsize = nbytes * 2 + 8 + 1;
  1474. text_data = text_data_p = (char *)G_malloc(nsize);
  1475. /* convert the 1st byte, which is the endianess flag, to hex */
  1476. hex_data = binary_to_hex(1, wkb_data);
  1477. strcpy(text_data_p, hex_data);
  1478. G_free(hex_data);
  1479. text_data_p += 2;
  1480. /* get the geom type which is bytes 2 through 5 */
  1481. memcpy(&sf_type, wkb_data + 1, 4);
  1482. /* add the SRID flag if an SRID is provided */
  1483. if (pg_info->srid > 0) {
  1484. unsigned int srs_flag;
  1485. /* change the flag to little endianess */
  1486. srs_flag = LSBWORD32(WKBSRIDFLAG);
  1487. /* apply the flag */
  1488. sf_type = sf_type | srs_flag;
  1489. }
  1490. /* write the geom type which is 4 bytes */
  1491. hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
  1492. strcpy(text_data_p, hex_data);
  1493. G_free(hex_data);
  1494. text_data_p += 8;
  1495. /* include SRID if provided */
  1496. if (pg_info->srid > 0) {
  1497. unsigned int srs_id;
  1498. /* force the srsid to little endianess */
  1499. srs_id = LSBWORD32(pg_info->srid);
  1500. hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
  1501. strcpy(text_data_p, hex_data);
  1502. G_free(hex_data);
  1503. text_data_p += 8;
  1504. }
  1505. /* copy the rest of the data over - subtract 5 since we already
  1506. copied 5 bytes above */
  1507. hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
  1508. strcpy(text_data_p, hex_data);
  1509. G_free(hex_data);
  1510. return text_data;
  1511. }
  1512. /*!
  1513. \brief Insert feature into table
  1514. \param Map pointer to Map_info structure
  1515. \param line feature id (topo access only)
  1516. \param type feature type (GV_POINT, GV_LINE, ...)
  1517. \param points pointer to line_pnts struct
  1518. \param nparts number of parts (rings for polygon)
  1519. \param cat category number (-1 for no category)
  1520. \return topo_id for PostGIS Topology
  1521. \return 0 for simple features access
  1522. \return -1 on error
  1523. */
  1524. int write_feature(struct Map_info *Map, int line, int type,
  1525. const struct line_pnts **points, int nparts, int cat)
  1526. {
  1527. int with_z, topo_id;
  1528. char *stmt, *geom_data;
  1529. struct Format_info_pg *pg_info;
  1530. pg_info = &(Map->fInfo.pg);
  1531. with_z = Map->head.with_z;
  1532. if (with_z && pg_info->coor_dim != 3) {
  1533. G_warning(_("Trying to insert 3D data into feature table "
  1534. "which store 2D data only"));
  1535. return -1;
  1536. }
  1537. if (!with_z && pg_info->coor_dim != 2) {
  1538. G_warning(_("Trying to insert 2D data into feature table "
  1539. "which store 3D data only"));
  1540. return -1;
  1541. }
  1542. /* build WKB geometry from line_pnts structures */
  1543. geom_data = line_to_wkb(pg_info, points, nparts, type, with_z);
  1544. if (!geom_data)
  1545. return -1;
  1546. /* start transaction */
  1547. if (!pg_info->inTransaction) {
  1548. pg_info->inTransaction = TRUE;
  1549. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
  1550. G_free(geom_data);
  1551. return -1;
  1552. }
  1553. }
  1554. /* write feature in PostGIS topology schema if enabled */
  1555. topo_id = -1;
  1556. if (pg_info->toposchema_name) {
  1557. /* insert feature into topology schema (node or edge) */
  1558. topo_id = insert_topo_element(Map, line, type, geom_data);
  1559. if (topo_id < 0) {
  1560. G_warning(_("Unable to insert topological element into PostGIS Topology schema"));
  1561. G_free(geom_data);
  1562. return -1;
  1563. }
  1564. if (pg_info->feature_type != SF_POLYGON) {
  1565. /* define relation */
  1566. Vect__define_topo_relation(pg_info, topo_id, topo_id);
  1567. }
  1568. }
  1569. /* build INSERT statement
  1570. simple feature geometry + attributes
  1571. */
  1572. stmt = build_insert_stmt(pg_info, geom_data, topo_id, cat);
  1573. /* stmt can NULL when writing PostGIS topology with no attributes
  1574. * attached */
  1575. if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1576. /* rollback transaction */
  1577. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1578. G_free(geom_data);
  1579. G_free(stmt);
  1580. return -1;
  1581. }
  1582. G_free(geom_data);
  1583. G_free(stmt);
  1584. return pg_info->toposchema_name ? topo_id : 0;
  1585. }
  1586. /*!
  1587. \brief Build INSERT statement to add new feature to the feature
  1588. table
  1589. Note: Allocated string should be freed.
  1590. \param pg_info pointer to Format_info_pg structure
  1591. \param geom_data geometry data
  1592. \param type feature type (GV_POINT, GV_LINE, ...) - (only for PostGIS Topology)
  1593. \param id topology element id (only for PostGIS Topology)
  1594. \param cat category number (or -1 for no category)
  1595. \param Fi pointer to field_info structure (NULL for no attributes)
  1596. \return allocated string with INSERT statement
  1597. */
  1598. char *build_insert_stmt(const struct Format_info_pg *pg_info,
  1599. const char *geom_data, int topo_id, int cat)
  1600. {
  1601. int topogeom_type;
  1602. char *stmt, buf[DB_SQL_MAX];
  1603. struct field_info *Fi;
  1604. topogeom_type = -1;
  1605. if (pg_info->toposchema_name) {
  1606. topogeom_type = type_to_topogeom(pg_info);
  1607. if (topogeom_type < 0)
  1608. return NULL;
  1609. }
  1610. Fi = pg_info->fi;
  1611. stmt = NULL;
  1612. if (Fi && cat > -1) {
  1613. /* write attributes (simple features and topology elements) */
  1614. int col, ncol, more;
  1615. int sqltype, ctype, is_fid;
  1616. char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
  1617. const char *colname;
  1618. dbString dbstmt;
  1619. dbCursor cursor;
  1620. dbTable *table;
  1621. dbColumn *column;
  1622. dbValue *value;
  1623. db_init_string(&dbstmt);
  1624. buf_val[0] = '\0';
  1625. /* read & set attributes */
  1626. sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
  1627. cat);
  1628. G_debug(4, "SQL: %s", buf);
  1629. db_set_string(&dbstmt, buf);
  1630. /* prepare INSERT statement */
  1631. sprintf(buf, "INSERT INTO \"%s\".\"%s\" (",
  1632. pg_info->schema_name, pg_info->table_name);
  1633. /* select data */
  1634. if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
  1635. &cursor, DB_SEQUENTIAL) != DB_OK) {
  1636. G_warning(_("Unable to select attributes for category %d"), cat);
  1637. }
  1638. else {
  1639. if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
  1640. G_warning(_("Unable to fetch data from table <%s>"),
  1641. Fi->table);
  1642. }
  1643. if (!more) {
  1644. G_warning(_("No database record for category %d, "
  1645. "no attributes will be written"), cat);
  1646. }
  1647. else {
  1648. table = db_get_cursor_table(&cursor);
  1649. ncol = db_get_table_number_of_columns(table);
  1650. for (col = 0; col < ncol; col++) {
  1651. column = db_get_table_column(table, col);
  1652. colname = db_get_column_name(column);
  1653. /* -> values */
  1654. value = db_get_column_value(column);
  1655. /* for debug only */
  1656. db_convert_column_value_to_string(column, &dbstmt);
  1657. G_debug(3, "col %d : val = %s", col,
  1658. db_get_string(&dbstmt));
  1659. sqltype = db_get_column_sqltype(column);
  1660. ctype = db_sqltype_to_Ctype(sqltype);
  1661. is_fid = strcmp(pg_info->fid_column, colname) == 0;
  1662. /* check fid column (must be integer) */
  1663. if (is_fid == TRUE &&
  1664. ctype != DB_C_TYPE_INT) {
  1665. G_warning(_("FID column must be integer, column <%s> ignored!"),
  1666. colname);
  1667. continue;
  1668. }
  1669. /* -> columns */
  1670. sprintf(buf_tmp, "\"%s\"", colname);
  1671. strcat(buf, buf_tmp);
  1672. if (col < ncol - 1)
  1673. strcat(buf, ",");
  1674. /* prevent writing NULL values */
  1675. if (!db_test_value_isnull(value)) {
  1676. switch (ctype) {
  1677. case DB_C_TYPE_INT:
  1678. sprintf(buf_tmp, "%d", db_get_value_int(value));
  1679. break;
  1680. case DB_C_TYPE_DOUBLE:
  1681. sprintf(buf_tmp, "%.14f",
  1682. db_get_value_double(value));
  1683. break;
  1684. case DB_C_TYPE_STRING: {
  1685. char *value_tmp;
  1686. value_tmp = G_str_replace(db_get_value_string(value), "'", "''");
  1687. sprintf(buf_tmp, "'%s'", value_tmp);
  1688. G_free(value_tmp);
  1689. break;
  1690. }
  1691. case DB_C_TYPE_DATETIME:
  1692. db_convert_column_value_to_string(column,
  1693. &dbstmt);
  1694. sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
  1695. break;
  1696. default:
  1697. G_warning(_("Unsupported column type %d"), ctype);
  1698. sprintf(buf_tmp, "NULL");
  1699. break;
  1700. }
  1701. }
  1702. else {
  1703. if (is_fid == TRUE)
  1704. G_warning(_("Invalid value for FID column: NULL"));
  1705. sprintf(buf_tmp, "NULL");
  1706. }
  1707. strcat(buf_val, buf_tmp);
  1708. if (col < ncol - 1)
  1709. strcat(buf_val, ",");
  1710. }
  1711. if (!pg_info->toposchema_name) {
  1712. /* simple feature access */
  1713. G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)",
  1714. buf, pg_info->geom_column, buf_val, geom_data);
  1715. }
  1716. else {
  1717. /* PostGIS topology access, write geometry in
  1718. * topology schema, skip geometry at this point */
  1719. if (buf[strlen(buf)-1] == ',') { /* last column skipped */
  1720. buf[strlen(buf)-1] = '\0';
  1721. buf_val[strlen(buf_val)-1] = '\0';
  1722. }
  1723. G_asprintf(&stmt, "%s, %s) VALUES (%s, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
  1724. buf, pg_info->topogeom_column, buf_val,
  1725. pg_info->toposchema_id, topo_id, topogeom_type);
  1726. }
  1727. }
  1728. }
  1729. }
  1730. else {
  1731. /* no attributes */
  1732. if (!pg_info->toposchema_name) {
  1733. /* no attributes (simple features access) */
  1734. if (cat > 0) {
  1735. /* cetegory defined */
  1736. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
  1737. "(%d, '%s'::GEOMETRY)",
  1738. pg_info->schema_name, pg_info->table_name,
  1739. GV_KEY_COLUMN, pg_info->geom_column,
  1740. cat, geom_data);
  1741. }
  1742. else {
  1743. /* no category */
  1744. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
  1745. "('%s'::GEOMETRY)",
  1746. pg_info->schema_name, pg_info->table_name,
  1747. pg_info->geom_column, geom_data);
  1748. }
  1749. }
  1750. else {
  1751. if (cat > 0) {
  1752. /* no attributes (topology elements) */
  1753. G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
  1754. "(%d, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
  1755. pg_info->schema_name, pg_info->table_name,
  1756. GV_KEY_COLUMN, pg_info->topogeom_column, cat,
  1757. pg_info->toposchema_id, topo_id, topogeom_type);
  1758. }
  1759. }
  1760. }
  1761. return stmt;
  1762. }
  1763. /*!
  1764. \brief Insert topological element into 'node' or 'edge' table
  1765. Negative id for nodes, 0 for next value.
  1766. \param Map pointer to Map_info struct
  1767. \param id feature id (0 for next val)
  1768. \param type feature type (GV_POINT, GV_LINE, ...)
  1769. \param geom_data geometry in wkb
  1770. \return new topo id
  1771. \return -1 on error
  1772. */
  1773. int insert_topo_element(struct Map_info *Map, int id, int type,
  1774. const char *geom_data)
  1775. {
  1776. int ret, topo_id;
  1777. char *stmt, stmt_id[DB_SQL_MAX];
  1778. struct Format_info_pg *pg_info;
  1779. struct Plus_head *plus;
  1780. struct P_line *Line;
  1781. pg_info = &(Map->fInfo.pg);
  1782. plus = &(Map->plus);
  1783. Line = NULL;
  1784. if (plus->built >= GV_BUILD_BASE) {
  1785. if (id > 0) { /* -> feature */
  1786. topo_id = id;
  1787. if (topo_id > Map->plus.n_lines) {
  1788. G_warning(_("Invalid feature %d (max: %d)"), topo_id, Map->plus.n_lines);
  1789. return -1;
  1790. }
  1791. Line = Map->plus.Line[topo_id];
  1792. if (Line->type & GV_POINTS) {
  1793. /* set topo_id for points */
  1794. topo_id = Vect_get_num_primitives(Map, GV_POINTS) + Vect_get_num_nodes(Map);
  1795. }
  1796. }
  1797. else if (id < 0) { /* node */
  1798. topo_id = abs(id);
  1799. if (type != GV_POINT) {
  1800. G_warning(_("Invalid feature type (%d) for node"), type);
  1801. return -1;
  1802. }
  1803. if (topo_id > Map->plus.n_nodes) {
  1804. G_warning(_("Invalid node %d (%d)"), topo_id, Map->plus.n_nodes);
  1805. return -1;
  1806. }
  1807. /* increment topo_id - also points and centroids are
  1808. * stored in 'node' table */
  1809. topo_id += Vect_get_num_primitives(Map, GV_POINTS);
  1810. }
  1811. }
  1812. stmt = NULL;
  1813. switch(type) {
  1814. case GV_POINT: {
  1815. /* insert new node */
  1816. #if USE_TOPO_STMT
  1817. G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
  1818. pg_info->toposchema_name, geom_data);
  1819. #else
  1820. if (id == 0) {
  1821. /* get node_id */
  1822. sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
  1823. pg_info->toposchema_name);
  1824. topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
  1825. }
  1826. /* build insert statement */
  1827. G_asprintf(&stmt, "INSERT INTO \"%s\".node (node_id, geom) VALUES "
  1828. "(%d, '%s'::GEOMETRY)", pg_info->toposchema_name, topo_id, geom_data);
  1829. #endif
  1830. break;
  1831. }
  1832. case GV_LINE:
  1833. case GV_BOUNDARY: {
  1834. /* insert new edge */
  1835. #if USE_TOPO_STMT
  1836. G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
  1837. pg_info->toposchema_name, geom_data);
  1838. #else
  1839. int n1, n2, nle, nre;
  1840. struct Format_info_offset *offset;
  1841. offset = &(pg_info->offset);
  1842. if (id == 0) {
  1843. /* get edge_id */
  1844. sprintf(stmt_id, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
  1845. pg_info->toposchema_name);
  1846. topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
  1847. }
  1848. nle = -topo_id; /* assuming isolated lines */
  1849. nre = topo_id;
  1850. if (Line) {
  1851. int i, n, next_edge;
  1852. struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
  1853. topo_id = (int)Line->offset;
  1854. /* start & end node */
  1855. n1 = topo->N1;
  1856. n2 = topo->N2;
  1857. /* next left & right edge */
  1858. for (i = 0; i < 2; i++) {
  1859. n = Vect_get_node_n_lines(Map, i == 0 ? n1 : n2);
  1860. if (n < 2) /* no connection */
  1861. continue;
  1862. next_edge = update_next_edge(Map, n,
  1863. i == 0 ? topo_id : -topo_id);
  1864. if (next_edge != 0) {
  1865. if (i == 0)
  1866. nre = next_edge; /* update next right edge for start node */
  1867. else
  1868. nle = next_edge; /* update next left edge for end node */
  1869. }
  1870. else {
  1871. G_warning(_("Unable to determine next left/right edge for edge %d"), topo_id);
  1872. }
  1873. }
  1874. }
  1875. else {
  1876. G_warning(_("Unable to insert new edge. Topology not available."));
  1877. return -1;
  1878. }
  1879. G_debug(3, "new edge: id=%d next_left_edge=%d next_right_edge=%d",
  1880. topo_id, nle, nre);
  1881. if (n1 > offset->array_num || n2 > offset->array_num) /* node id starts at 1 */
  1882. return -1;
  1883. /* build insert statement */
  1884. G_asprintf(&stmt, "INSERT INTO \"%s\".edge_data (edge_id, start_node, end_node, "
  1885. "next_left_edge, abs_next_left_edge, next_right_edge, abs_next_right_edge, "
  1886. "left_face, right_face, geom) VALUES "
  1887. "(%d, %d, %d, %d, %d, %d, %d, 0, 0, '%s'::GEOMETRY)",
  1888. pg_info->toposchema_name, topo_id, offset->array[n1-1], offset->array[n2-1],
  1889. nle, abs(nle), nre, abs(nre), geom_data);
  1890. #endif
  1891. break;
  1892. }
  1893. case GV_CENTROID: {
  1894. /* insert new node (with containing_face) */
  1895. #if USE_TOPO_STMT
  1896. G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
  1897. pg_info->toposchema_name, geom_data);
  1898. #else
  1899. int area;
  1900. if (id == 0) {
  1901. /* get node_id */
  1902. sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
  1903. pg_info->toposchema_name);
  1904. topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
  1905. }
  1906. if (Line) {
  1907. struct P_topo_c *topo = (struct P_topo_c *) Line->topo;
  1908. area = topo->area;
  1909. }
  1910. else {
  1911. area = 0;
  1912. }
  1913. G_asprintf(&stmt, "INSERT INTO \"%s\".node (node_id, containing_face, geom) VALUES "
  1914. "(%d, %d, '%s'::GEOMETRY)", pg_info->toposchema_name,
  1915. topo_id, area, geom_data);
  1916. #endif
  1917. break;
  1918. }
  1919. default:
  1920. G_warning(_("Unsupported feature type %d"), type);
  1921. break;
  1922. }
  1923. /* execute insert statement */
  1924. ret = Vect__execute_pg(pg_info->conn, stmt);
  1925. G_free(stmt);
  1926. if (ret == -1) {
  1927. /* rollback transaction */
  1928. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1929. return -1;
  1930. }
  1931. return topo_id;
  1932. }
  1933. int type_to_topogeom(const struct Format_info_pg *pg_info)
  1934. {
  1935. int topogeom_type;
  1936. topogeom_type = -1;
  1937. switch (pg_info->feature_type) {
  1938. case SF_POINT:
  1939. topogeom_type = 1;
  1940. break;
  1941. case SF_LINESTRING:
  1942. topogeom_type = 2;
  1943. break;
  1944. case SF_POLYGON:
  1945. topogeom_type = 3;
  1946. break;
  1947. default:
  1948. G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
  1949. }
  1950. return topogeom_type;
  1951. }
  1952. int Vect__define_topo_relation(const struct Format_info_pg *pg_info, int topo_id, int element_id)
  1953. {
  1954. int topogeom_type;
  1955. char stmt[DB_SQL_MAX];
  1956. topogeom_type = type_to_topogeom(pg_info);
  1957. if (topogeom_type < 0)
  1958. return -1;
  1959. sprintf(stmt, "INSERT into \"%s\".relation VALUES(%d, 1, %d, %d)",
  1960. pg_info->toposchema_name, topo_id, element_id, topogeom_type);
  1961. G_debug(3, "SQL: %s", stmt);
  1962. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1963. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1964. return -1;
  1965. }
  1966. return 0;
  1967. }
  1968. /*!
  1969. \brief Find next line (topo only)
  1970. \param Map pointer to Map_info struct
  1971. \param nlines number of lines
  1972. \param line current line
  1973. \param[out] left left line
  1974. \param[out] right right line
  1975. \return left (line < 0) or right (line > 0) next edge
  1976. \return 0 on failure
  1977. */
  1978. int update_next_edge(struct Map_info* Map, int nlines, int line)
  1979. {
  1980. int ret, next_line, edge;
  1981. char stmt[DB_SQL_MAX];
  1982. const struct Format_info_pg *pg_info;
  1983. struct P_line *Line_next, *Line;
  1984. Line = Line_next = NULL;
  1985. pg_info = &(Map->fInfo.pg);
  1986. /* find next line
  1987. start node -> next on the left
  1988. end node -> next on the right
  1989. */
  1990. next_line = dig_angle_next_line(&(Map->plus), line, GV_LEFT, GV_LINES, NULL);
  1991. G_debug(3, "line=%d next_line=%d", line, next_line);
  1992. if (next_line == 0) {
  1993. G_warning(_("Invalid topology"));
  1994. return 0;
  1995. }
  1996. Line = Map->plus.Line[abs(line)];
  1997. Line_next = Map->plus.Line[abs(next_line)];
  1998. if (!Line || !Line_next) {
  1999. G_warning(_("Invalid topology"));
  2000. return 0;
  2001. }
  2002. if (line > 0) {
  2003. edge = Line->offset;
  2004. ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
  2005. }
  2006. else {
  2007. edge = -Line->offset;
  2008. ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
  2009. }
  2010. if (next_line < 0) {
  2011. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
  2012. "abs_next_left_edge = %d WHERE edge_id = %d AND abs_next_left_edge = %d",
  2013. pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset, (int)Line_next->offset);
  2014. G_debug(3, "update edge=%d next_left_edge=%d (?)", (int)Line_next->offset, edge);
  2015. }
  2016. else {
  2017. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
  2018. "abs_next_right_edge = %d WHERE edge_id = %d AND abs_next_right_edge = %d",
  2019. pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset, (int)Line_next->offset);
  2020. G_debug(3, "update edge=%d next_right_edge=%d (?)", (int)Line_next->offset, edge);
  2021. }
  2022. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2023. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2024. return 0;
  2025. }
  2026. if (nlines > 2) {
  2027. /* more lines connected to the node
  2028. start node -> next on the right
  2029. end node -> next on the left
  2030. */
  2031. next_line = dig_angle_next_line(&(Map->plus), line, GV_RIGHT, GV_LINES, NULL);
  2032. Line_next = Map->plus.Line[abs(next_line)];
  2033. if (next_line < 0) {
  2034. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
  2035. "abs_next_left_edge = %d WHERE edge_id = %d",
  2036. pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset);
  2037. G_debug(3, "update edge=%d next_left_edge=%d", (int)Line_next->offset, edge);
  2038. }
  2039. else {
  2040. sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
  2041. "abs_next_right_edge = %d WHERE edge_id = %d",
  2042. pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset);
  2043. G_debug(3, "update edge=%d next_right_edge=%d", (int)Line_next->offset, edge);
  2044. }
  2045. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2046. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2047. return 0;
  2048. }
  2049. }
  2050. return ret;
  2051. }
  2052. /*!
  2053. \brief Insert new face to the 'face' table (topo only)
  2054. \param Map pointer to Map_info struct
  2055. \param area area id (negative id for isles)
  2056. \return 0 on error
  2057. \return area id on success (>0)
  2058. */
  2059. int Vect__insert_face_pg(struct Map_info *Map, int area)
  2060. {
  2061. char *stmt;
  2062. struct Format_info_pg *pg_info;
  2063. struct bound_box box;
  2064. if (area == 0)
  2065. return 0; /* universal face has id '0' in PostGIS Topology */
  2066. stmt = NULL;
  2067. pg_info = &(Map->fInfo.pg);
  2068. /* check if face exists */
  2069. /* get mbr of the area */
  2070. if (area > 0)
  2071. Vect_get_area_box(Map, area, &box);
  2072. else
  2073. Vect_get_isle_box(Map, abs(area), &box);
  2074. /* insert face if not exists */
  2075. G_asprintf(&stmt, "INSERT INTO \"%s\".face (face_id, mbr) VALUES "
  2076. "(%d, ST_GeomFromText('POLYGON((%.12f %.12f, %.12f %.12f, %.12f %.12f, %.12f %.12f, "
  2077. "%.12f %.12f))', %d))", pg_info->toposchema_name, area,
  2078. box.W, box.S, box.W, box.N, box.E, box.N,
  2079. box.E, box.S, box.W, box.S, pg_info->srid);
  2080. G_debug(3, "new face id=%d", area);
  2081. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2082. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2083. return 0;
  2084. }
  2085. G_free(stmt);
  2086. return area;
  2087. }
  2088. /*!
  2089. \brief Delete existing face (currently unused)
  2090. \todo Set foreign keys as DEFERRABLE INITIALLY DEFERRED and use SET
  2091. CONSTRAINTS ALL DEFERRED
  2092. \param Map pointer to Map_info struct
  2093. \param area area id to delete
  2094. \return 0 on success
  2095. \return -1 on error
  2096. */
  2097. int delete_face(const struct Map_info *Map, int area)
  2098. {
  2099. char stmt[DB_SQL_MAX];
  2100. const struct Format_info_pg *pg_info;
  2101. pg_info = &(Map->fInfo.pg);
  2102. /* update centroids first */
  2103. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 "
  2104. "WHERE containing_face = %d",
  2105. pg_info->toposchema_name, area);
  2106. G_debug(3, "SQL: %s", stmt);
  2107. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2108. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2109. return -1;
  2110. }
  2111. /* update also edges (left face) */
  2112. sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0 "
  2113. "WHERE left_face = %d",
  2114. pg_info->toposchema_name, area);
  2115. G_debug(3, "SQL: %s", stmt);
  2116. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2117. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2118. return -1;
  2119. }
  2120. /* update also edges (left face) */
  2121. sprintf(stmt, "UPDATE \"%s\".edge_data SET right_face = 0 "
  2122. "WHERE right_face = %d",
  2123. pg_info->toposchema_name, area);
  2124. G_debug(3, "SQL: %s", stmt);
  2125. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2126. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2127. return -1;
  2128. }
  2129. /* delete face */
  2130. sprintf(stmt, "DELETE FROM \"%s\".face WHERE face_id = %d",
  2131. pg_info->toposchema_name, area);
  2132. G_debug(3, "delete face id=%d", area);
  2133. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2134. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2135. return -1;
  2136. }
  2137. return 0;
  2138. }
  2139. /*!
  2140. \brief Update lines (next left and right edges)
  2141. - isolated edges
  2142. next left edge: -edge
  2143. next right edge: edge
  2144. - connected edges
  2145. next left edge: next edge or -edge
  2146. next right edge: next edge or edge
  2147. \param Map pointer to Map_info struct
  2148. \param line feature id
  2149. \return 0 on success
  2150. \return -1 on error
  2151. */
  2152. int update_topo_edge(struct Map_info *Map, int line)
  2153. {
  2154. int i, n;
  2155. int nle, nre, next_edge;
  2156. char stmt[DB_SQL_MAX];
  2157. struct Format_info_pg *pg_info;
  2158. struct P_line *Line;
  2159. pg_info = &(Map->fInfo.pg);
  2160. if (line < 1 || line > Map->plus.n_lines) {
  2161. G_warning(_("Attempt to access non-existing feature %d"), line);
  2162. return -1;
  2163. }
  2164. Line = Map->plus.Line[line];
  2165. if (!Line) {
  2166. G_warning(_("Attempt to access dead feature %d"), line);
  2167. return -1;
  2168. }
  2169. struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
  2170. nre = nle = 0; /* edge = 0 is an illegal value */
  2171. /* check for line connection */
  2172. for (i = 0; i < 2; i++) {
  2173. /* first check start node then end node */
  2174. n = i == 0 ? Vect_get_node_n_lines(Map, topo->N1)
  2175. : Vect_get_node_n_lines(Map, topo->N2);
  2176. if (n < 2) /* no connection */
  2177. continue;
  2178. next_edge = update_next_edge(Map, n,
  2179. i == 0 ? line : -line);
  2180. if (next_edge != 0) {
  2181. if (i == 0)
  2182. nre = next_edge; /* update next right edge for start node */
  2183. else
  2184. nle = next_edge; /* update next left edge for end node */
  2185. }
  2186. else {
  2187. G_warning(_("Unable to determine next left/right edge"));
  2188. return -1;
  2189. }
  2190. }
  2191. if (nle == 0 && nre == 0) /* nothing changed */
  2192. return 0;
  2193. if (nle != 0 && nre != 0) {
  2194. /* update both next left and right edge */
  2195. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  2196. "next_left_edge = %d, abs_next_left_edge = %d, "
  2197. "next_right_edge = %d, abs_next_right_edge = %d "
  2198. "WHERE edge_id = %d", pg_info->toposchema_name,
  2199. nle, abs(nle), nre, abs(nre), (int)Line->offset);
  2200. }
  2201. else if (nle != 0) {
  2202. /* update next left edge only */
  2203. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  2204. "next_left_edge = %d, abs_next_left_edge = %d "
  2205. "WHERE edge_id = %d", pg_info->toposchema_name,
  2206. nle, abs(nle), (int)Line->offset);
  2207. }
  2208. else {
  2209. /* update next right edge only */
  2210. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  2211. "next_right_edge = %d, abs_next_right_edge = %d "
  2212. "WHERE edge_id = %d", pg_info->toposchema_name,
  2213. nre, abs(nre), (int)Line->offset);
  2214. }
  2215. G_debug(3, "update edge=%d next_left_edge=%d next_right_edge=%d",
  2216. (int)Line->offset, nle, nre);
  2217. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2218. /* rollback transaction */
  2219. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2220. return -1;
  2221. }
  2222. return 0;
  2223. }
  2224. /*!
  2225. \brief Update lines (left and right faces)
  2226. TODO: handle isles
  2227. \param Map pointer to Map_info struct
  2228. \param line feature id
  2229. \return 0 on success
  2230. \return -1 on error
  2231. */
  2232. int update_topo_face(struct Map_info *Map, int line)
  2233. {
  2234. int i, s, area, face[2];
  2235. char stmt[DB_SQL_MAX];
  2236. struct Format_info_pg *pg_info;
  2237. struct P_line *Line, *Line_i;
  2238. struct P_area *Area;
  2239. struct P_topo_b *topo, *topo_i;
  2240. pg_info = &(Map->fInfo.pg);
  2241. if (line < 1 || line > Map->plus.n_lines) {
  2242. G_warning(_("Attempt to access non-existing feature %d"), line);
  2243. return -1;
  2244. }
  2245. Line = Map->plus.Line[line];
  2246. if (!Line) {
  2247. G_warning(_("Attempt to access dead feature %d"), line);
  2248. return -1;
  2249. }
  2250. topo = (struct P_topo_b *)Line->topo;
  2251. /* for both side on the current boundary (line) */
  2252. /* create new faces */
  2253. for (s = 0; s < 2; s++) { /* for each side */
  2254. area = s == 0 ? topo->left : topo->right;
  2255. if (area <= 0) /* no area - skip */
  2256. continue;
  2257. face[s] = Vect__insert_face_pg(Map, area);
  2258. if (face[s] < 1) {
  2259. G_warning(_("Unable to create new face"));
  2260. return -1;
  2261. }
  2262. }
  2263. /* update edges forming faces */
  2264. for (s = 0; s < 2; s++) { /* for each side */
  2265. area = s == 0 ? topo->left : topo->right;
  2266. if (area <= 0) /* no area - skip */
  2267. continue;
  2268. Area = Map->plus.Area[area];
  2269. for (i = 0; i < Area->n_lines; i++) {
  2270. Line_i = Map->plus.Line[abs(Area->lines[i])];
  2271. topo_i = (struct P_topo_b *)Line_i->topo;
  2272. sprintf(stmt, "UPDATE \"%s\".edge_data SET "
  2273. "left_face = %d, right_face = %d "
  2274. "WHERE edge_id = %d", pg_info->toposchema_name,
  2275. topo_i->left > 0 ? topo_i->left : 0,
  2276. topo_i->right > 0 ? topo_i->right : 0,
  2277. (int) Line_i->offset);
  2278. G_debug(2, "SQL: %s", stmt);
  2279. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2280. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2281. return -1;
  2282. }
  2283. }
  2284. /* update also centroids (stored as nodes) */
  2285. if (Area->centroid > 0) {
  2286. Line_i = Map->plus.Line[Area->centroid];
  2287. sprintf(stmt, "UPDATE \"%s\".node SET containing_face = %d "
  2288. "WHERE node_id = %d", pg_info->toposchema_name,
  2289. face[s], (int)Line_i->offset);
  2290. G_debug(2, "SQL: %s", stmt);
  2291. if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2292. /* rollback transaction */
  2293. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2294. return -1;
  2295. }
  2296. }
  2297. }
  2298. return 0;
  2299. }
  2300. /*!
  2301. \brief Add line to native and PostGIS topology
  2302. \param Map vector map
  2303. \param offset ???
  2304. \param type feature type
  2305. \param Points feature vertices
  2306. \return feature id
  2307. \return -1 on error
  2308. */
  2309. int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
  2310. const struct line_pnts *points)
  2311. {
  2312. int line, n_nodes;
  2313. struct Plus_head *plus;
  2314. plus = &(Map->plus);
  2315. Vect_reset_updated(Map);
  2316. line = V2__add_line_to_topo_nat(Map, offset, type, points, NULL,
  2317. -1, NULL);
  2318. /* insert new nodes into 'node' table */
  2319. n_nodes = Vect_get_num_updated_nodes(Map);
  2320. if (n_nodes > 0) {
  2321. int i, node;
  2322. double x, y, z;
  2323. if (!Points)
  2324. Points = Vect_new_line_struct();
  2325. for (i = 0; i < n_nodes; i++) {
  2326. node = Vect_get_updated_node(Map, i);
  2327. /* skip updated and deleted nodes */
  2328. if (node > 0 || plus->Node[abs(node)] == NULL)
  2329. continue;
  2330. G_debug(3, " new node: %d", node);
  2331. Vect_get_node_coor(Map, abs(node), &x, &y, &z);
  2332. Vect_reset_line(Points);
  2333. Vect_append_point(Points, x, y, z);
  2334. write_feature(Map, node, GV_POINT, (const struct line_pnts **) &Points, 1, -1);
  2335. }
  2336. }
  2337. return line;
  2338. }
  2339. /*!
  2340. \brief Delete line from native and PostGIS topology
  2341. \param Map vector map
  2342. \param line feature id to remove from topo
  2343. \param type feature type
  2344. \param Points feature vertices
  2345. \return 0 on success
  2346. \return -1 on error
  2347. */
  2348. int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
  2349. const struct line_pnts *Points)
  2350. {
  2351. int N1, N2, node_id;
  2352. char stmt[DB_SQL_MAX];
  2353. struct Format_info_pg *pg_info;
  2354. struct P_node *Node;
  2355. pg_info = &(Map->fInfo.pg);
  2356. Vect_reset_updated(Map);
  2357. if (!(type & GV_LINES))
  2358. return 0;
  2359. Vect_get_line_nodes(Map, line, &N1, &N2);
  2360. if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
  2361. return -1;
  2362. Node = Map->plus.Node[N1];
  2363. if (!Node || Node->n_lines == 0) {
  2364. node_id = pg_info->offset.array[N1-1];
  2365. sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
  2366. pg_info->toposchema_name, node_id);
  2367. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2368. G_warning(_("Unable to delete node %d"), node_id);
  2369. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2370. return -1;
  2371. }
  2372. }
  2373. Node = Map->plus.Node[N2];
  2374. if (!Node || Node->n_lines == 0) {
  2375. node_id = pg_info->offset.array[N2-1];
  2376. sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
  2377. pg_info->toposchema_name, node_id);
  2378. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  2379. G_warning(_("Unable to delete node %d"), node_id);
  2380. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2381. return -1;
  2382. }
  2383. }
  2384. return 0;
  2385. }
  2386. int set_constraint_to_deferrable(struct Format_info_pg *pg_info,
  2387. const char *table, const char *constraint,
  2388. const char *column, const char *ref_table,
  2389. const char *ref_column)
  2390. {
  2391. char stmt[DB_SQL_MAX];
  2392. sprintf(stmt, "ALTER TABLE \"%s\".%s DROP CONSTRAINT %s",
  2393. pg_info->toposchema_name, table, constraint);
  2394. if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
  2395. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2396. return -1;
  2397. }
  2398. sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT %s "
  2399. "FOREIGN KEY (%s) REFERENCES \"%s\".%s (%s) "
  2400. "DEFERRABLE INITIALLY DEFERRED",
  2401. pg_info->toposchema_name, table, constraint, column,
  2402. pg_info->toposchema_name, ref_table, ref_column);
  2403. if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
  2404. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  2405. return -1;
  2406. }
  2407. return 0;
  2408. }
  2409. /*!
  2410. \brief Open database connection with attribute table
  2411. \param pg_info pointer to Format_info_pg struct
  2412. \return pointer to dbDriver on succes
  2413. \return NULL on failure
  2414. */
  2415. dbDriver *open_db(struct Format_info_pg *pg_info) {
  2416. dbDriver *driver;
  2417. dbHandle handle;
  2418. struct field_info *Fi;
  2419. db_init_handle(&handle);
  2420. Fi = pg_info->fi;
  2421. pg_info->dbdriver = driver = db_start_driver(Fi->driver);
  2422. if (!driver) {
  2423. G_warning(_("Unable to start driver <%s>"), Fi->driver);
  2424. return NULL;
  2425. }
  2426. db_set_handle(&handle, Fi->database, NULL);
  2427. if (db_open_database(driver, &handle) != DB_OK) {
  2428. G_warning(_("Unable to open database <%s> by driver <%s>"),
  2429. Fi->database, Fi->driver);
  2430. db_close_database_shutdown_driver(driver);
  2431. pg_info->dbdriver = NULL;
  2432. return NULL;
  2433. }
  2434. return pg_info->dbdriver;
  2435. }
  2436. #endif