12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670 |
- /*!
- \file lib/vector/Vlib/read_pg.c
- \brief Vector library - reading features (PostGIS format)
- Higher level functions for reading/writing/manipulating vectors.
- \todo Currently only points, linestrings and polygons are supported,
- implement also other types
- \todo Support multigeometries
- \todo PostGIS Topology - fix category handling (read categories
- from feature table)
- (C) 2011-2013 by 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 <stdlib.h>
- #include <string.h>
- #include <limits.h>
- #include <grass/vector.h>
- #include <grass/dbmi.h>
- #include <grass/glocale.h>
- #include "local_proto.h"
- #ifdef HAVE_POSTGRES
- #include "pg_local_proto.h"
- /* #define USE_CURSOR_RND */
- static unsigned char *wkb_data;
- static unsigned int wkb_data_length;
- static int read_next_line_pg(struct Map_info *,
- struct line_pnts *, struct line_cats *, int);
- SF_FeatureType get_feature(struct Format_info_pg *, int, int);
- static unsigned char *hex_to_wkb(const char *, int *);
- static int point_from_wkb(const unsigned char *, int, int, int,
- struct line_pnts *);
- static int linestring_from_wkb(const unsigned char *, int, int, int,
- struct line_pnts *, int);
- static int polygon_from_wkb(const unsigned char *, int, int, int,
- struct Format_info_cache *, int *);
- static int geometry_collection_from_wkb(const unsigned char *, int, int, int,
- struct Format_info_cache *,
- struct feat_parts *);
- static int error_corrupted_data(const char *);
- static void reallocate_cache(struct Format_info_cache *, int, int);
- static void add_fpart(struct feat_parts *, SF_FeatureType, int, int);
- static int get_centroid(struct Map_info *, int, struct line_pnts *);
- static void error_tuples(struct Format_info_pg *);
- #endif
- /*!
- \brief Read next feature from PostGIS layer. Skip
- empty features (level 1 without topology).
- t
- This function implements sequential access.
- The action of this routine can be modified by:
- - Vect_read_constraint_region()
- - Vect_read_constraint_type()
- - Vect_remove_constraints()
- \param Map pointer to Map_info structure
- \param[out] line_p container used to store line points within
- (pointer to line_pnts struct)
- \param[out] line_c container used to store line categories within
- (pointer line_cats struct)
- \return feature type
- \return -2 no more features (EOF)
- \return -1 out of memory
- */
- int V1_read_next_line_pg(struct Map_info *Map,
- struct line_pnts *line_p, struct line_cats *line_c)
- {
- #ifdef HAVE_POSTGRES
- G_debug(3, "V1_read_next_line_pg()");
- /* constraints not ignored */
- return read_next_line_pg(Map, line_p, line_c, FALSE);
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Read next feature from PostGIS layer on topological level
- (simple feature access).
- This function implements sequential access.
- \param Map pointer to Map_info structure
- \param[out] line_p container used to store line points within
- (pointer to line_pnts struct)
- \param[out] line_c container used to store line categories within
- (pointer to line_cats struct)
- \return feature type
- \return -2 no more features (EOF)
- \return -1 on failure
- */
- int V2_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p,
- struct line_cats *line_c)
- {
- #ifdef HAVE_POSTGRES
- int line, ret;
- struct P_line *Line;
- struct bound_box lbox, mbox;
- struct Format_info_pg *pg_info;
-
- G_debug(3, "V2_read_next_line_pg()");
- pg_info = &(Map->fInfo.pg);
-
- if (Map->constraint.region_flag)
- Vect_get_constraint_box(Map, &mbox);
- ret = -1;
- while (TRUE) {
- line = Map->next_line;
- if (Map->next_line > Map->plus.n_lines)
- return -2;
- Line = Map->plus.Line[line];
- if (Line == NULL) { /* skip dead features */
- Map->next_line++;
- continue;
- }
- if (Map->constraint.type_flag) {
- /* skip by type */
- if (!(Line->type & Map->constraint.type)) {
- Map->next_line++;
- continue;
- }
- }
- if (!pg_info->toposchema_name &&
- Line->type == GV_CENTROID) {
- G_debug(4, "Determine centroid for simple features");
- if (line_p != NULL) {
- int i, found;
- struct bound_box box;
- struct boxlist list;
- struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
- /* get area bbox */
- Vect_get_area_box(Map, topo->area, &box);
- /* search in spatial index for centroid with area bbox */
- dig_init_boxlist(&list, TRUE);
- Vect_select_lines_by_box(Map, &box, Line->type, &list);
- found = -1;
- for (i = 0; i < list.n_values; i++) {
- if (list.id[i] == line) {
- found = i;
- break;
- }
- }
- if (found > -1) {
- Vect_reset_line(line_p);
- Vect_append_point(line_p, list.box[found].E,
- list.box[found].N, 0.0);
- }
- }
- if (line_c != NULL) {
- /* cat = FID and offset = FID for centroid */
- Vect_reset_cats(line_c);
- Vect_cat_set(line_c, 1, (int)Line->offset);
- }
- ret = GV_CENTROID;
- }
- else {
- /* ignore constraints */
- ret = read_next_line_pg(Map, line_p, line_c, TRUE);
- if (ret != Line->type) {
- G_warning(_("Unexpected feature type (%d) - should be (%d)"),
- ret, Line->type);
- return -1;
- }
- }
- if (Map->constraint.region_flag) {
- /* skip by region */
- Vect_line_box(line_p, &lbox);
- if (!Vect_box_overlap(&lbox, &mbox)) {
- Map->next_line++;
- continue;
- }
- }
- /* skip by field ignored */
-
- Map->next_line++; /* read next */
-
- return ret;
- }
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- #endif
- return -1; /* not reached */
- }
- /*!
- \brief Read feature from PostGIS layer at given offset (level 1 without topology)
- This function implements random access on level 1.
- \param Map pointer to Map_info structure
- \param[out] line_p container used to store line points within
- (pointer line_pnts struct)
- \param[out] line_c container used to store line categories within
- (pointer line_cats struct)
- \param offset given offset
- \return line type
- \return 0 dead line
- \return -2 no more features
- \return -1 out of memory
- */
- int V1_read_line_pg(struct Map_info *Map,
- struct line_pnts *line_p, struct line_cats *line_c,
- off_t offset)
- {
- #ifdef HAVE_POSTGRES
- long fid;
- int ipart, type;
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
- G_debug(3, "V1_read_line_pg(): offset = %lu offset_num = %lu",
- (long)offset, (long)pg_info->offset.array_num);
- if (offset >= pg_info->offset.array_num)
- return -2; /* nothing to read */
- if (line_p != NULL)
- Vect_reset_line(line_p);
- if (line_c != NULL)
- Vect_reset_cats(line_c);
- fid = pg_info->offset.array[offset];
- G_debug(4, " fid = %ld", fid);
- /* read feature to cache if necessary */
- if (pg_info->cache.fid != fid) {
- int type;
- G_debug(3, "read (%s) feature (fid = %ld) to cache",
- pg_info->table_name, fid);
- get_feature(pg_info, fid, -1);
- if (pg_info->cache.sf_type == SF_NONE) {
- G_warning(_("Feature %ld without geometry skipped"), fid);
- return -1;
- }
- type = (int)pg_info->cache.sf_type;
- if (type < 0) /* -1 || - 2 */
- return type;
- }
- /* get data from cache */
- if (pg_info->cache.sf_type == SF_POINT ||
- pg_info->cache.sf_type == SF_LINESTRING)
- ipart = 0;
- else
- ipart = pg_info->offset.array[offset + 1];
- type = pg_info->cache.lines_types[ipart];
- G_debug(3, "read feature part: %d -> type = %d", ipart, type);
- if (line_p)
- Vect_append_points(line_p, pg_info->cache.lines[ipart], GV_FORWARD);
- if (line_c)
- Vect_cat_set(line_c, 1, (int)fid);
- return type;
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- /*!
- \brief Read feature from PostGIS layer on topological level
- This function implements random access on level 2.
- Note: Topology must be built at level >= GV_BUILD_BASE
-
- \param Map pointer to Map_info structure
- \param[out] line_p container used to store line points within (pointer line_pnts struct)
- \param[out] line_c container used to store line categories within (pointer line_cats struct)
- \param line feature id to read
- \return feature type
- \return 0 dead feature
- \return -1 on error
- */
- int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p,
- struct line_cats *line_c, int line)
- {
- #ifdef HAVE_POSTGRES
- int fid, cache_idx;
-
- 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 feature with invalid id (%d)"), line);
- return -1;
- }
-
- Line = Map->plus.Line[line];
- if (Line == NULL) {
- G_warning(_("Attempt to access dead feature %d"), line);
- return 0;
- }
-
- G_debug(4, "V2_read_line_pg() line = %d type = %d offset = %"PRI_OFF_T,
- line, Line->type, Line->offset);
-
- if (!line_p && !line_c)
- return Line->type;
- if (line_p)
- Vect_reset_line(line_p);
- if (Line->type == GV_CENTROID && !pg_info->toposchema_name) {
- /* simple features access: get centroid from sidx */
- return get_centroid(Map, line, line_p);
- }
-
- /* get feature id */
- if (pg_info->toposchema_name)
- fid = Line->offset;
- else
- fid = pg_info->offset.array[Line->offset];
- /* read feature */
- if (pg_info->cache.ctype == CACHE_MAP) {
- cache_idx = line - 1;
-
- if (cache_idx >= pg_info->cache.lines_num)
- G_fatal_error(_("Requesting invalid feature from cache (%d). Number of features in cache: %d"),
- cache_idx, pg_info->cache.lines_num);
- if (pg_info->cache.lines_types[cache_idx] != Line->type)
- G_warning(_("Feature %d: unexpected type (%d) - should be %d"),
- line, pg_info->cache.lines_types[cache_idx], Line->type);
- }
- else {
- get_feature(pg_info, fid, Line->type);
- cache_idx = 0;
- }
- /* check sf type */
- if (pg_info->cache.sf_type == SF_NONE) {
- G_warning(_("Feature %d without geometry skipped"), line);
- return -1;
- }
- if (0 > (int)pg_info->cache.sf_type) /* -1 || - 2 */
- return -1;
- if (line_c) {
- int cat;
- Vect_reset_cats(line_c);
- if (!pg_info->toposchema_name) { /* simple features access */
- cat = (int) Line->offset;
- }
- else { /* PostGIS Topology (cats are cached) */
- cat = pg_info->cache.lines_cats[cache_idx];
- if (cat == 0) { /* not cached yet */
- int col_idx;
- Vect__select_line_pg(pg_info, fid, Line->type);
-
- col_idx = Line->type & GV_POINTS ? 2 : 3;
-
- if (!PQgetisnull(pg_info->res, 0, col_idx))
- cat = pg_info->cache.lines_cats[cache_idx] =
- atoi(PQgetvalue(pg_info->res, 0, col_idx));
- else
- pg_info->cache.lines_cats[cache_idx] = -1; /* no cat */
- }
- }
- if (cat > 0)
- Vect_cat_set(line_c, 1, cat);
- }
- if (line_p)
- Vect_append_points(line_p, pg_info->cache.lines[cache_idx], GV_FORWARD);
-
- return Line->type;
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return -1;
- #endif
- }
- #ifdef HAVE_POSTGRES
- /*!
- \brief Read next feature from PostGIS layer.
- \param Map pointer to Map_info structure
- \param[out] line_p container used to store line points within
- (pointer to line_pnts struct)
- \param[out] line_c container used to store line categories within
- (pointer line_cats struct)
- \param ignore_constraints TRUE to ignore constraints (type, region)
- \return feature type
- \return -2 no more features (EOF)
- \return -1 out of memory
- */
- int read_next_line_pg(struct Map_info *Map,
- struct line_pnts *line_p, struct line_cats *line_c,
- int ignore_constraints)
- {
- int itype;
- SF_FeatureType sf_type;
- struct Format_info_pg *pg_info;
- struct bound_box mbox, lbox;
- struct line_pnts *iline;
- pg_info = &(Map->fInfo.pg);
- if (Map->constraint.region_flag && !ignore_constraints)
- Vect_get_constraint_box(Map, &mbox);
- while (TRUE) {
- /* reset data structures */
- if (line_p != NULL)
- Vect_reset_line(line_p);
- if (line_c != NULL)
- Vect_reset_cats(line_c);
- /* read feature to cache if necessary */
- while (pg_info->cache.lines_next == pg_info->cache.lines_num) {
- if (pg_info->cache.ctype == CACHE_MAP &&
- pg_info->cache.fid == -2) {
- /* stop reading - last cached line */
- return -2;
- }
-
- /* cache feature -> line_p & line_c */
- sf_type = get_feature(pg_info, -1, -1);
-
- if (sf_type == SF_NONE) {
- G_warning(_("Feature %ld without geometry skipped"), pg_info->cache.fid);
- return -1;
- }
- if ((int)sf_type < 0) { /* -1 || - 2 */
- if (pg_info->cache.ctype == CACHE_MAP)
- pg_info->cache.fid = -2; /* last line cached */
- return (int)sf_type;
- }
- if (sf_type == SF_UNKNOWN || sf_type == SF_NONE) {
- G_warning(_("Feature without geometry. Skipped."));
- pg_info->cache.lines_next = pg_info->cache.lines_num = 0;
- continue;
- }
- G_debug(4, "%d lines read to cache", pg_info->cache.lines_num);
- /* store fid as offset to be used (used for topo access only */
- Map->head.last_offset = pg_info->cache.fid;
- }
- /* get data from cache, skip dead lines (NULL) */
- do {
- itype = pg_info->cache.lines_types[pg_info->cache.lines_next];
- iline = pg_info->cache.lines[pg_info->cache.lines_next];
- pg_info->cache.lines_next++; /* read next line from cache */
- } while (iline == NULL);
- G_debug(4, "read next cached line %d (type = %d)",
- pg_info->cache.lines_next, itype);
- /* apply constraints */
- if (Map->constraint.type_flag && !ignore_constraints) {
- /* skip feature by type */
- if (!(itype & Map->constraint.type))
- continue;
- }
- if (line_p && Map->constraint.region_flag && !ignore_constraints) {
- /* skip feature by region */
- Vect_line_box(iline, &lbox);
- if (!Vect_box_overlap(&lbox, &mbox))
- continue;
- }
- /* skip feature by field ignored */
- if (line_p)
- Vect_append_points(line_p, iline, GV_FORWARD);
- if (line_c) {
- int cat;
- if (!pg_info->toposchema_name) { /* simple features access */
- cat = (int)pg_info->cache.fid;
- }
- else { /* PostGIS Topology (cats are cached) */
- cat = pg_info->cache.lines_cats[pg_info->cache.lines_next-1];
- if (cat == 0) { /* not cached yet */
- int col_idx;
-
- col_idx = itype & GV_POINTS ? 2 : 3;
-
- if (!PQgetisnull(pg_info->res, pg_info->cache.lines_next-1, col_idx))
- cat = pg_info->cache.lines_cats[Map->next_line-1] =
- atoi(PQgetvalue(pg_info->res, pg_info->cache.lines_next-1, col_idx));
- else
- pg_info->cache.lines_cats[Map->next_line-1] = -1; /* no cat */
- }
- }
- if (cat > 0)
- Vect_cat_set(line_c, 1, cat);
- }
- return itype;
- }
- return -1; /* not reached */
- }
- /*!
- \brief Read feature geometry
- Geometry is stored in lines cache.
- \param[in,out] pg_info pointer to Format_info_pg struct
- \param fid feature id to be read (-1 for next)
- \param type feature type (GV_POINT, GV_LINE, ...) - use only for topological access
-
- \return simple feature type (SF_POINT, SF_LINESTRING, ...)
- \return -1 on error
- */
- SF_FeatureType get_feature(struct Format_info_pg *pg_info, int fid, int type)
- {
- int seq_type;
- int force_type; /* force type (GV_BOUNDARY or GV_CENTROID) for topo access only */
- char *data;
-
- if (!pg_info->geom_column && !pg_info->topogeom_column) {
- G_warning(_("No geometry or topo geometry column defined"));
- return -1;
- }
- if (fid < 1) { /* sequantial access */
- if (pg_info->cursor_name == NULL &&
- Vect__open_cursor_next_line_pg(pg_info, FALSE) != 0)
- return -1;
- }
- else { /* random access */
- if (!pg_info->fid_column && !pg_info->toposchema_name) {
- G_warning(_("Random access not supported. "
- "Primary key not defined."));
- return -1;
- }
- #ifdef USE_CURSOR_RND
- if (pg_info->cursor_fid > 0)
- pg_info->next_line = fid - pg_info->cursor_fid;
- else
- pg_info->next_line = 0;
-
- if (pg_info->next_line < 0 || pg_info->next_line > CURSOR_PAGE)
- Vect__close_cursor_pg(pg_info);
-
- if (pg_info->cursor_name == NULL &&
- Vect__open_cursor_line_pg(pg_info, fid, type) != 0)
- return -1;
- #else
- pg_info->next_line = 0;
- if (Vect__select_line_pg(pg_info, fid, type) != 0)
- return -1;
- #endif
- }
- /* do we need to fetch more records ? */
- if (PQntuples(pg_info->res) == CURSOR_PAGE &&
- PQntuples(pg_info->res) == pg_info->next_line) {
- char stmt[DB_SQL_MAX];
- PQclear(pg_info->res);
- sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
- G_debug(3, "SQL: %s", stmt);
- pg_info->res = PQexec(pg_info->conn, stmt);
- if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
- error_tuples(pg_info);
- return -1;
- }
- pg_info->next_line = 0;
- }
- G_debug(3, "get_feature(): next_line = %d", pg_info->next_line);
-
- /* out of results ? */
- if (PQntuples(pg_info->res) == pg_info->next_line) {
- if (Vect__close_cursor_pg(pg_info) != 0)
- return -1; /* failure */
- else
- return -2; /* nothing to read */
- }
- force_type = -1;
- if (pg_info->toposchema_name) {
- if (fid < 0) {
- /* sequatial access */
- seq_type = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
- if (seq_type == GV_BOUNDARY ||
- (seq_type == GV_LINE && pg_info->feature_type == SF_POLYGON))
- force_type = GV_BOUNDARY;
- else if (seq_type == GV_CENTROID)
- force_type = GV_CENTROID;
- }
- else {
- /* random access: check topological elemenent type consistency */
- if (type & GV_POINTS) {
- if (type == GV_POINT &&
- strlen(PQgetvalue(pg_info->res, pg_info->next_line, 1)) != 0)
- G_warning(_("Inconsistency in topology: detected centroid (should be point)"));
- }
- else {
- int left_face, right_face;
-
- left_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
- right_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
-
- if (type == GV_LINE &&
- (left_face != 0 || right_face != 0))
- G_warning(_("Inconsistency in topology: detected boundary (should be line)"));
- }
- }
- }
- /* get geometry data */
- data = (char *)PQgetvalue(pg_info->res, pg_info->next_line, 0);
-
- /* load feature to the cache */
- pg_info->cache.sf_type = Vect__cache_feature_pg(data,
- FALSE, force_type,
- &(pg_info->cache), NULL);
-
- /* cache also categories (only for PostGIS Topology) */
- if (pg_info->toposchema_name) {
- int col_idx;
- col_idx = type & GV_POINTS ? 2 : 3;
-
- if (!PQgetisnull(pg_info->res, pg_info->next_line, col_idx))
- pg_info->cache.lines_cats[pg_info->cache.lines_next] =
- atoi(PQgetvalue(pg_info->res, pg_info->next_line, col_idx));
- else
- pg_info->cache.lines_cats[pg_info->cache.lines_next] = -1; /* no cat */
- }
- /* set feature id */
- if (fid < 0) {
- pg_info->cache.fid =
- atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
- pg_info->next_line++;
- }
- else {
- pg_info->cache.fid = fid;
- }
-
- return pg_info->cache.sf_type;
- }
- /*!
- \brief Convert HEX to WKB data
- \param hex_data HEX data
- \param[out] nbytes number of bytes in output buffer
- \return pointer to WKB data buffer
- */
- unsigned char *hex_to_wkb(const char *hex_data, int *nbytes)
- {
- unsigned int length;
- int i;
- length = strlen(hex_data) / 2 + 1;
- if (length > wkb_data_length) {
- wkb_data_length = length;
- wkb_data = G_realloc(wkb_data, wkb_data_length);
- }
- *nbytes = length - 1;
- for (i = 0; i < (*nbytes); i++) {
- wkb_data[i] =
- (unsigned char)((hex_data[2 * i] >
- 'F' ? hex_data[2 * i] - 0x57 : hex_data[2 * i] >
- '9' ? hex_data[2 * i] - 0x37 : hex_data[2 * i] -
- 0x30) << 4);
- wkb_data[i] |=
- (unsigned char)(hex_data[2 * i + 1] >
- 'F' ? hex_data[2 * i + 1] -
- 0x57 : hex_data[2 * i + 1] >
- '9' ? hex_data[2 * i + 1] -
- 0x37 : hex_data[2 * i + 1] - 0x30);
- }
- wkb_data[(*nbytes)] = 0;
- return wkb_data;
- }
- /*!
- \brief Read geometry from HEX data
- This code is inspired by OGRGeometryFactory::createFromWkb() from
- GDAL/OGR library.
- \param data HEX data
- \param skip_polygon skip polygons (level 1)
- \param force_type force GV_BOUNDARY or GV_CENTROID (used for PostGIS topology only)
- \param[out] cache lines cache
- \param[out] fparts used for building pseudo-topology (or NULL)
- \return simple feature type
- \return SF_UNKNOWN on error
- */
- SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon,
- int force_type,
- struct Format_info_cache *cache,
- struct feat_parts * fparts)
- {
- int ret, byte_order, nbytes, is3D;
- unsigned char *wkb_data;
- unsigned int wkb_flags;
- SF_FeatureType ftype;
- /* reset cache */
- if (cache->ctype == CACHE_MAP)
- cache->lines_num++;
- else {
- /* next to be read from cache */
- cache->lines_next = 0;
- cache->lines_num = 1;
- }
- cache->fid = -1;
- if (fparts)
- fparts->n_parts = 0;
- wkb_flags = 0;
- wkb_data = hex_to_wkb(data, &nbytes);
- if (nbytes < 5) {
- /* G_free(wkb_data); */
- if (nbytes > 0) {
- G_debug(3, "Vect__cache_feature_pg(): invalid geometry");
- G_warning(_("Invalid WKB content: %d bytes"), nbytes);
- return SF_UNKNOWN;
- }
- else {
- G_debug(3, "Vect__cache_feature_pg(): no geometry");
- return SF_NONE;
- }
- }
- /* parsing M coordinate not supported */
- memcpy(&wkb_flags, wkb_data + 1, 4);
- byte_order = (wkb_data[0] == 0 ? ENDIAN_BIG : ENDIAN_LITTLE);
- if (byte_order == ENDIAN_BIG)
- wkb_flags = SWAP32(wkb_flags);
- if (wkb_flags & 0x40000000) {
- G_warning(_("Reading EWKB with 4-dimensional coordinates (XYZM) "
- "is not supported"));
- /* G_free(wkb_data); */
- return SF_UNKNOWN;
- }
- /* PostGIS EWKB format includes an SRID, but this won't be
- understood by OGR, so if the SRID flag is set, we remove the
- SRID (bytes at offset 5 to 8).
- */
- if (nbytes > 9 &&
- ((byte_order == ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
- (byte_order == ENDIAN_LITTLE && (wkb_data[4] & 0x20)))) {
- memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
- nbytes -= 4;
- if (byte_order == ENDIAN_BIG)
- wkb_data[1] &= (~0x20);
- else
- wkb_data[4] &= (~0x20);
- }
- if (nbytes < 9 && nbytes != -1) {
- /* G_free(wkb_data); */
- return SF_UNKNOWN;
- }
- /* Get the geometry feature type. For now we assume that geometry
- type is between 0 and 255 so we only have to fetch one byte.
- */
- if (byte_order == ENDIAN_LITTLE) {
- ftype = (SF_FeatureType) wkb_data[1];
- is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
- }
- else {
- ftype = (SF_FeatureType) wkb_data[4];
- is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
- }
- G_debug(3, "Vect__cache_feature_pg(): sf_type = %d", ftype);
- /* allocate space in lines cache - be minimalistic
- more lines require eg. polygon with more rings, multi-features
- or geometry collections
- */
- if (cache->ctype == CACHE_MAP) {
- reallocate_cache(cache, 1, TRUE);
- }
- else {
- if (!cache->lines) {
- reallocate_cache(cache, 1, FALSE);
- }
- }
-
- ret = -1;
- if (ftype == SF_POINT) {
- cache->lines_types[cache->lines_num-1] = force_type == GV_CENTROID ? force_type : GV_POINT;
- ret = point_from_wkb(wkb_data, nbytes, byte_order,
- is3D, cache->lines[cache->lines_num-1]);
- add_fpart(fparts, ftype, 0, 1);
- }
- else if (ftype == SF_LINESTRING) {
- cache->lines_types[cache->lines_num-1] = force_type == GV_BOUNDARY ? force_type : GV_LINE;
- ret = linestring_from_wkb(wkb_data, nbytes, byte_order,
- is3D, cache->lines[cache->lines_num-1], FALSE);
- add_fpart(fparts, ftype, 0, 1);
- }
- else if (ftype == SF_POLYGON && !skip_polygon) {
- int nrings;
- cache->lines_num = 0; /* reset before reading rings */
- ret = polygon_from_wkb(wkb_data, nbytes, byte_order,
- is3D, cache, &nrings);
- add_fpart(fparts, ftype, 0, nrings);
- }
- else if (ftype == SF_MULTIPOINT ||
- ftype == SF_MULTILINESTRING ||
- ftype == SF_MULTIPOLYGON || ftype == SF_GEOMETRYCOLLECTION) {
- ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order,
- is3D, cache, fparts);
- }
- else {
- G_warning(_("Unsupported feature type %d"), ftype);
- }
- if (cache->ctype != CACHE_MAP) {
- /* read next feature from cache */
- cache->lines_next = 0;
- }
- /* G_free(wkb_data); */
- return ret > 0 ? ftype : SF_UNKNOWN;
- }
- /*!
- \brief Read point for WKB data
- See OGRPoint::importFromWkb() from GDAL/OGR library
- \param wkb_data WKB data
- \param nbytes number of bytes (WKB data buffer)
- \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
- \param with_z WITH_Z for 3D data
- \param[out] line_p point geometry (pointer to line_pnts struct)
- \return wkb size
- \return -1 on error
- */
- int point_from_wkb(const unsigned char *wkb_data, int nbytes, int byte_order,
- int with_z, struct line_pnts *line_p)
- {
- double x, y, z;
- if (nbytes < 21 && nbytes != -1)
- return -1;
- /* get vertex */
- memcpy(&x, wkb_data + 5, 8);
- memcpy(&y, wkb_data + 5 + 8, 8);
- if (byte_order == ENDIAN_BIG) {
- SWAPDOUBLE(&x);
- SWAPDOUBLE(&y);
- }
- if (with_z) {
- if (nbytes < 29 && nbytes != -1)
- return -1;
- memcpy(&z, wkb_data + 5 + 16, 8);
- if (byte_order == ENDIAN_BIG) {
- SWAPDOUBLE(&z);
- }
- }
- else {
- z = 0.0;
- }
- if (line_p) {
- Vect_reset_line(line_p);
- Vect_append_point(line_p, x, y, z);
- }
- return 5 + 8 * (with_z == WITH_Z ? 3 : 2);
- }
- /*!
- \brief Read line for WKB data
- See OGRLineString::importFromWkb() from GDAL/OGR library
- \param wkb_data WKB data
- \param nbytes number of bytes (WKB data buffer)
- \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
- \param with_z WITH_Z for 3D data
- \param[out] line_p line geometry (pointer to line_pnts struct)
- \return wkb size
- \return -1 on error
- */
- int linestring_from_wkb(const unsigned char *wkb_data, int nbytes,
- int byte_order, int with_z, struct line_pnts *line_p,
- int is_ring)
- {
- int npoints, point_size, buff_min_size, offset;
- int i;
- double x, y, z;
- if (is_ring)
- offset = 5;
- else
- offset = 0;
- if (is_ring && nbytes < 4 && nbytes != -1)
- return error_corrupted_data(NULL);
- /* get the vertex count */
- memcpy(&npoints, wkb_data + (5 - offset), 4);
- if (byte_order == ENDIAN_BIG) {
- npoints = SWAP32(npoints);
- }
- /* check if the wkb stream buffer is big enough to store fetched
- number of points. 16 or 24 - size of point structure
- */
- point_size = with_z ? 24 : 16;
- if (npoints < 0 || npoints > INT_MAX / point_size)
- return error_corrupted_data(NULL);
- buff_min_size = point_size * npoints;
- if (nbytes != -1 && buff_min_size > nbytes - (9 - offset))
- return error_corrupted_data(_("Length of input WKB is too small"));
- if (line_p)
- Vect_reset_line(line_p);
- /* get the vertex */
- for (i = 0; i < npoints; i++) {
- memcpy(&x, wkb_data + (9 - offset) + i * point_size, 8);
- memcpy(&y, wkb_data + (9 - offset) + 8 + i * point_size, 8);
- if (with_z)
- memcpy(&z, wkb_data + (9 - offset) + 16 + i * point_size, 8);
- else
- z = 0.0;
- if (byte_order == ENDIAN_BIG) {
- SWAPDOUBLE(&x);
- SWAPDOUBLE(&y);
- if (with_z)
- SWAPDOUBLE(&z);
- }
- if (line_p)
- Vect_append_point(line_p, x, y, z);
- }
- return (9 - offset) + (with_z == WITH_Z ? 3 : 2) * 8 * line_p->n_points;
- }
- /*!
- \brief Read polygon for WKB data
- See OGRPolygon::importFromWkb() from GDAL/OGR library
- \param wkb_data WKB data
- \param nbytes number of bytes (WKB data buffer)
- \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
- \param with_z WITH_Z for 3D data
- \param[out] line_p array of rings (pointer to line_pnts struct)
- \param[out] nrings number of rings
- \return wkb size
- \return -1 on error
- */
- int polygon_from_wkb(const unsigned char *wkb_data, int nbytes,
- int byte_order, int with_z,
- struct Format_info_cache *cache, int *nrings)
- {
- int data_offset, i, nsize, isize;
- struct line_pnts *line_i;
- if (nbytes < 9 && nbytes != -1)
- return -1;
- /* get the ring count */
- memcpy(nrings, wkb_data + 5, 4);
- if (byte_order == ENDIAN_BIG) {
- *nrings = SWAP32(*nrings);
- }
- if (*nrings < 0) {
- return -1;
- }
- /* reallocate space for islands if needed */
- reallocate_cache(cache, *nrings, FALSE);
- cache->lines_num += *nrings;
- /* each ring has a minimum of 4 bytes (point count) */
- if (nbytes != -1 && nbytes - 9 < (*nrings) * 4) {
- return error_corrupted_data(_("Length of input WKB is too small"));
- }
- data_offset = 9;
- if (nbytes != -1)
- nbytes -= data_offset;
- /* get the rings */
- nsize = 9;
- for (i = 0; i < (*nrings); i++) {
- if (cache->lines_next >= cache->lines_num)
- G_fatal_error(_("Invalid cache index %d (max: %d)"),
- cache->lines_next, cache->lines_num);
- line_i = cache->lines[cache->lines_next];
- cache->lines_types[cache->lines_next++] = GV_BOUNDARY;
- linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order,
- with_z, line_i, TRUE);
- if (nbytes != -1) {
- isize = 4 + 8 * (with_z == WITH_Z ? 3 : 2) * line_i->n_points;
- nbytes -= isize;
- }
- nsize += isize;
- data_offset += isize;
- }
- return nsize;
- }
- /*!
- \brief Read geometry collection for WKB data
- See OGRGeometryCollection::importFromWkbInternal() from GDAL/OGR library
- \param wkb_data WKB data
- \param nbytes number of bytes (WKB data buffer)
- \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
- \param with_z WITH_Z for 3D data
- \param ipart part to cache (starts at 0)
- \param[out] cache lines cache
- \param[in,out] fparts feature parts (required for building pseudo-topology)
- \return number of parts
- \return -1 on error
- */
- int geometry_collection_from_wkb(const unsigned char *wkb_data, int nbytes,
- int byte_order, int with_z,
- struct Format_info_cache *cache,
- struct feat_parts *fparts)
- {
- int ipart, nparts, data_offset, nsize;
- unsigned char *wkb_subdata;
- SF_FeatureType ftype;
- if (nbytes < 9 && nbytes != -1)
- return error_corrupted_data(NULL);
- /* get the geometry count */
- memcpy(&nparts, wkb_data + 5, 4);
- if (byte_order == ENDIAN_BIG) {
- nparts = SWAP32(nparts);
- }
- if (nparts < 0 || nparts > INT_MAX / 9) {
- return error_corrupted_data(NULL);
- }
- G_debug(5, "\t(geometry collections) parts: %d", nparts);
- /* each geometry has a minimum of 9 bytes */
- if (nbytes != -1 && nbytes - 9 < nparts * 9) {
- return error_corrupted_data(_("Length of input WKB is too small"));
- }
- data_offset = 9;
- if (nbytes != -1)
- nbytes -= data_offset;
- /* reallocate space for parts if needed */
- reallocate_cache(cache, nparts, FALSE);
- /* get parts */
- for (ipart = 0; ipart < nparts; ipart++) {
- wkb_subdata = (unsigned char *)wkb_data + data_offset;
- if (nbytes < 9 && nbytes != -1)
- return error_corrupted_data(NULL);
- if (byte_order == ENDIAN_LITTLE) {
- ftype = (SF_FeatureType) wkb_subdata[1];
- }
- else {
- ftype = (SF_FeatureType) wkb_subdata[4];
- }
- if (ftype == SF_POINT) {
- cache->lines_types[cache->lines_next] = GV_POINT;
- nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
- cache->lines[cache->lines_next]);
- cache->lines_num++;
- add_fpart(fparts, ftype, cache->lines_next, 1);
- cache->lines_next++;
- }
- else if (ftype == SF_LINESTRING) {
- cache->lines_types[cache->lines_next] = GV_LINE;
- nsize =
- linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
- cache->lines[cache->lines_next], FALSE);
- cache->lines_num++;
- add_fpart(fparts, ftype, cache->lines_next, 1);
- cache->lines_next++;
- }
- else if (ftype == SF_POLYGON) {
- int idx, nrings;
- idx = cache->lines_next;
- nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order,
- with_z, cache, &nrings);
- add_fpart(fparts, ftype, idx, nrings);
- }
- else if (ftype == SF_GEOMETRYCOLLECTION ||
- ftype == SF_MULTIPOLYGON ||
- ftype == SF_MULTILINESTRING || ftype == SF_MULTIPOLYGON) {
- geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
- with_z, cache, fparts);
- }
- else {
- G_warning(_("Unsupported feature type %d"), ftype);
- }
- if (nbytes != -1) {
- nbytes -= nsize;
- }
- data_offset += nsize;
- }
- return nparts;
- }
- /*!
- \brief Report error message
- \param msg message (NULL)
- \return -1
- */
- int error_corrupted_data(const char *msg)
- {
- if (msg)
- G_warning(_("Corrupted data. %s."), msg);
- else
- G_warning(_("Corrupted data"));
- return -1;
- }
- /*!
- \brief Create select cursor for sequential access (internal use only)
-
- Allocated cursor name should be freed by G_free().
-
- \param pg_info pointer to Format_info_pg struct
- \param fetch_all TRUE to fetch all records
- \param[out] cursor name
-
- \return 0 on success
- \return -1 on failure
- */
- int Vect__open_cursor_next_line_pg(struct Format_info_pg *pg_info, int fetch_all)
- {
- char stmt[DB_SQL_MAX];
-
- if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
- return -1;
-
- /* set cursor name */
- G_asprintf(&(pg_info->cursor_name),
- "%s_%s_%p", pg_info->schema_name, pg_info->table_name, pg_info->conn);
-
- if (!pg_info->toposchema_name) {
- /* simple feature access (geom, fid) */
- /* TODO: start_fid */
- sprintf(stmt,
- "DECLARE %s CURSOR FOR SELECT %s,%s FROM \"%s\".\"%s\" ORDER BY %s",
- pg_info->cursor_name, pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
- pg_info->table_name, pg_info->fid_column);
- }
- else {
- /* topology access (geom,fid,type) */
- /* TODO: optimize SQL statement (for points/centroids) */
- sprintf(stmt,
- "DECLARE %s CURSOR FOR "
- "SELECT geom,id,type,fid FROM ("
- "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt "
- "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 1 AND (%s).id = node_id "
- "WHERE containing_face IS NULL AND node_id NOT IN "
- "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL "
- "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL "
- "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt "
- "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 3 AND (%s).id = containing_face "
- "WHERE containing_face IS NOT NULL AND node_id NOT IN "
- "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL "
- "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL "
- "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt "
- "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id "
- "WHERE left_face = 0 AND right_face = 0 UNION ALL "
- "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt "
- "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id "
- "WHERE left_face != 0 OR right_face != 0 ) AS foo ORDER BY type,id",
- pg_info->cursor_name,
- GV_POINT, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
- pg_info->topogeom_column, pg_info->topogeom_column, pg_info->toposchema_name, pg_info->toposchema_name,
- GV_CENTROID, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
- pg_info->topogeom_column, pg_info->topogeom_column, pg_info->toposchema_name, pg_info->toposchema_name,
- GV_LINE, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
- pg_info->topogeom_column, pg_info->topogeom_column,
- GV_BOUNDARY, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
- pg_info->topogeom_column, pg_info->topogeom_column);
- }
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- if (fetch_all)
- sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
- else
- sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
- G_debug(3, "SQL: %s", stmt);
- pg_info->res = PQexec(pg_info->conn, stmt); /* fetch records from select cursor */
- if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
- error_tuples(pg_info);
- return -1;
- }
- pg_info->next_line = 0;
- return 0;
- }
- /*!
- \brief Open select cursor for random access (internal use only)
- Fetch number of feature (given by CURSOR_PAGE) starting with
- <em>fid</em>.
- Allocated cursor name should be freed by G_free().
- \param pg_info pointer to Format_info_pg struct
- \param fid feature id to get
- \param type feature type
- \return 0 on success
- \return -1 on failure
- */
- int Vect__open_cursor_line_pg(struct Format_info_pg *pg_info, int fid, int type)
- {
- char stmt[DB_SQL_MAX];
-
- G_debug(3, "Vect__open_cursor_line_pg(): fid range = %d-%d, type = %d",
- fid, fid + CURSOR_PAGE, type);
- if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
- return -1;
- pg_info->cursor_fid = fid;
- G_asprintf(&(pg_info->cursor_name),
- "%s_%s_%d_%p", pg_info->schema_name, pg_info->table_name, fid, pg_info->conn);
-
- if (!pg_info->toposchema_name) {
- /* simple feature access (geom) */
- sprintf(stmt,
- "DECLARE %s CURSOR FOR SELECT %s FROM \"%s\".\"%s\" "
- "WHERE %s BETWEEN %d AND %d ORDER BY %s", pg_info->cursor_name,
- pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
- pg_info->fid_column, fid, fid + CURSOR_PAGE, pg_info->fid_column);
- }
- else {
- /* topological access */
- if (!(type & (GV_POINTS | GV_LINES))) {
- G_warning(_("Unsupported feature type %d"), type);
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
-
- if (type & GV_POINTS) {
- /* points (geom,containing_face) */
- sprintf(stmt,
- "DECLARE %s CURSOR FOR SELECT geom,containing_face "
- " FROM \"%s\".node WHERE node_id BETWEEN %d AND %d ORDER BY node_id",
- pg_info->cursor_name,
- pg_info->toposchema_name, fid, fid + CURSOR_PAGE);
- }
- else {
- /* edges (geom,left_face,right_face) */
- sprintf(stmt,
- "DECLARE %s CURSOR FOR SELECT geom,left_face,right_face "
- " FROM \"%s\".edge WHERE edge_id BETWEEN %d AND %d ORDER BY edge_id",
- pg_info->cursor_name,
- pg_info->toposchema_name, fid, fid + CURSOR_PAGE);
- }
- }
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- pg_info->next_line = 0;
- sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
- pg_info->res = PQexec(pg_info->conn, stmt);
- if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
- error_tuples(pg_info);
- return -1;
- }
-
- return 0;
- }
- /*!
- \brief Close select cursor
- \param pg_info pointer to Format_info_pg struct
- \return 0 on success
- \return -1 on failure
- */
- int Vect__close_cursor_pg(struct Format_info_pg *pg_info)
- {
- if (pg_info->res) {
- PQclear(pg_info->res);
- pg_info->res = NULL;
- }
-
- if (pg_info->cursor_name) {
- char stmt[DB_SQL_MAX];
-
- sprintf(stmt, "CLOSE %s", pg_info->cursor_name);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to close cursor %s"), pg_info->cursor_name);
- return -1;
- }
- Vect__execute_pg(pg_info->conn, "COMMIT");
- G_free(pg_info->cursor_name);
- pg_info->cursor_name = NULL;
- }
-
- return 0;
- }
- /*!
- \brief Select feature (internal use only)
- \param pg_info pointer to Format_info_pg struct
- \param fid feature id to get
- \param type feature type
- \return 0 on success
- \return -1 on failure
- */
- int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
- {
- char stmt[DB_SQL_MAX];
-
- if (!pg_info->toposchema_name) {
- /* simple feature access */
- sprintf(stmt,
- "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
- pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
- pg_info->fid_column, fid);
- }
- else {
- /* topological access */
- if (!(type & (GV_POINTS | GV_LINES))) {
- G_warning(_("Unsupported feature type %d"), type);
- return -1;
- }
-
- if (type & GV_POINTS) {
- int topotype;
- char *nodeid;
-
- if (type == GV_POINT) {
- topotype = 1;
- nodeid = pg_info->fid_column;
- }
- else { /* assuming GV_CENTROID */
- topotype = 3;
- nodeid = "containing_face";
- }
-
- sprintf(stmt,
- "SELECT tt.geom,tt.containing_face,ft.%s FROM \"%s\".node AS tt "
- "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = %d and (%s).id = %s "
- "WHERE node_id = %d",
- pg_info->fid_column, pg_info->toposchema_name,
- pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
- topotype, pg_info->topogeom_column, nodeid, fid);
- }
- else {
- sprintf(stmt,
- "SELECT tt.geom,tt.left_face,tt.right_face,ft.%s FROM \"%s\".edge AS tt "
- "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 and (%s).id = edge_id "
- "WHERE edge_id = %d",
- pg_info->fid_column, pg_info->toposchema_name,
- pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
- pg_info->topogeom_column, fid);
- }
- }
- G_debug(3, "SQL: %s", stmt);
-
- pg_info->next_line = 0;
-
- pg_info->res = PQexec(pg_info->conn, stmt);
- if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
- error_tuples(pg_info);
- return -1;
- }
-
- return 0;
- }
- /*!
- \brief Execute SQL statement
- See pg_local_proto.h
- \param conn pointer to PGconn
- \param stmt query
- \return 0 on success
- \return -1 on error
- */
- int Vect__execute_pg(PGconn * conn, const char *stmt)
- {
- PGresult *result;
- result = NULL;
- G_debug(3, "Vect__execute_pg(): %s", stmt);
- result = PQexec(conn, stmt);
- if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
- PQclear(result);
-
- G_warning(_("Execution failed: %s\nReason: %s"), stmt,
- PQerrorMessage(conn));
- return -1;
- }
- PQclear(result);
- return 0;
- }
- /*!
- \brief Execute SQL statement and get value.
- \param conn pointer to PGconn
- \param stmt query
- \return value on success
- \return -1 on error
- */
- int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
- {
- int ret;
- PGresult *result;
- result = NULL;
- G_debug(3, "Vect__execute_get_value_pg(): %s", stmt);
- result = PQexec(conn, stmt);
- if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
- PQntuples(result) != 1) {
- PQclear(result);
- G_warning(_("Execution failed: %s\nReason: %s"), stmt,
- PQerrorMessage(conn));
- return -1;
- }
- ret = atoi(PQgetvalue(result, 0, 0));
- PQclear(result);
-
- return ret;
- }
- /*!
- \brief Reallocate lines cache
- */
- void reallocate_cache(struct Format_info_cache *cache, int num, int incr)
- {
- int i;
- if (!incr && cache->lines_alloc >= num)
- return;
- if (!incr && !cache->lines) {
- /* most of features requires only one line cache */
- cache->lines_alloc = 1;
- }
- else {
- cache->lines_alloc += num;
- }
- cache->lines = (struct line_pnts **)G_realloc(cache->lines,
- cache->lines_alloc *
- sizeof(struct line_pnts *));
- cache->lines_types = (int *)G_realloc(cache->lines_types,
- cache->lines_alloc * sizeof(int));
- cache->lines_cats = (int *)G_realloc(cache->lines_cats,
- cache->lines_alloc * sizeof(int));
- if (cache->lines_alloc > 1) {
- for (i = cache->lines_alloc - num; i < cache->lines_alloc; i++) {
- cache->lines[i] = Vect_new_line_struct();
- cache->lines_types[i] = -1;
- cache->lines_cats[i] = -1;
- }
- }
- else {
- cache->lines[0] = Vect_new_line_struct();
- cache->lines_types[0] = -1;
- cache->lines_cats[0] = -1;
- }
- }
- void add_fpart(struct feat_parts *fparts, SF_FeatureType ftype,
- int idx, int nlines)
- {
- if (!fparts)
- return;
- if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
- if (fparts->a_parts == 0)
- fparts->a_parts = 1;
- else
- fparts->a_parts += fparts->n_parts;
- fparts->ftype = (SF_FeatureType *) G_realloc(fparts->ftype,
- fparts->a_parts *
- sizeof(SF_FeatureType));
- fparts->nlines =
- (int *)G_realloc(fparts->nlines, fparts->a_parts * sizeof(int));
- fparts->idx =
- (int *)G_realloc(fparts->idx, fparts->a_parts * sizeof(int));
- }
- fparts->ftype[fparts->n_parts] = ftype;
- fparts->idx[fparts->n_parts] = idx;
- fparts->nlines[fparts->n_parts] = nlines;
- fparts->n_parts++;
- }
- /*
- \brief Get centroid
-
- \param pg_info pointer to Format_info_pg
- \param centroid centroid id
- \param[out] line_p output geometry
- \return GV_CENTROID on success
- \return -1 on error
- */
- int get_centroid(struct Map_info *Map, int centroid,
- struct line_pnts *line_p)
- {
- int i, found;
- struct bound_box box;
- struct boxlist list;
- struct P_line *Line;
- struct P_topo_c *topo;
-
- Line = Map->plus.Line[centroid];
- topo = (struct P_topo_c *)Line->topo;
-
- /* get area bbox */
- Vect_get_area_box(Map, topo->area, &box);
- /* search in spatial index for centroid with area bbox */
- dig_init_boxlist(&list, TRUE);
- Vect_select_lines_by_box(Map, &box, Line->type, &list);
-
- found = -1;
- for (i = 0; i < list.n_values; i++) {
- if (list.id[i] == centroid) {
- found = i;
- break;
- }
- }
-
- if (found == -1)
- return -1;
-
- if (line_p) {
- Vect_reset_line(line_p);
- Vect_append_point(line_p, list.box[found].E, list.box[found].N, 0.0);
- }
-
- return GV_CENTROID;
- }
- void error_tuples(struct Format_info_pg *pg_info)
- {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- G_warning(_("Unable to read features. Reason:\n%s"),
- PQresultErrorMessage(pg_info->res));
- if (pg_info->res) {
- PQclear(pg_info->res);
- pg_info->res = NULL;
- }
- }
- #endif
|