1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702 |
- /*!
- \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 Map_info *, 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 add_fpart(struct feat_parts *, SF_FeatureType, int, int);
- static int get_centroid(struct Map_info *, int, struct line_pnts *, struct line_cats *);
- 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(Map, 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, line_c);
- }
-
- /* 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(Map, 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 = fid;
- }
- 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(Map, -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_GEOMETRY || 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] Map pointer to Map_info 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 Map_info *Map, int fid, int type)
- {
- int seq_type;
- int force_type; /* force type (GV_BOUNDARY or GV_CENTROID) for topo access only */
- char *data;
-
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
-
- 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, Map->plus.built) != 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 cat, col_idx;
- col_idx = fid < 0 ? 3 : 2; /* TODO: dermine col_idx for random access */
-
- if (!PQgetisnull(pg_info->res, pg_info->next_line, col_idx))
- cat = atoi(PQgetvalue(pg_info->res, pg_info->next_line, col_idx));
- else
- cat = -1; /* no cat */
- pg_info->cache.lines_cats[pg_info->cache.lines_next] = cat;
- G_debug(3, "line=%d, type=%d -> cat=%d", pg_info->cache.lines_next+1,
- pg_info->cache.lines_types[pg_info->cache.lines_next], 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_GEOMETRY 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_GEOMETRY;
- }
- 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_GEOMETRY;
- }
- /* 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_GEOMETRY;
- }
- /* 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) {
- Vect__reallocate_cache(cache, 1, TRUE);
- }
- else {
- if (!cache->lines) {
- Vect__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_GEOMETRY;
- }
- /*!
- \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;
- int num_of_rings;
- 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;
- }
- num_of_rings = *nrings;
-
- /* reallocate space for islands if needed */
- Vect__reallocate_cache(cache, num_of_rings, FALSE);
- cache->lines_num += num_of_rings;
- /* each ring has a minimum of 4 bytes (point count) */
- if (nbytes != -1 && nbytes - 9 < num_of_rings * 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 < num_of_rings; 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 */
- Vect__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, int built_level)
- {
- 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 */
- if (pg_info->where) {
- /* set attribute filter if where sql statement defined */
- char **tokens = G_tokenize(pg_info->where, "=");
- if (G_number_of_tokens(tokens) != 2) {
- G_warning(_("Unable to parse '%s'"), pg_info->where);
- return -1;
- }
- sprintf(stmt,
- "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM \"%s\".\"%s\" WHERE \"%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, tokens[0], tokens[1], pg_info->fid_column);
- G_free_tokens(tokens);
- }
- else {
- 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,id,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 = %s "
- "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,
- built_level >= GV_BUILD_CENTROIDS ? "containing_face" : "node_id",
- 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) {
- size_t stmt_len;
- char stmt_prt[512];
-
- PQclear(result);
- stmt_len = strlen(stmt);
- strncpy(stmt_prt, stmt, stmt_len > 511 ? 511 : stmt_len);
- stmt_prt[stmt_len > 511 ? 511 : stmt_len] = '\0';
- G_warning(_("Execution failed: %s (...)\nReason: %s"), stmt_prt,
- 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 Vect__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, struct line_cats *line_c)
- {
- 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);
- }
- if (line_c) {
- Vect_cat_set(line_c, 1, Line->offset);
- }
-
- 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
|