32 #include "local_proto.h" 35 #include "pg_local_proto.h" 39 static unsigned char *wkb_data;
40 static unsigned int wkb_data_length;
42 static int read_next_line_pg(
struct Map_info *,
45 static unsigned char *hex_to_wkb(
const char *,
int *);
46 static int point_from_wkb(
const unsigned char *,
int,
int,
int,
48 static int linestring_from_wkb(
const unsigned char *,
int,
int,
int,
50 static int polygon_from_wkb(
const unsigned char *,
int,
int,
int,
52 static int geometry_collection_from_wkb(
const unsigned char *,
int,
int,
int,
55 static int error_corrupted_data(
const char *);
56 static void add_fpart(
struct feat_parts *,
SF_FeatureType,
int,
int);
86 G_debug(3,
"V1_read_next_line_pg()");
89 return read_next_line_pg(Map, line_p, line_c,
FALSE);
122 G_debug(3,
"V2_read_next_line_pg()");
152 G_debug(4,
"Determine centroid for simple features");
154 if (line_p !=
NULL) {
167 for (i = 0; i < list.
n_values; i++) {
168 if (list.
id[i] == line) {
177 list.
box[found].
N, 0.0);
180 if (line_c !=
NULL) {
190 ret = read_next_line_pg(Map, line_p, line_c,
TRUE);
191 if (ret != Line->
type) {
192 G_warning(
_(
"Unexpected feature type (%d) - should be (%d)"),
214 G_fatal_error(
_(
"GRASS is not compiled with PostgreSQL support"));
249 G_debug(3,
"V1_read_line_pg(): offset = %lu offset_num = %lu",
267 G_debug(3,
"read (%s) feature (fid = %ld) to cache",
272 G_warning(
_(
"Feature %ld without geometry skipped"), fid);
288 G_debug(3,
"read feature part: %d -> type = %d", ipart, type);
298 G_fatal_error(
_(
"GRASS is not compiled with PostgreSQL support"));
331 G_warning(
_(
"Attempt to access feature with invalid id (%d)"), line);
337 G_warning(
_(
"Attempt to access dead feature %d"), line);
344 if (!line_p && !line_c)
351 return get_centroid(Map, line, line_p, line_c);
362 cache_idx = line - 1;
365 G_fatal_error(
_(
"Requesting invalid feature from cache (%d). Number of features in cache: %d"),
368 G_warning(
_(
"Feature %d: unexpected type (%d) - should be %d"),
378 G_warning(
_(
"Feature %d without geometry skipped"), line);
400 if (!PQgetisnull(pg_info->
res, 0, col_idx))
402 atoi(PQgetvalue(pg_info->
res, 0, col_idx));
416 G_fatal_error(
_(
"GRASS is not compiled with PostgreSQL support"));
436 int read_next_line_pg(
struct Map_info *Map,
438 int ignore_constraints)
475 if ((
int)sf_type < 0) {
482 G_warning(
_(
"Feature without geometry. Skipped."));
498 }
while (iline ==
NULL);
500 G_debug(4,
"read next cached line %d (type = %d)",
575 G_warning(
_(
"No geometry or topo geometry column defined"));
586 "Primary key not defined."));
590 #ifdef USE_CURSOR_RND 610 if (PQntuples(pg_info->
res) == CURSOR_PAGE &&
614 PQclear(pg_info->
res);
616 sprintf(stmt,
"FETCH %d in %s", CURSOR_PAGE, pg_info->
cursor_name);
618 pg_info->
res = PQexec(pg_info->
conn, stmt);
619 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
620 error_tuples(pg_info);
640 seq_type = atoi(PQgetvalue(pg_info->
res, pg_info->
next_line, 2));
651 strlen(PQgetvalue(pg_info->
res, pg_info->
next_line, 1)) != 0)
652 G_warning(
_(
"Inconsistency in topology: detected centroid (should be point)"));
655 int left_face, right_face;
657 left_face = atoi(PQgetvalue(pg_info->
res, pg_info->
next_line, 1));
658 right_face = atoi(PQgetvalue(pg_info->
res, pg_info->
next_line, 2));
661 (left_face != 0 || right_face != 0))
662 G_warning(
_(
"Inconsistency in topology: detected boundary (should be line)"));
668 data = (
char *)PQgetvalue(pg_info->
res, pg_info->
next_line, 0);
679 col_idx = fid < 0 ? 3 : 2;
681 if (!PQgetisnull(pg_info->
res, pg_info->
next_line, col_idx))
682 cat = atoi(PQgetvalue(pg_info->
res, pg_info->
next_line, col_idx));
711 unsigned char *hex_to_wkb(
const char *hex_data,
int *nbytes)
716 length = strlen(hex_data) / 2 + 1;
717 if (length > wkb_data_length) {
718 wkb_data_length = length;
719 wkb_data =
G_realloc(wkb_data, wkb_data_length);
722 *nbytes = length - 1;
723 for (i = 0; i < (*nbytes); i++) {
725 (
unsigned char)((hex_data[2 * i] >
726 'F' ? hex_data[2 * i] - 0x57 : hex_data[2 * i] >
727 '9' ? hex_data[2 * i] - 0x37 : hex_data[2 * i] -
730 (
unsigned char)(hex_data[2 * i + 1] >
731 'F' ? hex_data[2 * i + 1] -
732 0x57 : hex_data[2 * i + 1] >
733 '9' ? hex_data[2 * i + 1] -
734 0x37 : hex_data[2 * i + 1] - 0x30);
737 wkb_data[(*nbytes)] = 0;
760 struct feat_parts * fparts)
762 int ret, byte_order, nbytes, is3D;
763 unsigned char *wkb_data;
764 unsigned int wkb_flags;
768 if (cache->
ctype == CACHE_MAP)
781 wkb_data = hex_to_wkb(data, &nbytes);
786 G_debug(3,
"Vect__cache_feature_pg(): invalid geometry");
787 G_warning(
_(
"Invalid WKB content: %d bytes"), nbytes);
791 G_debug(3,
"Vect__cache_feature_pg(): no geometry");
797 memcpy(&wkb_flags, wkb_data + 1, 4);
800 wkb_flags = SWAP32(wkb_flags);
802 if (wkb_flags & 0x40000000) {
803 G_warning(
_(
"Reading EWKB with 4-dimensional coordinates (XYZM) " 804 "is not supported"));
814 ((byte_order ==
ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
816 memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
819 wkb_data[1] &= (~0x20);
821 wkb_data[4] &= (~0x20);
824 if (nbytes < 9 && nbytes != -1) {
834 is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
838 is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
840 G_debug(3,
"Vect__cache_feature_pg(): sf_type = %d", ftype);
847 if (cache->
ctype == CACHE_MAP) {
859 ret = point_from_wkb(wkb_data, nbytes, byte_order,
861 add_fpart(fparts, ftype, 0, 1);
865 ret = linestring_from_wkb(wkb_data, nbytes, byte_order,
867 add_fpart(fparts, ftype, 0, 1);
869 else if (ftype ==
SF_POLYGON && !skip_polygon) {
873 ret = polygon_from_wkb(wkb_data, nbytes, byte_order,
874 is3D, cache, &nrings);
875 add_fpart(fparts, ftype, 0, nrings);
880 ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order,
881 is3D, cache, fparts);
884 G_warning(
_(
"Unsupported feature type %d"), ftype);
887 if (cache->
ctype != CACHE_MAP) {
911 int point_from_wkb(
const unsigned char *wkb_data,
int nbytes,
int byte_order,
916 if (nbytes < 21 && nbytes != -1)
920 memcpy(&x, wkb_data + 5, 8);
921 memcpy(&y, wkb_data + 5 + 8, 8);
929 if (nbytes < 29 && nbytes != -1)
932 memcpy(&z, wkb_data + 5 + 16, 8);
946 return 5 + 8 * (with_z ==
WITH_Z ? 3 : 2);
963 int linestring_from_wkb(
const unsigned char *wkb_data,
int nbytes,
964 int byte_order,
int with_z,
struct line_pnts *line_p,
967 int npoints, point_size, buff_min_size,
offset;
976 if (is_ring && nbytes < 4 && nbytes != -1)
977 return error_corrupted_data(
NULL);
980 memcpy(&npoints, wkb_data + (5 - offset), 4);
983 npoints = SWAP32(npoints);
989 point_size = with_z ? 24 : 16;
990 if (npoints < 0 || npoints > INT_MAX / point_size)
991 return error_corrupted_data(
NULL);
993 buff_min_size = point_size * npoints;
995 if (nbytes != -1 && buff_min_size > nbytes - (9 - offset))
996 return error_corrupted_data(
_(
"Length of input WKB is too small"));
1002 for (i = 0; i < npoints; i++) {
1003 memcpy(&x, wkb_data + (9 - offset) + i * point_size, 8);
1004 memcpy(&y, wkb_data + (9 - offset) + 8 + i * point_size, 8);
1006 memcpy(&z, wkb_data + (9 - offset) + 16 + i * point_size, 8);
1021 return (9 - offset) + (with_z ==
WITH_Z ? 3 : 2) * 8 * line_p->
n_points;
1039 int polygon_from_wkb(
const unsigned char *wkb_data,
int nbytes,
1040 int byte_order,
int with_z,
1043 int data_offset, i, nsize,
isize;
1047 if (nbytes < 9 && nbytes != -1)
1051 memcpy(nrings, wkb_data + 5, 4);
1053 *nrings = SWAP32(*nrings);
1058 num_of_rings = *nrings;
1065 if (nbytes != -1 && nbytes - 9 < num_of_rings * 4) {
1066 return error_corrupted_data(
_(
"Length of input WKB is too small"));
1071 nbytes -= data_offset;
1075 for (i = 0; i < num_of_rings; i++) {
1082 linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order,
1083 with_z, line_i,
TRUE);
1091 data_offset +=
isize;
1113 int geometry_collection_from_wkb(
const unsigned char *wkb_data,
int nbytes,
1114 int byte_order,
int with_z,
1116 struct feat_parts *fparts)
1118 int ipart, nparts, data_offset, nsize;
1119 unsigned char *wkb_subdata;
1122 if (nbytes < 9 && nbytes != -1)
1123 return error_corrupted_data(
NULL);
1126 memcpy(&nparts, wkb_data + 5, 4);
1128 nparts = SWAP32(nparts);
1130 if (nparts < 0 || nparts > INT_MAX / 9) {
1131 return error_corrupted_data(
NULL);
1133 G_debug(5,
"\t(geometry collections) parts: %d", nparts);
1136 if (nbytes != -1 && nbytes - 9 < nparts * 9) {
1137 return error_corrupted_data(
_(
"Length of input WKB is too small"));
1142 nbytes -= data_offset;
1148 for (ipart = 0; ipart < nparts; ipart++) {
1149 wkb_subdata = (
unsigned char *)wkb_data + data_offset;
1150 if (nbytes < 9 && nbytes != -1)
1151 return error_corrupted_data(
NULL);
1162 nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1165 add_fpart(fparts, ftype, cache->
lines_next, 1);
1171 linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1174 add_fpart(fparts, ftype, cache->
lines_next, 1);
1181 nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order,
1182 with_z, cache, &nrings);
1183 add_fpart(fparts, ftype, idx, nrings);
1188 geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
1189 with_z, cache, fparts);
1192 G_warning(
_(
"Unsupported feature type %d"), ftype);
1199 data_offset += nsize;
1212 int error_corrupted_data(
const char *msg)
1248 if (pg_info->
where) {
1256 "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM \"%s\".\"%s\" WHERE \"%s\"=%s ORDER BY \"%s\"",
1263 "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM \"%s\".\"%s\" ORDER BY \"%s\"",
1272 "DECLARE %s CURSOR FOR " 1273 "SELECT geom,id,type,fid FROM (" 1274 "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt " 1275 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 1 AND (%s).id = node_id " 1276 "WHERE containing_face IS NULL AND node_id NOT IN " 1277 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL " 1278 "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL " 1279 "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt " 1280 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 3 AND (%s).id = %s " 1281 "WHERE containing_face IS NOT NULL AND node_id NOT IN " 1282 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL " 1283 "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL " 1284 "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt " 1285 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id " 1286 "WHERE left_face = 0 AND right_face = 0 UNION ALL " 1287 "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt " 1288 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id " 1289 "WHERE left_face != 0 OR right_face != 0 ) AS foo ORDER BY type,id",
1308 sprintf(stmt,
"FETCH ALL in %s", pg_info->
cursor_name);
1310 sprintf(stmt,
"FETCH %d in %s", CURSOR_PAGE, pg_info->
cursor_name);
1312 pg_info->
res = PQexec(pg_info->
conn, stmt);
1313 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
1314 error_tuples(pg_info);
1341 G_debug(3,
"Vect__open_cursor_line_pg(): fid range = %d-%d, type = %d",
1342 fid, fid + CURSOR_PAGE, type);
1354 "DECLARE %s CURSOR FOR SELECT %s FROM \"%s\".\"%s\" " 1355 "WHERE %s BETWEEN %d AND %d ORDER BY %s", pg_info->
cursor_name,
1362 G_warning(
_(
"Unsupported feature type %d"), type);
1370 "DECLARE %s CURSOR FOR SELECT geom,containing_face " 1371 " FROM \"%s\".node WHERE node_id BETWEEN %d AND %d ORDER BY node_id",
1378 "DECLARE %s CURSOR FOR SELECT geom,left_face,right_face " 1379 " FROM \"%s\".edge WHERE edge_id BETWEEN %d AND %d ORDER BY edge_id",
1390 sprintf(stmt,
"FETCH ALL in %s", pg_info->
cursor_name);
1391 pg_info->
res = PQexec(pg_info->
conn, stmt);
1392 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
1393 error_tuples(pg_info);
1411 PQclear(pg_info->
res);
1448 "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
1455 G_warning(
_(
"Unsupported feature type %d"), type);
1469 nodeid =
"containing_face";
1473 "SELECT tt.geom,tt.containing_face,ft.%s FROM \"%s\".node AS tt " 1474 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = %d and (%s).id = %s " 1475 "WHERE node_id = %d",
1482 "SELECT tt.geom,tt.left_face,tt.right_face,ft.%s FROM \"%s\".edge AS tt " 1483 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 and (%s).id = edge_id " 1484 "WHERE edge_id = %d",
1494 pg_info->
res = PQexec(pg_info->
conn, stmt);
1495 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
1496 error_tuples(pg_info);
1520 G_debug(3,
"Vect__execute_pg(): %s", stmt);
1521 result = PQexec(conn, stmt);
1522 if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
1527 stmt_len = strlen(stmt);
1528 strncpy(stmt_prt, stmt, stmt_len > 511 ? 511 : stmt_len);
1529 stmt_prt[stmt_len > 511 ? 511 : stmt_len] =
'\0';
1530 G_warning(
_(
"Execution failed: %s (...)\nReason: %s"), stmt_prt,
1531 PQerrorMessage(conn));
1555 G_debug(3,
"Vect__execute_get_value_pg(): %s", stmt);
1556 result = PQexec(conn, stmt);
1557 if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
1558 PQntuples(result) != 1) {
1561 G_warning(
_(
"Execution failed: %s\nReason: %s"), stmt,
1562 PQerrorMessage(conn));
1566 ret = atoi(PQgetvalue(result, 0, 0));
1582 if (!incr && !cache->
lines) {
1599 for (i = cache->
lines_alloc - num; i < cache->lines_alloc; i++) {
1613 int idx,
int nlines)
1618 if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
1619 if (fparts->a_parts == 0)
1620 fparts->a_parts = 1;
1622 fparts->a_parts += fparts->n_parts;
1628 (
int *)
G_realloc(fparts->nlines, fparts->a_parts *
sizeof(
int));
1630 (
int *)
G_realloc(fparts->idx, fparts->a_parts *
sizeof(
int));
1633 fparts->ftype[fparts->n_parts] = ftype;
1634 fparts->idx[fparts->n_parts] = idx;
1635 fparts->nlines[fparts->n_parts] = nlines;
1650 int get_centroid(
struct Map_info *Map,
int centroid,
1669 for (i = 0; i < list.
n_values; i++) {
1670 if (list.
id[i] == centroid) {
1693 G_warning(
_(
"Unable to read features. Reason:\n%s"),
1694 PQresultErrorMessage(pg_info->
res));
1697 PQclear(pg_info->
res);
int Vect_get_area_box(const struct Map_info *, int, struct bound_box *)
Get bounding box of area.
int Vect_append_points(struct line_pnts *, const struct line_pnts *, int)
Appends points to the end of a line.
#define ENDIAN_LITTLE
Endian check.
#define GV_FORWARD
Line direction indicator forward/backward.
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
int built
Highest level of topology currently available.
plus_t area
Area number, negative for duplicate centroid.
int Vect__open_cursor_line_pg(struct Format_info_pg *pg_info, int fid, int type)
Open select cursor for random access (internal use only)
off_t offset
Offset in coor file for line.
struct P_line ** Line
Array of vector geometries.
int region_flag
Non-zero value to enable region constraint.
int V1_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c)
Read next feature from PostGIS layer. Skip empty features (level 1 without topology). t This function implements sequential access.
int n_points
Number of points.
void G_free(void *)
Free allocated memory.
struct Format_info fInfo
Format info for non-native formats.
char ** G_tokenize(const char *, const char *)
Tokenize string.
void Vect_line_box(const struct line_pnts *, struct bound_box *)
Get bounding box of line.
int type
Feature type constraint.
int n_values
Number of items in the list.
#define GV_POINT
Feature types used in memory on run time (may change)
struct bound_box * box
Array of bounding boxes.
int Vect__open_cursor_next_line_pg(struct Format_info_pg *pg_info, int fetch_all, int built_level)
Create select cursor for sequential access (internal use only)
plus_t n_lines
Current number of lines.
void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
Reallocate lines cache.
int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, int line)
Read feature from PostGIS layer on topological level.
int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
Execute SQL statement and get value.
int Vect_select_lines_by_box(struct Map_info *, const struct bound_box *, int, struct boxlist *)
Select lines with bounding boxes by box.
Feature geometry info - coordinates.
plus_t next_line
Feature id for sequential access.
struct Map_info::@11 constraint
Constraints for sequential feature access.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
int Vect__close_cursor_pg(struct Format_info_pg *pg_info)
Close select cursor.
void * topo
Topology info.
int Vect_get_constraint_box(const struct Map_info *, struct bound_box *)
Get constraint box.
int G_number_of_tokens(char **)
Return number of tokens.
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
Select feature (internal use only)
struct Plus_head plus
Plus info (topology, version, ...)
SF_FeatureType get_feature(struct Map_info *, int, int)
Read feature geometry.
int Vect_reset_cats(struct line_cats *)
Reset category structure to make sure cats structure is clean to be re-used.
int dig_init_boxlist(struct boxlist *, int)
int type_flag
Non-zero value to enable feature type constraint.
struct dig_head head
Header info.
int V2_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c)
Read next feature from PostGIS layer on topological level (simple feature access).
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon, int force_type, struct Format_info_cache *cache, struct feat_parts *fparts)
Read geometry from HEX data.
List of bounding boxes with id.
void G_free_tokens(char **)
Free memory allocated to tokens.
void G_warning(const char *,...) __attribute__((format(printf
off_t last_offset
Offset of last read line.
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
int G_asprintf(char **, const char *,...) __attribute__((format(printf
int Vect_box_overlap(const struct bound_box *, const struct bound_box *)
Tests for overlap of two boxes.
int V1_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, off_t offset)
Read feature from PostGIS layer at given offset (level 1 without topology)
int G_debug(int, const char *,...) __attribute__((format(printf
void Vect_reset_line(struct line_pnts *)
Reset line.
int Vect_cat_set(struct line_cats *, int, int)
Add new field/cat to category structure if doesn't exist yet.
SF_FeatureType
Simple feature types.