12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963 |
- /*!
- \file lib/vector/Vlib/write_pg.c
- \brief Vector library - write vector feature (PostGIS format)
- Higher level functions for reading/writing/manipulating vectors.
- Write subroutine inspired by OGR PostgreSQL driver.
- \todo PostGIS version of V2__delete_area_cats_from_cidx_nat()
- \todo function to delete corresponding entry in fidx
- \todo PostGIS version of V2__add_area_cats_to_cidx_nat
- (C) 2012-2014 by Martin Landa, and the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- \author Martin Landa <landa.martin gmail.com>
- */
- #include <string.h>
- #include <grass/vector.h>
- #include <grass/glocale.h>
- #include "local_proto.h"
- #ifdef HAVE_POSTGRES
- #include "pg_local_proto.h"
- #define WKBSRIDFLAG 0x20000000
- #define TOPOGEOM_COLUMN "topo"
- /*! Use SQL statements from PostGIS Topology extension (this options
- is quite slow. By default are used simple SQL statements (INSERT, UPDATE)
- */
- #define USE_TOPO_STMT 0
- static int create_table(struct Format_info_pg *);
- static int check_schema(const struct Format_info_pg *);
- static int create_topo_schema(struct Format_info_pg *, int);
- static int create_pg_layer(struct Map_info *, int);
- static char *get_sftype(SF_FeatureType);
- static off_t write_line_sf(struct Map_info *, int,
- const struct line_pnts **, int,
- const struct line_cats *);
- static off_t write_line_tp(struct Map_info *, int, int,
- const struct line_pnts *,
- const struct line_cats *);
- static char *binary_to_hex(int, const unsigned char *);
- static unsigned char *point_to_wkb(int, const struct line_pnts *, int, int *);
- static unsigned char *linestring_to_wkb(int, const struct line_pnts *,
- int, int *);
- static unsigned char *polygon_to_wkb(int, const struct line_pnts **, int,
- int, int *);
- static char *line_to_wkb(struct Format_info_pg *, const struct line_pnts **,
- int, int, int);
- static int write_feature(struct Map_info *, int, int,
- const struct line_pnts **, int, int);
- static char *build_insert_stmt(const struct Format_info_pg *, const char *, int, int);
- static int insert_topo_element(struct Map_info *, int, int, const char *);
- static int type_to_topogeom(const struct Format_info_pg *);
- static int update_next_edge(struct Map_info*, int, int);
- static int delete_face(const struct Map_info *, int);
- static int update_topo_edge(struct Map_info *, int);
- static int update_topo_face(struct Map_info *, int);
- static int add_line_to_topo_pg(struct Map_info *, off_t, int, const struct line_pnts *);
- static int delete_line_from_topo_pg(struct Map_info *, int, int, const struct line_pnts *);
- static int set_constraint_to_deferrable(struct Format_info_pg *, const char *, const char *,
- const char *, const char *, const char *);
- static dbDriver *open_db(struct Format_info_pg *);
- #endif
- static struct line_pnts *Points;
- /*!
- \brief Writes feature on level 1 (PostGIS interface)
- Notes for simple feature access:
- - centroids are not supported in PostGIS, pseudotopo holds virtual
- centroids
- - boundaries are not supported in PostGIS, pseudotopo treats polygons
- as boundaries
- Notes for PostGIS Topology access:
- - centroids are stored as isolated nodes
- - boundaries are stored as edges
- \param Map pointer to Map_info structure
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param points pointer to line_pnts structure (feature geometry)
- \param cats pointer to line_cats structure (feature categories)
- \return feature offset into file
- \return -1 on error
- */
- off_t V1_write_line_pg(struct Map_info *Map, int type,
- const struct line_pnts *points,
- const struct line_cats *cats)
- {
- #ifdef HAVE_POSTGRES
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
-
- if (pg_info->feature_type == SF_GEOMETRY) {
- /* create PostGIS table if doesn't exist */
- if (create_pg_layer(Map, type) < 0)
- return -1;
- }
- if (!points)
- return 0;
-
- if (!pg_info->toposchema_name) { /* simple features access */
- return write_line_sf(Map, type, &points, 1, cats);
- }
- /* PostGIS Topology access */
- return write_line_tp(Map, type, FALSE, points, cats);
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Writes feature on topological level (PostGIS interface)
- Calls V2_write_line_sfa() for simple features access.
-
- \param Map pointer to Map_info structure
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param points pointer to line_pnts structure (feature geometry)
- \param cats pointer to line_cats structure (feature categories)
- \return feature offset into file
- \return -1 on error
- */
- off_t V2_write_line_pg(struct Map_info *Map, int type,
- const struct line_pnts *points,
- const struct line_cats *cats)
- {
- #ifdef HAVE_POSTGRES
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
-
- if (!pg_info->toposchema_name) { /* pseudo-topology */
- return V2_write_line_sfa(Map, type, points, cats);
- }
- /* PostGIS Topology */
- return write_line_tp(Map, type, FALSE, points, cats);
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Rewrites feature at the given offset (level 1) (PostGIS interface, internal use only)
- Only for simple feature access. PostGIS Topology requires level 2.
-
- \todo Use UPDATE statement ?
-
- \param Map pointer to Map_info structure
- \param offset feature offset
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param points feature geometry
- \param cats feature categories
- \return feature offset (rewritten feature)
- \return -1 on error
- */
- off_t V1_rewrite_line_pg(struct Map_info * Map,
- off_t offset, int type,
- const struct line_pnts * points,
- const struct line_cats * cats)
- {
- G_debug(3, "V1_rewrite_line_pg(): type=%d offset=%" PRI_OFF_T,
- type, offset);
- #ifdef HAVE_POSTGRES
- if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
- G_warning(_("Unable to rewrite feature (incompatible feature types)"));
- return -1;
- }
- /* delete old */
- V1_delete_line_pg(Map, offset);
- return V1_write_line_pg(Map, type, points, cats);
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Rewrites feature at topological level (PostGIS interface, internal use only)
- Note: Topology must be built at level >= GV_BUILD_BASE
-
- \todo Handle also categories
- \todo Store original geometry in tmp table for restore
-
- \param Map pointer to Map_info structure
- \param line feature id
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param points feature geometry
- \param cats feature categories
-
- \return offset where feature was rewritten
- \return -1 on error
- */
- off_t V2_rewrite_line_pg(struct Map_info *Map, off_t line, int type,
- const struct line_pnts *points, const struct line_cats *cats)
- {
- G_debug(3, "V2_rewrite_line_pg(): line=%d type=%d",
- (int)line, type);
- #ifdef HAVE_POSTGRES
- const char *schema_name, *table_name, *keycolumn;
- char *stmt, *geom_data;
-
- struct Format_info_pg *pg_info;
- struct P_line *Line;
- off_t offset;
- geom_data = NULL;
- stmt = NULL;
- pg_info = &(Map->fInfo.pg);
-
- if (line < 1 || line > Map->plus.n_lines) {
- G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
- return -1;
- }
-
- Line = Map->plus.Line[line];
- if (Line == NULL) {
- G_warning(_("Attempt to access dead feature %d"), (int)line);
- return -1;
- }
- offset = Line->offset;
- if (!(Map->plus.update_cidx)) {
- Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
- }
- if (!Points)
- Points = Vect_new_line_struct();
-
- if (type != V2_read_line_pg(Map, Points, NULL, line)) {
- G_warning(_("Unable to rewrite feature (incompatible feature types)"));
- return -1;
- }
- /* remove line from topology */
- if (0 != delete_line_from_topo_pg(Map, line, type, Points))
- return -1;
- if (pg_info->toposchema_name) { /* PostGIS Topology */
- schema_name = pg_info->toposchema_name;
- if (type & GV_POINTS) {
- table_name = keycolumn = "node";
- }
- else {
- table_name = "edge_data";
- keycolumn = "edge";
- }
- }
- else { /* simple features access */
- schema_name = pg_info->schema_name;
- table_name = pg_info->table_name;
- keycolumn = pg_info->fid_column;
- }
-
- geom_data = line_to_wkb(pg_info, &points, 1, type, Map->head.with_z);
- G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET geom = '%s'::GEOMETRY WHERE %s_id = %" PRI_OFF_T,
- schema_name, table_name, geom_data, keycolumn, line);
- G_free(geom_data);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to rewrite feature %d"), (int)line);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* update topology
- note: offset is not changed */
- return add_line_to_topo_pg(Map, offset, type, points);
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Deletes feature at the given offset (level 1)
- Only for simple feature access. PostGIS Topology requires level 2.
- \param Map pointer Map_info structure
- \param offset feature offset
- \return 0 on success
- \return -1 on error
- */
- int V1_delete_line_pg(struct Map_info *Map, off_t offset)
- {
- #ifdef HAVE_POSTGRES
- long fid;
- char stmt[DB_SQL_MAX];
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
- if (!pg_info->conn || !pg_info->table_name) {
- G_warning(_("No connection defined"));
- return -1;
- }
- if (offset >= pg_info->offset.array_num) {
- G_warning(_("Invalid offset (%" PRI_OFF_T ")"), offset);
- return -1;
- }
- fid = pg_info->offset.array[offset];
- G_debug(3, "V1_delete_line_pg(): offset = %lu -> fid = %ld",
- (unsigned long)offset, fid);
- if (!pg_info->inTransaction) {
- /* start transaction */
- pg_info->inTransaction = TRUE;
- if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
- return -1;
- }
- sprintf(stmt, "DELETE FROM %s WHERE %s = %ld",
- pg_info->table_name, pg_info->fid_column, fid);
- G_debug(3, "SQL: %s", stmt);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to delete feature"));
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- return 0;
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Deletes feature on topological level (PostGIS interface)
- Note: Topology must be built at level >= GV_BUILD_BASE
-
- Calls V2_delete_line_sfa() for simple feature access.
-
- \param Map pointer to Map_info structure
- \param line feature id to be deleted
-
- \return 0 on success
- \return -1 on error
- */
- int V2_delete_line_pg(struct Map_info *Map, off_t line)
- {
- #ifdef HAVE_POSTGRES
- int ret;
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
- if (line < 1 || line > Map->plus.n_lines) {
- G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
- return -1;
- }
-
- if (!pg_info->toposchema_name) { /* pseudo-topology */
- return V2_delete_line_sfa(Map, line);
- }
- else { /* PostGIS topology */
- int type;
- char stmt[DB_SQL_MAX];
- const char *table_name, *keycolumn;
-
- struct P_line *Line;
-
- if (line < 1 || line > Map->plus.n_lines) {
- G_warning(_("Attempt to access feature with invalid id (%d)"), (int)line);
- return -1;
- }
-
- Line = Map->plus.Line[line];
- if (!Line) {
- G_warning(_("Attempt to access dead feature %d"), (int)line);
- return -1;
- }
- if (!(Map->plus.update_cidx)) {
- Map->plus.cidx_up_to_date = FALSE; /* category index will be outdated */
- }
- Vect__execute_pg(pg_info->conn, "BEGIN");
-
- if (Line->type & GV_POINTS) {
- table_name = keycolumn = "node";
- }
- else {
- table_name = "edge_data";
- keycolumn = "edge";
- /* first remove references to this edge */
- /* (1) left next edge */
- sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_left_edge = edge_id, "
- "next_left_edge = -edge_id WHERE abs_next_left_edge = %d",
- pg_info->toposchema_name, table_name, (int)Line->offset);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* (2) right next edge */
- sprintf(stmt, "UPDATE \"%s\".\"%s\" SET abs_next_right_edge = edge_id, "
- "next_right_edge = edge_id WHERE abs_next_right_edge = %d",
- pg_info->toposchema_name, table_name, (int)Line->offset);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
-
- /* read the line */
- if (!Points)
- Points = Vect_new_line_struct();
-
- type = V2_read_line_pg(Map, Points, NULL, line);
- if (type < 0)
- return -1;
- /* delete record from topology table */
- sprintf(stmt, "DELETE FROM \"%s\".\"%s\" WHERE %s_id = %d",
- pg_info->toposchema_name, table_name, keycolumn, (int)Line->offset);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to delete feature (%s) %d"), keycolumn,
- (int)line);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- if (pg_info->cache.ctype == CACHE_MAP) {
- /* delete from cache */
-
- Vect_destroy_line_struct(pg_info->cache.lines[line-1]);
- pg_info->cache.lines[line-1] = NULL;
- pg_info->cache.lines_types[line-1] = 0;
- pg_info->cache.lines_cats[line-1] = 0;
- }
-
- /* update topology */
- ret = delete_line_from_topo_pg(Map, line, type, Points);
- if (ret == 0)
- Vect__execute_pg(pg_info->conn, "COMMIT");
-
- return ret;
- }
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- #ifdef HAVE_POSTGRES
- /*!
- \brief Writes node on topological level (PostGIS Topology
- interface, internal use only)
- The vector map must be open on level 2 at least with
- GV_BUILD_BASE. PostGIS Topology schema must be defined.
- \param Map pointer to Map_info structure
- \param node node id (starts at 1)
- \param points pointer to line_pnts structure
-
- \return 0 on success
- \return -1 on error
- */
- off_t V2__write_node_pg(struct Map_info *Map, const struct line_pnts *points)
- {
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
-
- if (!pg_info->toposchema_name)
- return -1; /* PostGIS Topology required */
-
- return write_line_tp(Map, GV_POINT, TRUE, points, NULL);
- }
- /*!
- \brief Writes area on topological level (PostGIS Simple Features
- interface, internal use only)
- \param Map pointer to Map_info structure
- \param points feature geometry (exterior + interior rings)
- \param nparts number of parts including exterior ring
- \param cats feature categories
-
- \return feature offset
- \return -1 on error
- */
- off_t V2__write_area_pg(struct Map_info *Map,
- const struct line_pnts **points, int nparts,
- const struct line_cats *cats)
- {
- return write_line_sf(Map, GV_BOUNDARY, points, nparts, cats);
- }
- /*!
- \brief Updates simple features geometry from GRASS-like topo
- \param Map pointer to Map_info structure
- \param points feature geometry (exterior + interior rings)
- \param nparts number of parts including exterior ring
- \param cat area category
-
- \return 0 on success
- \return -1 on error
- */
- int V2__update_area_pg(struct Map_info *Map,
- const struct line_pnts **points, int nparts,
- int cat)
- {
- int part, npoints;
- char *stmt, *geom_data;
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
- for (part = 0; part < nparts; part++) {
- npoints = points[part]->n_points - 1;
- if (points[part]->x[0] != points[part]->x[npoints] ||
- points[part]->y[0] != points[part]->y[npoints] ||
- points[part]->z[0] != points[part]->z[npoints]) {
- G_warning(_("Boundary is not closed. Skipping."));
- return -1;
- }
- }
-
- geom_data = line_to_wkb(pg_info, points, nparts, GV_AREA, Vect_is_3d(Map));
- if (!geom_data)
- return -1;
-
- stmt = NULL;
- G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET %s = '%s'::GEOMETRY WHERE %s = %d",
- pg_info->schema_name, pg_info->table_name, pg_info->geom_column,
- geom_data, pg_info->fid_column,
- cat);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- /* rollback transaction */
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- G_free(geom_data);
- G_free(stmt);
- return -1;
- }
- G_free(geom_data);
- G_free(stmt);
- return 0;
- }
- /*!
- \brief Create new feature table
- \param pg_info pointer to Format_info_pg
- \return -1 on error
- \return 0 on success
- */
- int create_table(struct Format_info_pg *pg_info)
- {
- int spatial_index, primary_key;
- char stmt[DB_SQL_MAX];
- char *geom_type, *def_file;
-
- struct field_info *Fi;
-
- PGresult *result;
- def_file = getenv("GRASS_VECTOR_PGFILE");
-
- /* by default create spatial index & add primary key */
- spatial_index = primary_key = TRUE;
- if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
- FILE *fp;
- const char *p;
- struct Key_Value *key_val;
- fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
- if (!fp) {
- G_warning(_("Unable to open PG file"));
- }
- else {
- key_val = G_fread_key_value(fp);
- fclose(fp);
-
- /* disable spatial index ? */
- p = G_find_key_value("spatial_index", key_val);
- if (p && G_strcasecmp(p, "no") == 0)
- spatial_index = FALSE;
-
- /* disable primary key ? */
- p = G_find_key_value("primary_key", key_val);
- if (p && G_strcasecmp(p, "no") == 0)
- primary_key = FALSE;
- G_free_key_value(key_val);
- }
- }
-
- /* create schema if not exists */
- if (G_strcasecmp(pg_info->schema_name, "public") != 0) {
- if (check_schema(pg_info) != 0)
- return -1;
- }
- /* prepare CREATE TABLE statement */
- sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s SERIAL%s, %s INTEGER",
- pg_info->schema_name, pg_info->table_name, pg_info->fid_column,
- primary_key ? " PRIMARY KEY" : "", GV_KEY_COLUMN);
-
- Fi = pg_info->fi;
-
- if (Fi) {
- /* append attributes */
- int col, ncols, sqltype, length;
- char stmt_col[DB_SQL_MAX];
- const char *colname;
- dbString dbtable_name;
- dbDriver *driver;
- dbTable *table;
- dbColumn *column;
- db_init_string(&dbtable_name);
- driver = open_db(pg_info);
- if (driver == NULL)
- return -1;
- /* describe table */
- db_set_string(&dbtable_name, Fi->table);
- if (db_describe_table(driver, &dbtable_name, &table) != DB_OK) {
- G_warning(_("Unable to describe table <%s>"),
- Fi->table);
- db_close_database_shutdown_driver(driver);
- pg_info->dbdriver = NULL;
- return -1;
- }
- ncols = db_get_table_number_of_columns(table);
- G_debug(3,
- "copying attributes: driver = %s database = %s table = %s cols = %d",
- Fi->driver, Fi->database, Fi->table, ncols);
- for (col = 0; col < ncols; col++) {
- column = db_get_table_column(table, col);
- colname = db_get_column_name(column);
- sqltype = db_get_column_sqltype(column);
- length = db_get_column_length(column);
- G_debug(3, "\tcolumn = %d name = %s type = %d length = %d",
- col, colname, sqltype, length);
- if (G_strcasecmp(pg_info->fid_column, colname) == 0 ||
- G_strcasecmp(GV_KEY_COLUMN, colname) == 0) {
- /* skip fid column if exists */
- G_debug(3, "\t%s skipped", colname);
- continue;
- }
- /* append column */
- sprintf(stmt_col, ",\"%s\" %s", colname, db_sqltype_name(sqltype));
- strcat(stmt, stmt_col);
- if (sqltype == DB_SQL_TYPE_CHARACTER) {
- /* length only for string columns */
- sprintf(stmt_col, "(%d)", length);
- strcat(stmt, stmt_col);
- }
- }
- db_free_string(&dbtable_name);
- }
- strcat(stmt, ")"); /* close CREATE TABLE statement */
- /* begin transaction (create table) */
- if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
- return -1;
- }
- /* create table */
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* determine geometry type (string) */
- switch (pg_info->feature_type) {
- case (SF_POINT):
- geom_type = "POINT";
- break;
- case (SF_LINESTRING):
- geom_type = "LINESTRING";
- break;
- case (SF_POLYGON):
- geom_type = "POLYGON";
- break;
- case (SF_POLYGON25D):
- geom_type = "POLYGONZ";
- break;
- case (SF_GEOMETRY):
- geom_type = "GEOMETRY";
- break;
- default:
- G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- /* add geometry column */
- sprintf(stmt, "SELECT AddGeometryColumn('%s', '%s', "
- "'%s', %d, '%s', %d)",
- pg_info->schema_name, pg_info->table_name,
- pg_info->geom_column, pg_info->srid,
- geom_type, pg_info->coor_dim);
- G_debug(2, "SQL: %s", stmt);
- result = PQexec(pg_info->conn, stmt);
-
- if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
- G_warning("%s", PQresultErrorMessage(result));
- PQclear(result);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- /* create indices
- - GV_KEY_COLUMN
- - geometry column
- */
- sprintf(stmt,
- "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" (%s)",
- pg_info->table_name, GV_KEY_COLUMN,
- pg_info->schema_name, pg_info->table_name,
- GV_KEY_COLUMN);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- if (spatial_index) {
- G_verbose_message(_("Building spatial index on <%s>..."),
- pg_info->geom_column);
- sprintf(stmt,
- "CREATE INDEX %s_%s_idx ON \"%s\".\"%s\" USING GIST (%s)",
- pg_info->table_name, pg_info->geom_column,
- pg_info->schema_name, pg_info->table_name,
- pg_info->geom_column);
-
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
- /* close transaction (create table) */
- if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
- return -1;
- }
- return 0;
- }
- /*!
- \brief Creates new schema for feature table if not exists
- \param pg_info pointer to Format_info_pg
- \return -1 on error
- \return 0 on success
- */
- int check_schema(const struct Format_info_pg *pg_info)
- {
- int i, found, nschema;
- char stmt[DB_SQL_MAX];
- PGresult *result;
- if (!pg_info->conn || !pg_info->table_name) {
- G_warning(_("No connection defined"));
- return -1;
- }
- /* add geometry column */
- sprintf(stmt, "SELECT nspname FROM pg_namespace");
- G_debug(2, "SQL: %s", stmt);
- result = PQexec(pg_info->conn, stmt);
- if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
- PQclear(result);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- found = FALSE;
- nschema = PQntuples(result);
- for (i = 0; i < nschema && !found; i++) {
- if (strcmp(pg_info->schema_name, PQgetvalue(result, i, 0)) == 0)
- found = TRUE;
- }
- PQclear(result);
- if (!found) {
- sprintf(stmt, "CREATE SCHEMA %s", pg_info->schema_name);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- G_warning(_("Schema <%s> doesn't exist, created"),
- pg_info->schema_name);
- }
- return 0;
- }
- /*!
- \brief Create new PostGIS topology schema
- - create topology schema
- - add topology column to the feature table
-
- \todo Add constraints for grass-like tables
-
- \param pg_info pointer to Format_info_pg
- \return 0 on success
- \return 1 topology disable, nothing to do
- \return -1 on failure
- */
- int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
- {
- double tolerance;
- char stmt[DB_SQL_MAX];
- char *def_file;
-
- def_file = getenv("GRASS_VECTOR_PGFILE");
-
- /* read default values from PG file*/
- tolerance = 0.;
- if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
- FILE *fp;
- const char *p;
- struct Key_Value *key_val;
- fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
- if (!fp) {
- G_fatal_error(_("Unable to open PG file"));
- }
- key_val = G_fread_key_value(fp);
- fclose(fp);
- /* tolerance */
- p = G_find_key_value("topo_tolerance", key_val);
- if (p)
- tolerance = atof(p);
- G_debug(1, "PG: tolerance: %f", tolerance);
-
- /* topogeom column */
- p = G_find_key_value("topogeom_name", key_val);
- if (p)
- pg_info->topogeom_column = G_store(p);
- else
- pg_info->topogeom_column = G_store(TOPOGEOM_COLUMN);
- G_debug(1, "PG: topogeom_column :%s", pg_info->topogeom_column);
- /* topo-geo only (default: no) */
- p = G_find_key_value("topo_geo_only", key_val);
- if (p && G_strcasecmp(p, "yes") == 0)
- pg_info->topo_geo_only = TRUE;
- G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
- /* build simple features from topogeometry data */
- p = G_find_key_value("simple_feature", key_val);
- if (p && G_strcasecmp(p, "yes") == 0)
- pg_info->topo_geo_only = TRUE;
- G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
- G_free_key_value(key_val);
- }
- /* begin transaction (create topo schema) */
- if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
- return -1;
- }
- /* create topology schema */
- G_verbose_message(_("Creating topology schema <%s>..."),
- pg_info->toposchema_name);
- sprintf(stmt, "SELECT topology.createtopology('%s', "
- "find_srid('%s', '%s', '%s'), %f, '%s')",
- pg_info->toposchema_name, pg_info->schema_name,
- pg_info->table_name, pg_info->geom_column, tolerance,
- with_z == WITH_Z ? "t" : "f");
- pg_info->toposchema_id = Vect__execute_get_value_pg(pg_info->conn, stmt);
- if (pg_info->toposchema_id == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* add topo column to the feature table */
- G_verbose_message(_("Adding new topology column <%s>..."),
- pg_info->topogeom_column);
- sprintf(stmt, "SELECT topology.AddTopoGeometryColumn('%s', '%s', '%s', "
- "'%s', '%s')", pg_info->toposchema_name, pg_info->schema_name,
- pg_info->table_name, pg_info->topogeom_column,
- get_sftype(pg_info->feature_type));
- if (-1 == Vect__execute_get_value_pg(pg_info->conn, stmt)) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* create index on topo column */
- sprintf(stmt, "CREATE INDEX \"%s_%s_%s_idx\" ON \"%s\".\"%s\" (((%s).id))",
- pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
- pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- /* change constraints to deferrable initially deferred */
- if (!pg_info->topo_geo_only) {
- if (-1 == set_constraint_to_deferrable(pg_info, "node", "face_exists",
- "containing_face", "face", "face_id") ||
- -1 == set_constraint_to_deferrable(pg_info, "edge_data", "end_node_exists",
- "end_node", "node", "node_id") ||
- -1 == set_constraint_to_deferrable(pg_info, "edge_data", "left_face_exists",
- "left_face", "face", "face_id") ||
- -1 == set_constraint_to_deferrable(pg_info, "edge_data", "right_face_exists",
- "right_face", "face", "face_id") ||
- -1 == set_constraint_to_deferrable(pg_info, "edge_data", "start_node_exists",
- "start_node", "node", "node_id"))
- return -1;
- }
- /* create additional tables in topological schema to store
- GRASS topology in DB */
- if (!pg_info->topo_geo_only) {
- /* (1) create 'node_grass' (see P_node struct)
-
- todo: add constraints for lines and angles
- */
- sprintf(stmt, "CREATE TABLE \"%s\".%s (node_id SERIAL PRIMARY KEY, "
- "lines integer[], angles float[])", pg_info->toposchema_name, TOPO_TABLE_NODE);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT node_exists "
- "FOREIGN KEY (node_id) REFERENCES \"%s\".node (node_id) "
- "DEFERRABLE INITIALLY DEFERRED",
- pg_info->toposchema_name, TOPO_TABLE_NODE, pg_info->toposchema_name);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* (2) create 'line_grass' (see P_line struct)
-
- */
- sprintf(stmt, "CREATE TABLE \"%s\".%s (line_id SERIAL PRIMARY KEY, "
- "left_area integer, right_area integer)",
- pg_info->toposchema_name, TOPO_TABLE_LINE);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT line_exists "
- "FOREIGN KEY (line_id) REFERENCES \"%s\".edge_data (edge_id) "
- "DEFERRABLE INITIALLY DEFERRED",
- pg_info->toposchema_name, TOPO_TABLE_LINE, pg_info->toposchema_name);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* (3) create 'area_grass' (see P_area struct)
-
- todo: add constraints for lines, centtroid and isles
- */
- sprintf(stmt, "CREATE TABLE \"%s\".%s (area_id SERIAL PRIMARY KEY, "
- "lines integer[], centroid integer, isles integer[])",
- pg_info->toposchema_name, TOPO_TABLE_AREA);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* (4) create 'isle_grass' (see P_isle struct)
-
- todo: add constraints for lines and area
- */
- sprintf(stmt, "CREATE TABLE \"%s\".%s (isle_id SERIAL PRIMARY KEY, "
- "lines integer[], area integer)",
- pg_info->toposchema_name, TOPO_TABLE_ISLE);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
- /* close transaction (create topo schema) */
- if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
- return -1;
- }
- return 0;
- }
- /*!
- \brief Create new PostGIS layer in given database (internal use only)
- V1_open_new_pg() must be called before this function.
- List of currently supported types:
- - GV_POINT (SF_POINT)
- - GV_LINE (SF_LINESTRING)
- - GV_BOUNDARY (SF_POLYGON)
- When PostGIS Topology the map level is updated to topological level
- and build level set to GV_BUILD_BASE.
- \param[in,out] Map pointer to Map_info structure
- \param type feature type (GV_POINT, GV_LINE, ...)
- \return 0 success
- \return -1 error
- */
- int create_pg_layer(struct Map_info *Map, int type)
- {
- int ndblinks;
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
- if (!pg_info->conninfo) {
- G_warning(_("Connection string not defined"));
- return -1;
- }
- if (!pg_info->table_name) {
- G_warning(_("PostGIS feature table not defined"));
- return -1;
- }
- G_debug(1, "Vect__open_new_pg(): conninfo='%s' table='%s' -> type = %d",
- pg_info->conninfo, pg_info->table_name, type);
- /* determine geometry type */
-
- switch (type) {
- case GV_POINT:
- case GV_CENTROID:
- pg_info->feature_type = SF_POINT;
- break;
- case GV_LINE:
- case GV_BOUNDARY:
- pg_info->feature_type = SF_LINESTRING;
- break;
- case GV_AREA:
- pg_info->feature_type = SF_POLYGON;
- break;
- case GV_FACE:
- pg_info->feature_type = SF_POLYGON25D;
- break;
- case -2:
- pg_info->feature_type = SF_GEOMETRY;
- break;
- default:
- G_warning(_("Unsupported geometry type (%d)"), type);
- return -1;
- }
- /* coordinate dimension */
- pg_info->coor_dim = Vect_is_3d(Map) ? 3 : 2;
- /* create new PostGIS table */
- ndblinks = Vect_get_num_dblinks(Map);
- if (ndblinks > 0) {
- pg_info->fi = Vect_get_dblink(Map, 0); /* TODO: support more layers */
- if (pg_info->fi) {
- if (ndblinks > 1)
- G_warning(_("More layers defined, using driver <%s> and "
- "database <%s>"),
- pg_info->fi->driver, pg_info->fi->database);
- }
- else {
- G_warning(_("Database connection not defined. "
- "Unable to write attributes."));
- }
- }
- /* create new feature table */
- if (create_table(pg_info) == -1) {
- G_warning(_("Unable to create new PostGIS feature table"));
- return -1;
- }
-
- /* create new topology schema (if PostGIS topology support is enabled) */
- if (pg_info->toposchema_name) {
- /* force topological level */
- Map->level = LEVEL_2;
- Map->plus.built = GV_BUILD_BASE;
-
- /* track updated features, used in V2__add_line_to_topo_nat() */
- Vect_set_updated(Map, TRUE);
-
- if (create_topo_schema(pg_info, Vect_is_3d(Map)) == -1) {
- G_warning(_("Unable to create new PostGIS topology schema"));
- return -1;
- }
- }
-
- return 0;
- }
- /*!
- \brief Get simple feature type as a string
- Used for AddTopoGeometryColumn().
- Valid types:
- - SF_POINT
- - SF_LINESTRING
- - SF_POLYGON
- \return string with feature type
- \return empty string
- */
- char *get_sftype(SF_FeatureType sftype)
- {
- if (sftype == SF_POINT)
- return "POINT";
- else if (sftype == SF_LINESTRING)
- return "LINE";
- else if (sftype == SF_POLYGON)
- return "POLYGON";
- else if (sftype == SF_GEOMETRY || sftype == SF_GEOMETRYCOLLECTION)
- return "COLLECTION";
- else
- G_warning(_("Unsupported feature type %d"), sftype);
-
- return "";
- }
- /*!
- \brief Write vector features as PostGIS simple feature element
-
- \param Map pointer to Map_info structure
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param points feature geometry (exterior + interior rings for polygonsx)
- \param nparts number of parts
- \param cats feature categories
- \return feature offset
- \return -1 on error
- */
- off_t write_line_sf(struct Map_info *Map, int type,
- const struct line_pnts **points, int nparts,
- const struct line_cats *cats)
- {
- int cat;
- off_t offset;
- SF_FeatureType sf_type;
- struct Format_info_pg *pg_info;
- struct Format_info_offset *offset_info;
- pg_info = &(Map->fInfo.pg);
- offset_info = &(pg_info->offset);
- if (nparts < 1)
- return -1;
-
- /* check required PG settings */
- if (!pg_info->conn) {
- G_warning(_("No connection defined"));
- return -1;
- }
- if (!pg_info->table_name) {
- G_warning(_("PostGIS feature table not defined"));
- return -1;
- }
- /* create PostGIS table if doesn't exist */
- if (pg_info->feature_type == SF_GEOMETRY) {
- if (create_pg_layer(Map, type) < 0)
- return -1;
- }
-
- /* get category & check for attributes */
- cat = -1;
- if (cats->n_cats > 0) {
- int field;
-
- if (pg_info->fi)
- field = pg_info->fi->number;
- else
- field = 1;
-
- if (!Vect_cat_get(cats, field, &cat))
- G_warning(_("No category defined for layer %d"), field);
- if (cats->n_cats > 1) {
- G_warning(_("Feature has more categories, using "
- "category %d (from layer %d)"),
- cat, field);
- }
- }
- sf_type = pg_info->feature_type;
- /* determine matching PostGIS feature geometry type */
- if (type & (GV_POINT | GV_KERNEL)) {
- if (sf_type != SF_POINT && sf_type != SF_POINT25D) {
- G_warning(_("Point skipped (output feature type: %s)"),
- Vect_get_finfo_geometry_type(Map));
- return 0;
- }
- }
- else if (type & GV_LINE) {
- if (sf_type != SF_LINESTRING && sf_type != SF_LINESTRING25D) {
- G_warning(_("Line skipped (output feature type: %s)"),
- Vect_get_finfo_geometry_type(Map));
- return 0;
- }
- }
- else if (type & GV_CENTROID) {
- if (sf_type != SF_POLYGON && sf_type != SF_POINT) {
- G_warning(_("Centroid skipped (output feature type: %s)"),
- Vect_get_finfo_geometry_type(Map));
- return 0;
- }
- }
- else if (type & GV_BOUNDARY) {
- if (sf_type != SF_POLYGON && sf_type != SF_LINESTRING) {
- G_warning(_("Boundary skipped (output feature type: %s)"),
- Vect_get_finfo_geometry_type(Map));
- return 0;
- }
- }
- else if (type & GV_FACE) {
- if (sf_type != SF_POLYGON25D) {
- G_warning(_("Face skipped (output feature type: %s)"),
- Vect_get_finfo_geometry_type(Map));
- return 0;
- }
- }
- else {
- G_warning(_("Unsupported feature type %d"), type);
- return -1;
- }
-
- G_debug(3, "write_line_sf(): type = %d n_points = %d cat = %d",
- type, points[0]->n_points, cat);
- if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
- /* skip this check when writing PostGIS topology */
- int part, npoints;
- for (part = 0; part < nparts; part++) {
- npoints = points[part]->n_points - 1;
- if (points[part]->x[0] != points[part]->x[npoints] ||
- points[part]->y[0] != points[part]->y[npoints] ||
- points[part]->z[0] != points[part]->z[npoints]) {
- G_warning(_("Boundary is not closed. Skipping."));
- return -1;
- }
- }
- }
- /* write feature's geometry and fid */
- if (-1 == write_feature(Map, -1, type, points, nparts, cat)) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* update offset array */
- if (offset_info->array_num >= offset_info->array_alloc) {
- offset_info->array_alloc += 1000;
- offset_info->array = (int *)G_realloc(offset_info->array,
- offset_info->array_alloc *
- sizeof(int));
- }
- offset = offset_info->array_num;
- offset_info->array[offset_info->array_num++] = cat;
- if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
- /* register first part in offset array */
- offset_info->array[offset_info->array_num++] = 0;
- }
- G_debug(3, "write_line_sf(): -> offset = %lu offset_num = %d cat = %d",
- (unsigned long)offset, offset_info->array_num, cat);
- return offset;
- }
- /*!
- \brief Write vector feature in PostGIS topology schema and
- updates internal topology structures
- \param Map vector map
- \param type feature type to be written
- \param points feature geometry
- \param is_node TRUE for nodes (written as points)
-
- \return feature id (build level >= GV_BUILD_BASE otherwise 0)
- \return 0 for nodes
- \return -1 on error
- */
- off_t write_line_tp(struct Map_info *Map, int type, int is_node,
- const struct line_pnts *points,
- const struct line_cats *cats)
- {
- int line, cat, line_id;
- struct Format_info_pg *pg_info;
- struct Plus_head *plus;
- struct field_info *Fi;
-
- pg_info = &(Map->fInfo.pg);
- plus = &(Map->plus);
- if (!(plus->update_cidx)) {
- plus->cidx_up_to_date = FALSE; /* category index will be outdated */
- }
- /* check type for nodes */
- if (is_node && type != GV_POINT) {
- G_warning(_("Invalid feature type (%d) for nodes"), type);
- return -1;
- }
- /* check required PG settings */
- if (!pg_info->conn) {
- G_warning(_("No connection defined"));
- return -1;
- }
- if (!pg_info->table_name) {
- G_warning(_("PostGIS feature table not defined"));
- return -1;
- }
- if (!pg_info->toposchema_name) {
- G_warning(_("PostGIS topology schema not defined"));
- return -1;
- }
-
- /* create PostGIS table if doesn't exist */
- if (pg_info->feature_type == SF_GEOMETRY) {
- if (create_pg_layer(Map, type) < 0)
- return -1;
- }
- if (!points)
- return 0;
-
- G_debug(3, "write_line_pg(): type = %d n_points = %d",
- type, points->n_points);
- Fi = pg_info->fi;
-
- cat = -1;
- if (cats && cats->n_cats > 0) {
- if (Fi) {
- if (!pg_info->dbdriver)
- open_db(pg_info);
- if (!Vect_cat_get(cats, Fi->number, &cat))
- G_warning(_("No category defined for layer %d"), Fi->number);
- if (cats->n_cats > 1) {
- G_warning(_("Feature has more categories, using "
- "category %d (from layer %d)"),
- cat, cats->field[0]);
- }
- }
- /* assume layer=1 */
- Vect_cat_get(cats, 1, &cat);
- }
- /* update GRASS topology before writing PostGIS feature */
- line = 0;
- if (plus->built >= GV_BUILD_BASE) {
- if (is_node) {
- /* nodes are given with negative id */
- line = -1 * dig_add_node(plus, points->x[0], points->y[0], points->z[0]);
- }
- else {
- off_t offset;
-
- /* better is probably to check nextval directly */
- if (type & GV_POINTS) {
- offset = Vect_get_num_primitives(Map, GV_POINTS) + 1; /* next */
- offset += Vect_get_num_nodes(Map); /* nodes are also stored in 'node' table */
- }
- else { /* LINES */
- offset = Vect_get_num_primitives(Map, GV_LINES) + 1; /* next */
- }
-
- line = add_line_to_topo_pg(Map, offset, type, points);
- }
- }
-
- /* write new feature to PostGIS
- - feature table for simple features
- - feature table and topo schema for topological access
- */
- line_id = write_feature(Map, line, type, &points, 1, cat);
- if (line_id < 0) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- if (pg_info->cache.ctype == CACHE_MAP) {
- /* add line to the cache */
- Vect__reallocate_cache(&(pg_info->cache), 1, TRUE);
- pg_info->cache.lines[line-1] = Vect_new_line_struct();
- pg_info->cache.lines_types[line-1] = type;
- pg_info->cache.lines_cats[line-1] = cat;
- }
- /* update offset array for nodes */
- if (is_node) {
- int node;
-
- struct Format_info_offset *offset;
- offset = &(pg_info->offset);
-
- node = abs(line);
- if (node > offset->array_alloc) {
- offset->array_alloc += 1000;
- offset->array = (int *) G_realloc (offset->array, offset->array_alloc * sizeof(int));
- }
-
- offset->array_num = node;
- offset->array[node-1] = (int) line_id; /* node id starts at 1 */
- }
- /* update PostGIS-line topo */
- if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
- update_topo_face(Map, line); /* TODO: avoid extra statements */
-
- return !is_node ? line : 0;
- }
- /*!
- \brief Binary data to HEX
- Allocated buffer should be freed by G_free().
- \param nbytes number of bytes to allocate
- \param wkb_data WKB data
- \return allocated buffer with HEX data
- */
- char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
- {
- char *hex_data;
- int i, nlow, nhigh;
- static const char ach_hex[] = "0123456789ABCDEF";
- hex_data = (char *)G_malloc(nbytes * 2 + 1);
- hex_data[nbytes * 2] = '\0';
- for (i = 0; i < nbytes; i++) {
- nlow = wkb_data[i] & 0x0f;
- nhigh = (wkb_data[i] & 0xf0) >> 4;
- hex_data[i * 2] = ach_hex[nhigh];
- hex_data[i * 2 + 1] = ach_hex[nlow];
- }
- return hex_data;
- }
- /*!
- \brief Write point into WKB buffer
- See OGRPoint::exportToWkb from GDAL/OGR library
- \param byte_order byte order (ENDIAN_LITTLE or BIG_ENDIAN)
- \param points feature geometry
- \param with_z WITH_Z for 3D data
- \param[out] nsize buffer size
- \return allocated WKB buffer
- \return NULL on error
- */
- unsigned char *point_to_wkb(int byte_order,
- const struct line_pnts *points, int with_z,
- int *nsize)
- {
- unsigned char *wkb_data;
- unsigned int sf_type;
- if (points->n_points != 1)
- return NULL;
- /* allocate buffer */
- *nsize = with_z ? 29 : 21;
- wkb_data = G_malloc(*nsize);
- G_zero(wkb_data, *nsize);
- G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
- /* set the byte order */
- if (byte_order == ENDIAN_LITTLE)
- wkb_data[0] = '\001';
- else
- wkb_data[0] = '\000';
- /* set the geometry feature type */
- sf_type = with_z ? SF_POINT25D : SF_POINT;
- if (byte_order == ENDIAN_LITTLE)
- sf_type = LSBWORD32(sf_type);
- else
- sf_type = MSBWORD32(sf_type);
- memcpy(wkb_data + 1, &sf_type, 4);
- /* copy in the raw data */
- memcpy(wkb_data + 5, &(points->x[0]), 8);
- memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
- if (with_z) {
- memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
- }
- /* swap if needed */
- if (byte_order == ENDIAN_BIG) {
- SWAPDOUBLE(wkb_data + 5);
- SWAPDOUBLE(wkb_data + 5 + 8);
- if (with_z)
- SWAPDOUBLE(wkb_data + 5 + 16);
- }
- return wkb_data;
- }
- /*!
- \bried Write linestring into WKB buffer
- See OGRLineString::exportToWkb from GDAL/OGR library
- \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
- \param points feature geometry
- \param with_z WITH_Z for 3D data
- \param[out] nsize buffer size
- \return allocated WKB buffer
- \return NULL on error
- */
- unsigned char *linestring_to_wkb(int byte_order,
- const struct line_pnts *points, int with_z,
- int *nsize)
- {
- int i, point_size;
- unsigned char *wkb_data;
- unsigned int sf_type;
- if (points->n_points < 1)
- return NULL;
- /* allocate buffer */
- point_size = 8 * (with_z ? 3 : 2);
- *nsize = 5 + 4 + points->n_points * point_size;
- wkb_data = G_malloc(*nsize);
- G_zero(wkb_data, *nsize);
- G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
- /* set the byte order */
- if (byte_order == ENDIAN_LITTLE)
- wkb_data[0] = '\001';
- else
- wkb_data[0] = '\000';
- /* set the geometry feature type */
- sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
- if (byte_order == ENDIAN_LITTLE)
- sf_type = LSBWORD32(sf_type);
- else
- sf_type = MSBWORD32(sf_type);
- memcpy(wkb_data + 1, &sf_type, 4);
- /* copy in the data count */
- memcpy(wkb_data + 5, &(points->n_points), 4);
- /* copy in the raw data */
- for (i = 0; i < points->n_points; i++) {
- memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
- memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
- if (with_z) {
- memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
- }
- }
- /* swap if needed */
- if (byte_order == ENDIAN_BIG) {
- int npoints, nitems;
- npoints = SWAP32(points->n_points);
- memcpy(wkb_data + 5, &npoints, 4);
- nitems = (with_z ? 3 : 2) * points->n_points;
- for (i = 0; i < nitems; i++) {
- SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
- }
- }
- return wkb_data;
- }
- /*!
- \bried Write polygon into WKB buffer
- See OGRPolygon::exportToWkb from GDAL/OGR library
- \param byte_order byte order (ENDIAN_LITTLE or ENDIAN_BIG)
- \param ipoints list of ring geometries (first is outer ring)
- \param nrings number of rings
- \param with_z WITH_Z for 3D data
- \param[out] nsize buffer size
- \return allocated WKB buffer
- \return NULL on error
- */
- unsigned char *polygon_to_wkb(int byte_order,
- const struct line_pnts** points, int nrings,
- int with_z, int *nsize)
- {
- int i, ring, point_size, offset;
- unsigned char *wkb_data;
- unsigned int sf_type;
- /* check data validity */
- if (nrings < 1)
- return NULL;
- for (ring = 0; ring < nrings; ring++) {
- if (points[ring]->n_points < 3)
- return NULL;
- }
- /* allocate buffer */
- point_size = 8 * (with_z ? 3 : 2);
- *nsize = 9;
- for (ring = 0; ring < nrings; ring++)
- *nsize += 4 + point_size * points[ring]->n_points;
- wkb_data = G_malloc(*nsize);
- G_zero(wkb_data, *nsize);
- G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
- /* set the byte order */
- if (byte_order == ENDIAN_LITTLE)
- wkb_data[0] = '\001';
- else
- wkb_data[0] = '\000';
- /* set the geometry feature type */
- sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
- if (byte_order == ENDIAN_LITTLE)
- sf_type = LSBWORD32(sf_type);
- else
- sf_type = MSBWORD32(sf_type);
- memcpy(wkb_data + 1, &sf_type, 4);
- /* copy in the raw data */
- if (byte_order == ENDIAN_BIG) {
- int ncount;
- ncount = SWAP32(nrings);
- memcpy(wkb_data + 5, &ncount, 4);
- }
- else {
- memcpy(wkb_data + 5, &nrings, 4);
- }
- /* serialize rings */
- offset = 9;
- for (ring = 0; ring < nrings; ring++) {
- memcpy(wkb_data + offset, &(points[ring]->n_points), 4);
- for (i = 0; i < points[ring]->n_points; i++) {
- memcpy(wkb_data + offset +
- 4 + point_size * i, &(points[ring]->x[i]), 8);
- memcpy(wkb_data + offset +
- 4 + 8 + point_size * i, &(points[ring]->y[i]), 8);
-
- if (with_z) {
- memcpy(wkb_data + offset +
- 4 + 16 + point_size * i, &(points[ring]->z[i]), 8);
- }
- }
-
- offset += 4 + point_size * points[ring]->n_points;
-
- /* swap if needed */
- if (byte_order == ENDIAN_BIG) {
- int npoints, nitems;
-
- npoints = SWAP32(points[ring]->n_points);
- memcpy(wkb_data + 5, &npoints, 4);
-
- nitems = (with_z ? 3 : 2) * points[ring]->n_points;
- for (i = 0; i < nitems; i++) {
- SWAPDOUBLE(wkb_data + offset + 4 + 8 * i);
- }
- }
- }
-
- return wkb_data;
- }
- /*!
- \brief Write feature to WKB buffer
- Allocated string buffer should be freed by G_free().
- \param pg_info pointer to Format_info_pg struct
- \param points array of geometries which form feature
- \param nparts number of geometries in array
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param with_z WITH_Z for 3D data
- \return allocated string buffer
- \return NULL on error
- */
- char *line_to_wkb(struct Format_info_pg *pg_info,
- const struct line_pnts **points, int nparts, int type, int with_z)
- {
- int byte_order, nbytes, nsize;
- unsigned int sf_type;
-
- unsigned char *wkb_data;
- char *text_data, *text_data_p, *hex_data;
-
- byte_order = dig__byte_order_out();
- /* get wkb data */
- nbytes = -1;
- wkb_data = NULL;
- if (type & GV_POINTS) /* point or centroid */
- wkb_data = point_to_wkb(byte_order, points[0], with_z, &nbytes);
- else if (type == GV_LINE ||
- (type == GV_BOUNDARY && pg_info->feature_type == SF_LINESTRING))
- wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
- else if (type & (GV_BOUNDARY | GV_FACE | GV_AREA)) {
- if (!pg_info->toposchema_name || type == GV_AREA) {
- /* PostGIS simple feature access */
- wkb_data = polygon_to_wkb(byte_order, points, nparts,
- with_z, &nbytes);
- }
- else {
- /* PostGIS topology access */
- wkb_data = linestring_to_wkb(byte_order, points[0], with_z, &nbytes);
- }
- }
-
- if (!wkb_data || nbytes < 1) {
- G_warning(_("Unsupported feature type %d"), type);
- return NULL;
- }
- /* When converting to hex, each byte takes 2 hex characters. In
- addition we add in 8 characters to represent the SRID integer
- in hex, and one for a null terminator */
- nsize = nbytes * 2 + 8 + 1;
- text_data = text_data_p = (char *)G_malloc(nsize);
- /* convert the 1st byte, which is the endianess flag, to hex */
- hex_data = binary_to_hex(1, wkb_data);
- strcpy(text_data_p, hex_data);
- G_free(hex_data);
- text_data_p += 2;
- /* get the geom type which is bytes 2 through 5 */
- memcpy(&sf_type, wkb_data + 1, 4);
- /* add the SRID flag if an SRID is provided */
- if (pg_info->srid > 0) {
- unsigned int srs_flag;
- /* change the flag to little endianess */
- srs_flag = LSBWORD32(WKBSRIDFLAG);
- /* apply the flag */
- sf_type = sf_type | srs_flag;
- }
- /* write the geom type which is 4 bytes */
- hex_data = binary_to_hex(4, (unsigned char *)&sf_type);
- strcpy(text_data_p, hex_data);
- G_free(hex_data);
- text_data_p += 8;
- /* include SRID if provided */
- if (pg_info->srid > 0) {
- unsigned int srs_id;
- /* force the srsid to little endianess */
- srs_id = LSBWORD32(pg_info->srid);
- hex_data = binary_to_hex(sizeof(srs_id), (unsigned char *)&srs_id);
- strcpy(text_data_p, hex_data);
- G_free(hex_data);
- text_data_p += 8;
- }
- /* copy the rest of the data over - subtract 5 since we already
- copied 5 bytes above */
- hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
- strcpy(text_data_p, hex_data);
- G_free(hex_data);
- return text_data;
- }
- /*!
- \brief Insert feature into table
- \param Map pointer to Map_info structure
- \param line feature id (topo access only)
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param points pointer to line_pnts struct
- \param nparts number of parts (rings for polygon)
- \param cat category number (-1 for no category)
- \return topo_id for PostGIS Topology
- \return 0 for simple features access
- \return -1 on error
- */
- int write_feature(struct Map_info *Map, int line, int type,
- const struct line_pnts **points, int nparts, int cat)
- {
- int with_z, topo_id;
- char *stmt, *geom_data;
- struct Format_info_pg *pg_info;
-
- pg_info = &(Map->fInfo.pg);
- with_z = Map->head.with_z;
-
- if (with_z && pg_info->coor_dim != 3) {
- G_warning(_("Trying to insert 3D data into feature table "
- "which store 2D data only"));
- return -1;
- }
- if (!with_z && pg_info->coor_dim != 2) {
- G_warning(_("Trying to insert 2D data into feature table "
- "which store 3D data only"));
- return -1;
- }
- /* build WKB geometry from line_pnts structures */
- geom_data = line_to_wkb(pg_info, points, nparts, type, with_z);
- if (!geom_data)
- return -1;
-
- /* start transaction */
- if (!pg_info->inTransaction) {
- pg_info->inTransaction = TRUE;
- if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1) {
- G_free(geom_data);
- return -1;
- }
- }
- /* write feature in PostGIS topology schema if enabled */
- topo_id = -1;
- if (pg_info->toposchema_name) {
- /* insert feature into topology schema (node or edge) */
- topo_id = insert_topo_element(Map, line, type, geom_data);
- if (topo_id < 0) {
- G_warning(_("Unable to insert topological element into PostGIS Topology schema"));
- G_free(geom_data);
-
- return -1;
- }
- if (pg_info->feature_type != SF_POLYGON) {
- /* define relation */
- Vect__define_topo_relation(pg_info, topo_id, topo_id);
- }
- }
- /* build INSERT statement
- simple feature geometry + attributes
- */
- stmt = build_insert_stmt(pg_info, geom_data, topo_id, cat);
- /* stmt can NULL when writing PostGIS topology with no attributes
- * attached */
- if (stmt && Vect__execute_pg(pg_info->conn, stmt) == -1) {
- /* rollback transaction */
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- G_free(geom_data);
- G_free(stmt);
-
- return -1;
- }
- G_free(geom_data);
- G_free(stmt);
-
- return pg_info->toposchema_name ? topo_id : 0;
- }
- /*!
- \brief Build INSERT statement to add new feature to the feature
- table
- Note: Allocated string should be freed.
-
- \param pg_info pointer to Format_info_pg structure
- \param geom_data geometry data
- \param type feature type (GV_POINT, GV_LINE, ...) - (only for PostGIS Topology)
- \param id topology element id (only for PostGIS Topology)
- \param cat category number (or -1 for no category)
- \param Fi pointer to field_info structure (NULL for no attributes)
- \return allocated string with INSERT statement
- */
- char *build_insert_stmt(const struct Format_info_pg *pg_info,
- const char *geom_data, int topo_id, int cat)
- {
- int topogeom_type;
-
- char *stmt, buf[DB_SQL_MAX];
- struct field_info *Fi;
-
- topogeom_type = -1;
- if (pg_info->toposchema_name) {
- topogeom_type = type_to_topogeom(pg_info);
- if (topogeom_type < 0)
- return NULL;
- }
- Fi = pg_info->fi;
-
- stmt = NULL;
- if (Fi && cat > -1) {
- /* write attributes (simple features and topology elements) */
- int col, ncol, more;
- int sqltype, ctype, is_fid;
- char buf_val[DB_SQL_MAX], buf_tmp[DB_SQL_MAX];
- const char *colname;
- dbString dbstmt;
- dbCursor cursor;
- dbTable *table;
- dbColumn *column;
- dbValue *value;
- db_init_string(&dbstmt);
- buf_val[0] = '\0';
- /* read & set attributes */
- sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
- cat);
- G_debug(4, "SQL: %s", buf);
- db_set_string(&dbstmt, buf);
-
- /* prepare INSERT statement */
- sprintf(buf, "INSERT INTO \"%s\".\"%s\" (",
- pg_info->schema_name, pg_info->table_name);
-
- /* select data */
- if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
- &cursor, DB_SEQUENTIAL) != DB_OK) {
- G_warning(_("Unable to select attributes for category %d"), cat);
- }
- else {
- if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
- G_warning(_("Unable to fetch data from table <%s>"),
- Fi->table);
- }
- if (!more) {
- G_warning(_("No database record for category %d, "
- "no attributes will be written"), cat);
- }
- else {
- table = db_get_cursor_table(&cursor);
- ncol = db_get_table_number_of_columns(table);
- for (col = 0; col < ncol; col++) {
- column = db_get_table_column(table, col);
- colname = db_get_column_name(column);
- /* -> values */
- value = db_get_column_value(column);
- /* for debug only */
- db_convert_column_value_to_string(column, &dbstmt);
- G_debug(3, "col %d : val = %s", col,
- db_get_string(&dbstmt));
- sqltype = db_get_column_sqltype(column);
- ctype = db_sqltype_to_Ctype(sqltype);
- is_fid = strcmp(pg_info->fid_column, colname) == 0;
-
- /* check fid column (must be integer) */
- if (is_fid == TRUE &&
- ctype != DB_C_TYPE_INT) {
- G_warning(_("FID column must be integer, column <%s> ignored!"),
- colname);
- continue;
- }
- /* -> columns */
- sprintf(buf_tmp, "\"%s\"", colname);
- strcat(buf, buf_tmp);
- if (col < ncol - 1)
- strcat(buf, ",");
-
- /* prevent writing NULL values */
- if (!db_test_value_isnull(value)) {
- switch (ctype) {
- case DB_C_TYPE_INT:
- sprintf(buf_tmp, "%d", db_get_value_int(value));
- break;
- case DB_C_TYPE_DOUBLE:
- sprintf(buf_tmp, "%.14f",
- db_get_value_double(value));
- break;
- case DB_C_TYPE_STRING: {
- char *value_tmp;
- value_tmp = G_str_replace(db_get_value_string(value), "'", "''");
- sprintf(buf_tmp, "'%s'", value_tmp);
- G_free(value_tmp);
- break;
- }
- case DB_C_TYPE_DATETIME:
- db_convert_column_value_to_string(column,
- &dbstmt);
- sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
- break;
- default:
- G_warning(_("Unsupported column type %d"), ctype);
- sprintf(buf_tmp, "NULL");
- break;
- }
- }
- else {
- if (is_fid == TRUE)
- G_warning(_("Invalid value for FID column: NULL"));
- sprintf(buf_tmp, "NULL");
- }
- strcat(buf_val, buf_tmp);
- if (col < ncol - 1)
- strcat(buf_val, ",");
- }
-
- if (!pg_info->toposchema_name) {
- /* simple feature access */
- G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)",
- buf, pg_info->geom_column, buf_val, geom_data);
- }
- else {
- /* PostGIS topology access, write geometry in
- * topology schema, skip geometry at this point */
- if (buf[strlen(buf)-1] == ',') { /* last column skipped */
- buf[strlen(buf)-1] = '\0';
- buf_val[strlen(buf_val)-1] = '\0';
- }
- G_asprintf(&stmt, "%s, %s) VALUES (%s, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
- buf, pg_info->topogeom_column, buf_val,
- pg_info->toposchema_id, topo_id, topogeom_type);
- }
- }
- }
- }
- else {
- /* no attributes */
- if (!pg_info->toposchema_name) {
- /* no attributes (simple features access) */
- if (cat > 0) {
- /* cetegory defined */
- G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
- "(%d, '%s'::GEOMETRY)",
- pg_info->schema_name, pg_info->table_name,
- GV_KEY_COLUMN, pg_info->geom_column,
- cat, geom_data);
- }
- else {
- /* no category */
- G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
- "('%s'::GEOMETRY)",
- pg_info->schema_name, pg_info->table_name,
- pg_info->geom_column, geom_data);
- }
- }
- else {
- if (cat > 0) {
- /* no attributes (topology elements) */
- G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s,%s) VALUES "
- "(%d, '(%d, 1, %d, %d)'::topology.TopoGeometry)",
- pg_info->schema_name, pg_info->table_name,
- GV_KEY_COLUMN, pg_info->topogeom_column, cat,
- pg_info->toposchema_id, topo_id, topogeom_type);
- }
- }
- }
-
- return stmt;
- }
- /*!
- \brief Insert topological element into 'node' or 'edge' table
- Negative id for nodes, 0 for next value.
-
- \param Map pointer to Map_info struct
- \param id feature id (0 for next val)
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param geom_data geometry in wkb
- \return new topo id
- \return -1 on error
- */
- int insert_topo_element(struct Map_info *Map, int id, int type,
- const char *geom_data)
- {
- int ret, topo_id;
- char *stmt, stmt_id[DB_SQL_MAX];
- struct Format_info_pg *pg_info;
- struct Plus_head *plus;
- struct P_line *Line;
- pg_info = &(Map->fInfo.pg);
- plus = &(Map->plus);
-
- Line = NULL;
- if (plus->built >= GV_BUILD_BASE) {
- if (id > 0) { /* -> feature */
- topo_id = id;
- if (topo_id > Map->plus.n_lines) {
- G_warning(_("Invalid feature %d (max: %d)"), topo_id, Map->plus.n_lines);
- return -1;
- }
- Line = Map->plus.Line[topo_id];
- if (Line->type & GV_POINTS) {
- /* set topo_id for points */
- topo_id = Vect_get_num_primitives(Map, GV_POINTS) + Vect_get_num_nodes(Map);
- }
- }
- else if (id < 0) { /* node */
- topo_id = abs(id);
- if (type != GV_POINT) {
- G_warning(_("Invalid feature type (%d) for node"), type);
- return -1;
- }
- if (topo_id > Map->plus.n_nodes) {
- G_warning(_("Invalid node %d (%d)"), topo_id, Map->plus.n_nodes);
- return -1;
- }
- /* increment topo_id - also points and centroids are
- * stored in 'node' table */
- topo_id += Vect_get_num_primitives(Map, GV_POINTS);
- }
- }
- stmt = NULL;
- switch(type) {
- case GV_POINT: {
- /* insert new node */
- #if USE_TOPO_STMT
- G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
- pg_info->toposchema_name, geom_data);
- #else
- if (id == 0) {
- /* get node_id */
- sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
- pg_info->toposchema_name);
- topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
- }
- /* build insert statement */
- G_asprintf(&stmt, "INSERT INTO \"%s\".node (node_id, geom) VALUES "
- "(%d, '%s'::GEOMETRY)", pg_info->toposchema_name, topo_id, geom_data);
- #endif
- break;
- }
- case GV_LINE:
- case GV_BOUNDARY: {
- /* insert new edge */
- #if USE_TOPO_STMT
- G_asprintf(&stmt, "SELECT topology.AddEdge('%s', '%s'::GEOMETRY)",
- pg_info->toposchema_name, geom_data);
- #else
- int n1, n2, nle, nre;
-
- struct Format_info_offset *offset;
-
- offset = &(pg_info->offset);
-
- if (id == 0) {
- /* get edge_id */
- sprintf(stmt_id, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
- pg_info->toposchema_name);
- topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
- }
-
- nle = -topo_id; /* assuming isolated lines */
- nre = topo_id;
-
- if (Line) {
- int i, n, next_edge;
- struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
-
- topo_id = (int)Line->offset;
- /* start & end node */
- n1 = topo->N1;
- n2 = topo->N2;
-
- /* next left & right edge */
- for (i = 0; i < 2; i++) {
- n = Vect_get_node_n_lines(Map, i == 0 ? n1 : n2);
- if (n < 2) /* no connection */
- continue;
-
- next_edge = update_next_edge(Map, n,
- i == 0 ? topo_id : -topo_id);
- if (next_edge != 0) {
- if (i == 0)
- nre = next_edge; /* update next right edge for start node */
- else
- nle = next_edge; /* update next left edge for end node */
- }
- else {
- G_warning(_("Unable to determine next left/right edge for edge %d"), topo_id);
- }
- }
- }
- else {
- G_warning(_("Unable to insert new edge. Topology not available."));
- return -1;
- }
-
- G_debug(3, "new edge: id=%d next_left_edge=%d next_right_edge=%d",
- topo_id, nle, nre);
-
- if (n1 > offset->array_num || n2 > offset->array_num) /* node id starts at 1 */
- return -1;
-
- /* build insert statement */
- G_asprintf(&stmt, "INSERT INTO \"%s\".edge_data (edge_id, start_node, end_node, "
- "next_left_edge, abs_next_left_edge, next_right_edge, abs_next_right_edge, "
- "left_face, right_face, geom) VALUES "
- "(%d, %d, %d, %d, %d, %d, %d, 0, 0, '%s'::GEOMETRY)",
- pg_info->toposchema_name, topo_id, offset->array[n1-1], offset->array[n2-1],
- nle, abs(nle), nre, abs(nre), geom_data);
- #endif
- break;
- }
- case GV_CENTROID: {
- /* insert new node (with containing_face) */
- #if USE_TOPO_STMT
- G_asprintf(&stmt, "SELECT topology.AddNode('%s', '%s'::GEOMETRY)",
- pg_info->toposchema_name, geom_data);
- #else
- int area;
-
- if (id == 0) {
- /* get node_id */
- sprintf(stmt_id, "SELECT nextval('\"%s\".node_node_id_seq')",
- pg_info->toposchema_name);
- topo_id = Vect__execute_get_value_pg(pg_info->conn, stmt_id);
- }
-
- if (Line) {
- struct P_topo_c *topo = (struct P_topo_c *) Line->topo;
-
- area = topo->area;
- }
- else {
- area = 0;
- }
- G_asprintf(&stmt, "INSERT INTO \"%s\".node (node_id, containing_face, geom) VALUES "
- "(%d, %d, '%s'::GEOMETRY)", pg_info->toposchema_name,
- topo_id, area, geom_data);
- #endif
- break;
- }
- default:
- G_warning(_("Unsupported feature type %d"), type);
- break;
- }
- /* execute insert statement */
- ret = Vect__execute_pg(pg_info->conn, stmt);
- G_free(stmt);
-
- if (ret == -1) {
- /* rollback transaction */
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- return topo_id;
- }
- int type_to_topogeom(const struct Format_info_pg *pg_info)
- {
- int topogeom_type;
-
- topogeom_type = -1;
- switch (pg_info->feature_type) {
- case SF_POINT:
- topogeom_type = 1;
- break;
- case SF_LINESTRING:
- topogeom_type = 2;
- break;
- case SF_POLYGON:
- topogeom_type = 3;
- break;
- default:
- G_warning(_("Unsupported feature type %d"), pg_info->feature_type);
- }
- return topogeom_type;
- }
- int Vect__define_topo_relation(const struct Format_info_pg *pg_info, int topo_id, int element_id)
- {
- int topogeom_type;
- char stmt[DB_SQL_MAX];
- topogeom_type = type_to_topogeom(pg_info);
- if (topogeom_type < 0)
- return -1;
- sprintf(stmt, "INSERT into \"%s\".relation VALUES(%d, 1, %d, %d)",
- pg_info->toposchema_name, topo_id, element_id, topogeom_type);
- G_debug(3, "SQL: %s", stmt);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- return 0;
- }
- /*!
- \brief Find next line (topo only)
- \param Map pointer to Map_info struct
- \param nlines number of lines
- \param line current line
- \param[out] left left line
- \param[out] right right line
-
- \return left (line < 0) or right (line > 0) next edge
- \return 0 on failure
- */
- int update_next_edge(struct Map_info* Map, int nlines, int line)
- {
- int ret, next_line, edge;
- char stmt[DB_SQL_MAX];
-
- const struct Format_info_pg *pg_info;
- struct P_line *Line_next, *Line;
-
- Line = Line_next = NULL;
-
- pg_info = &(Map->fInfo.pg);
- /* find next line
- start node -> next on the left
- end node -> next on the right
- */
- next_line = dig_angle_next_line(&(Map->plus), line, GV_LEFT, GV_LINES, NULL);
- G_debug(3, "line=%d next_line=%d", line, next_line);
- if (next_line == 0) {
- G_warning(_("Invalid topology"));
- return 0;
- }
-
- Line = Map->plus.Line[abs(line)];
- Line_next = Map->plus.Line[abs(next_line)];
- if (!Line || !Line_next) {
- G_warning(_("Invalid topology"));
- return 0;
- }
-
- if (line > 0) {
- edge = Line->offset;
- ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
- }
- else {
- edge = -Line->offset;
- ret = next_line > 0 ? Line_next->offset : -Line_next->offset;
- }
-
- if (next_line < 0) {
- sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
- "abs_next_left_edge = %d WHERE edge_id = %d AND abs_next_left_edge = %d",
- pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset, (int)Line_next->offset);
- G_debug(3, "update edge=%d next_left_edge=%d (?)", (int)Line_next->offset, edge);
- }
- else {
- sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
- "abs_next_right_edge = %d WHERE edge_id = %d AND abs_next_right_edge = %d",
- pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset, (int)Line_next->offset);
- G_debug(3, "update edge=%d next_right_edge=%d (?)", (int)Line_next->offset, edge);
- }
-
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
-
- if (nlines > 2) {
- /* more lines connected to the node
- start node -> next on the right
- end node -> next on the left
- */
- next_line = dig_angle_next_line(&(Map->plus), line, GV_RIGHT, GV_LINES, NULL);
- Line_next = Map->plus.Line[abs(next_line)];
-
- if (next_line < 0) {
- sprintf(stmt, "UPDATE \"%s\".edge_data SET next_left_edge = %d, "
- "abs_next_left_edge = %d WHERE edge_id = %d",
- pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset);
- G_debug(3, "update edge=%d next_left_edge=%d", (int)Line_next->offset, edge);
- }
- else {
- sprintf(stmt, "UPDATE \"%s\".edge_data SET next_right_edge = %d, "
- "abs_next_right_edge = %d WHERE edge_id = %d",
- pg_info->toposchema_name, edge, abs(edge), (int)Line_next->offset);
- G_debug(3, "update edge=%d next_right_edge=%d", (int)Line_next->offset, edge);
- }
-
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- }
-
- return ret;
- }
- /*!
- \brief Insert new face to the 'face' table (topo only)
- \param Map pointer to Map_info struct
- \param area area id (negative id for isles)
- \return 0 on error
- \return area id on success (>0)
- */
- int Vect__insert_face_pg(struct Map_info *Map, int area)
- {
- char *stmt;
-
- struct Format_info_pg *pg_info;
- struct bound_box box;
-
- if (area == 0)
- return 0; /* universal face has id '0' in PostGIS Topology */
- stmt = NULL;
- pg_info = &(Map->fInfo.pg);
- /* check if face exists */
-
- /* get mbr of the area */
- if (area > 0)
- Vect_get_area_box(Map, area, &box);
- else
- Vect_get_isle_box(Map, abs(area), &box);
-
- /* insert face if not exists */
- G_asprintf(&stmt, "INSERT INTO \"%s\".face (face_id, mbr) VALUES "
- "(%d, ST_GeomFromText('POLYGON((%.12f %.12f, %.12f %.12f, %.12f %.12f, %.12f %.12f, "
- "%.12f %.12f))', %d))", pg_info->toposchema_name, area,
- box.W, box.S, box.W, box.N, box.E, box.N,
- box.E, box.S, box.W, box.S, pg_info->srid);
- G_debug(3, "new face id=%d", area);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- G_free(stmt);
- return area;
- }
- /*!
- \brief Delete existing face (currently unused)
- \todo Set foreign keys as DEFERRABLE INITIALLY DEFERRED and use SET
- CONSTRAINTS ALL DEFERRED
-
- \param Map pointer to Map_info struct
- \param area area id to delete
- \return 0 on success
- \return -1 on error
- */
- int delete_face(const struct Map_info *Map, int area)
- {
- char stmt[DB_SQL_MAX];
- const struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
-
- /* update centroids first */
- sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 "
- "WHERE containing_face = %d",
- pg_info->toposchema_name, area);
- G_debug(3, "SQL: %s", stmt);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* update also edges (left face) */
- sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0 "
- "WHERE left_face = %d",
- pg_info->toposchema_name, area);
- G_debug(3, "SQL: %s", stmt);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* update also edges (left face) */
- sprintf(stmt, "UPDATE \"%s\".edge_data SET right_face = 0 "
- "WHERE right_face = %d",
- pg_info->toposchema_name, area);
- G_debug(3, "SQL: %s", stmt);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- /* delete face */
- sprintf(stmt, "DELETE FROM \"%s\".face WHERE face_id = %d",
- pg_info->toposchema_name, area);
- G_debug(3, "delete face id=%d", area);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- return 0;
- }
- /*!
- \brief Update lines (next left and right edges)
-
- - isolated edges
- next left edge: -edge
- next right edge: edge
-
- - connected edges
- next left edge: next edge or -edge
- next right edge: next edge or edge
- \param Map pointer to Map_info struct
- \param line feature id
- \return 0 on success
- \return -1 on error
- */
- int update_topo_edge(struct Map_info *Map, int line)
- {
- int i, n;
- int nle, nre, next_edge;
- char stmt[DB_SQL_MAX];
-
- struct Format_info_pg *pg_info;
- struct P_line *Line;
- pg_info = &(Map->fInfo.pg);
-
- if (line < 1 || line > Map->plus.n_lines) {
- G_warning(_("Attempt to access non-existing feature %d"), line);
- return -1;
- }
- Line = Map->plus.Line[line];
- if (!Line) {
- G_warning(_("Attempt to access dead feature %d"), line);
- return -1;
- }
-
- struct P_topo_l *topo = (struct P_topo_l *) Line->topo;
-
- nre = nle = 0; /* edge = 0 is an illegal value */
-
- /* check for line connection */
- for (i = 0; i < 2; i++) {
- /* first check start node then end node */
- n = i == 0 ? Vect_get_node_n_lines(Map, topo->N1)
- : Vect_get_node_n_lines(Map, topo->N2);
-
- if (n < 2) /* no connection */
- continue;
-
- next_edge = update_next_edge(Map, n,
- i == 0 ? line : -line);
- if (next_edge != 0) {
- if (i == 0)
- nre = next_edge; /* update next right edge for start node */
- else
- nle = next_edge; /* update next left edge for end node */
- }
- else {
- G_warning(_("Unable to determine next left/right edge"));
- return -1;
- }
- }
- if (nle == 0 && nre == 0) /* nothing changed */
- return 0;
-
- if (nle != 0 && nre != 0) {
- /* update both next left and right edge */
- sprintf(stmt, "UPDATE \"%s\".edge_data SET "
- "next_left_edge = %d, abs_next_left_edge = %d, "
- "next_right_edge = %d, abs_next_right_edge = %d "
- "WHERE edge_id = %d", pg_info->toposchema_name,
- nle, abs(nle), nre, abs(nre), (int)Line->offset);
- }
- else if (nle != 0) {
- /* update next left edge only */
- sprintf(stmt, "UPDATE \"%s\".edge_data SET "
- "next_left_edge = %d, abs_next_left_edge = %d "
- "WHERE edge_id = %d", pg_info->toposchema_name,
- nle, abs(nle), (int)Line->offset);
- }
- else {
- /* update next right edge only */
- sprintf(stmt, "UPDATE \"%s\".edge_data SET "
- "next_right_edge = %d, abs_next_right_edge = %d "
- "WHERE edge_id = %d", pg_info->toposchema_name,
- nre, abs(nre), (int)Line->offset);
- }
- G_debug(3, "update edge=%d next_left_edge=%d next_right_edge=%d",
- (int)Line->offset, nle, nre);
-
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- /* rollback transaction */
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- return 0;
- }
- /*!
- \brief Update lines (left and right faces)
- TODO: handle isles
-
- \param Map pointer to Map_info struct
- \param line feature id
- \return 0 on success
- \return -1 on error
- */
- int update_topo_face(struct Map_info *Map, int line)
- {
- int i, s, area, face[2];
- char stmt[DB_SQL_MAX];
-
- struct Format_info_pg *pg_info;
- struct P_line *Line, *Line_i;
- struct P_area *Area;
- struct P_topo_b *topo, *topo_i;
-
- pg_info = &(Map->fInfo.pg);
-
- if (line < 1 || line > Map->plus.n_lines) {
- G_warning(_("Attempt to access non-existing feature %d"), line);
- return -1;
- }
- Line = Map->plus.Line[line];
- if (!Line) {
- G_warning(_("Attempt to access dead feature %d"), line);
- return -1;
- }
-
- topo = (struct P_topo_b *)Line->topo;
-
- /* for both side on the current boundary (line) */
- /* create new faces */
- for (s = 0; s < 2; s++) { /* for each side */
- area = s == 0 ? topo->left : topo->right;
- if (area <= 0) /* no area - skip */
- continue;
- face[s] = Vect__insert_face_pg(Map, area);
- if (face[s] < 1) {
- G_warning(_("Unable to create new face"));
- return -1;
- }
- }
-
- /* update edges forming faces */
- for (s = 0; s < 2; s++) { /* for each side */
- area = s == 0 ? topo->left : topo->right;
- if (area <= 0) /* no area - skip */
- continue;
-
- Area = Map->plus.Area[area];
- for (i = 0; i < Area->n_lines; i++) {
- Line_i = Map->plus.Line[abs(Area->lines[i])];
- topo_i = (struct P_topo_b *)Line_i->topo;
-
- sprintf(stmt, "UPDATE \"%s\".edge_data SET "
- "left_face = %d, right_face = %d "
- "WHERE edge_id = %d", pg_info->toposchema_name,
- topo_i->left > 0 ? topo_i->left : 0,
- topo_i->right > 0 ? topo_i->right : 0,
- (int) Line_i->offset);
- G_debug(2, "SQL: %s", stmt);
-
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
-
- /* update also centroids (stored as nodes) */
- if (Area->centroid > 0) {
- Line_i = Map->plus.Line[Area->centroid];
- sprintf(stmt, "UPDATE \"%s\".node SET containing_face = %d "
- "WHERE node_id = %d", pg_info->toposchema_name,
- face[s], (int)Line_i->offset);
- G_debug(2, "SQL: %s", stmt);
-
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- /* rollback transaction */
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
- }
-
- return 0;
- }
- /*!
- \brief Add line to native and PostGIS topology
- \param Map vector map
- \param offset ???
- \param type feature type
- \param Points feature vertices
- \return feature id
- \return -1 on error
- */
- int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
- const struct line_pnts *points)
- {
- int line, n_nodes;
-
- struct Plus_head *plus;
- plus = &(Map->plus);
- Vect_reset_updated(Map);
- line = V2__add_line_to_topo_nat(Map, offset, type, points, NULL,
- -1, NULL);
-
- /* insert new nodes into 'node' table */
- n_nodes = Vect_get_num_updated_nodes(Map);
- if (n_nodes > 0) {
- int i, node;
- double x, y, z;
-
- if (!Points)
- Points = Vect_new_line_struct();
-
- for (i = 0; i < n_nodes; i++) {
- node = Vect_get_updated_node(Map, i);
- /* skip updated and deleted nodes */
- if (node > 0 || plus->Node[abs(node)] == NULL)
- continue;
-
- G_debug(3, " new node: %d", node);
-
- Vect_get_node_coor(Map, abs(node), &x, &y, &z);
- Vect_reset_line(Points);
- Vect_append_point(Points, x, y, z);
-
- write_feature(Map, node, GV_POINT, (const struct line_pnts **) &Points, 1, -1);
- }
- }
- return line;
- }
- /*!
- \brief Delete line from native and PostGIS topology
- \param Map vector map
- \param line feature id to remove from topo
- \param type feature type
- \param Points feature vertices
- \return 0 on success
- \return -1 on error
- */
- int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
- const struct line_pnts *Points)
- {
- int N1, N2, node_id;
- char stmt[DB_SQL_MAX];
-
- struct Format_info_pg *pg_info;
- struct P_node *Node;
- pg_info = &(Map->fInfo.pg);
-
- Vect_reset_updated(Map);
- if (!(type & GV_LINES))
- return 0;
- Vect_get_line_nodes(Map, line, &N1, &N2);
- if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
- return -1;
-
- Node = Map->plus.Node[N1];
- if (!Node || Node->n_lines == 0) {
- node_id = pg_info->offset.array[N1-1];
- sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
- pg_info->toposchema_name, node_id);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to delete node %d"), node_id);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
- Node = Map->plus.Node[N2];
- if (!Node || Node->n_lines == 0) {
- node_id = pg_info->offset.array[N2-1];
- sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
- pg_info->toposchema_name, node_id);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to delete node %d"), node_id);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
- return 0;
- }
- int set_constraint_to_deferrable(struct Format_info_pg *pg_info,
- const char *table, const char *constraint,
- const char *column, const char *ref_table,
- const char *ref_column)
- {
- char stmt[DB_SQL_MAX];
-
- sprintf(stmt, "ALTER TABLE \"%s\".%s DROP CONSTRAINT %s",
- pg_info->toposchema_name, table, constraint);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT %s "
- "FOREIGN KEY (%s) REFERENCES \"%s\".%s (%s) "
- "DEFERRABLE INITIALLY DEFERRED",
- pg_info->toposchema_name, table, constraint, column,
- pg_info->toposchema_name, ref_table, ref_column);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt)) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- return 0;
- }
- /*!
- \brief Open database connection with attribute table
- \param pg_info pointer to Format_info_pg struct
- \return pointer to dbDriver on succes
- \return NULL on failure
- */
- dbDriver *open_db(struct Format_info_pg *pg_info) {
- dbDriver *driver;
- dbHandle handle;
-
- struct field_info *Fi;
-
- db_init_handle(&handle);
-
- Fi = pg_info->fi;
-
- pg_info->dbdriver = driver = db_start_driver(Fi->driver);
- if (!driver) {
- G_warning(_("Unable to start driver <%s>"), Fi->driver);
- return NULL;
- }
- db_set_handle(&handle, Fi->database, NULL);
- if (db_open_database(driver, &handle) != DB_OK) {
- G_warning(_("Unable to open database <%s> by driver <%s>"),
- Fi->database, Fi->driver);
- db_close_database_shutdown_driver(driver);
- pg_info->dbdriver = NULL;
- return NULL;
- }
- return pg_info->dbdriver;
- }
- #endif
|