33 #include "local_proto.h"
36 #include "pg_local_proto.h"
40 static unsigned char *wkb_data;
41 static unsigned int wkb_data_length;
46 static unsigned char *hex_to_wkb(
const char *,
int *);
47 static int point_from_wkb(
const unsigned char *,
int,
int,
int,
49 static int linestring_from_wkb(
const unsigned char *,
int,
int,
int,
51 static int polygon_from_wkb(
const unsigned char *,
int,
int,
int,
53 static int geometry_collection_from_wkb(
const unsigned char *,
int,
int,
int,
56 static int error_corrupted_data(
const char *);
57 static void add_fpart(
struct feat_parts *,
SF_FeatureType,
int,
int);
64 #define NOPG_UNUSED UNUSED
93 G_debug(3,
"V1_read_next_line_pg()");
96 return read_next_line_pg(Map, line_p, line_c,
FALSE);
130 G_debug(3,
"V2_read_next_line_pg()");
132 pg_info = &(Map->fInfo.pg);
134 if (Map->constraint.region_flag)
139 line = Map->next_line;
141 if (Map->next_line > Map->plus.n_lines)
144 Line = Map->plus.Line[line];
150 if (Map->constraint.type_flag) {
152 if (!(Line->
type & Map->constraint.type)) {
159 G_debug(4,
"Determine centroid for simple features");
161 if (line_p !=
NULL) {
174 for (i = 0; i <
list.n_values; i++) {
175 if (
list.id[i] == line) {
184 list.box[found].N, 0.0);
187 if (line_c !=
NULL) {
197 ret = read_next_line_pg(Map, line_p, line_c,
TRUE);
198 if (ret != Line->
type) {
199 G_warning(
_(
"Unexpected feature type (%d) - should be (%d)"),
205 if (Map->constraint.region_flag) {
221 G_fatal_error(
_(
"GRASS is not compiled with PostgreSQL support"));
256 pg_info = &(Map->fInfo.pg);
258 G_debug(3,
"V1_read_line_pg(): offset = %lu offset_num = %lu", (
long)
offset,
276 G_debug(3,
"read (%s) feature (fid = %ld) to cache",
281 G_warning(
_(
"Feature %ld without geometry skipped"), fid);
297 G_debug(3,
"read feature part: %d -> type = %d", ipart, type);
307 G_fatal_error(
_(
"GRASS is not compiled with PostgreSQL support"));
338 pg_info = &(Map->fInfo.pg);
340 if (line < 1 || line > Map->plus.n_lines) {
341 G_warning(
_(
"Attempt to access feature with invalid id (%d)"), line);
345 Line = Map->plus.Line[line];
347 G_warning(
_(
"Attempt to access dead feature %d"), line);
351 G_debug(4,
"V2_read_line_pg() line = %d type = %d offset = %" PRId64, line,
354 if (!line_p && !line_c)
361 return get_centroid(Map, line, line_p, line_c);
372 cache_idx = line - 1;
376 "Number of features in cache: %d"),
379 G_warning(
_(
"Feature %d: unexpected type (%d) - should be %d"),
389 G_warning(
_(
"Feature %d without geometry skipped"), line);
411 if (!PQgetisnull(pg_info->
res, 0, col_idx))
413 atoi(PQgetvalue(pg_info->
res, 0, col_idx));
427 G_fatal_error(
_(
"GRASS is not compiled with PostgreSQL support"));
448 struct line_cats *line_c,
int ignore_constraints)
480 G_warning(
_(
"Feature %ld without geometry skipped"),
485 if ((
int)sf_type < 0) {
492 G_warning(
_(
"Feature without geometry. Skipped."));
508 }
while (iline ==
NULL);
510 G_debug(4,
"read next cached line %d (type = %d)",
546 if (!PQgetisnull(pg_info->
res,
549 atoi(PQgetvalue(pg_info->
res,
592 G_warning(
_(
"No geometry or topo geometry column defined"));
604 "Primary key not defined."));
608 #ifdef USE_CURSOR_RND
628 if (PQntuples(pg_info->
res) == CURSOR_PAGE &&
632 PQclear(pg_info->
res);
634 snprintf(stmt,
sizeof(stmt),
"FETCH %d in %s", CURSOR_PAGE,
637 pg_info->
res = PQexec(pg_info->
conn, stmt);
638 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
639 error_tuples(pg_info);
659 seq_type = atoi(PQgetvalue(pg_info->
res, pg_info->
next_line, 2));
670 strlen(PQgetvalue(pg_info->
res, pg_info->
next_line, 1)) !=
672 G_warning(
_(
"Inconsistency in topology: detected centroid "
673 "(should be point)"));
676 int left_face, right_face;
683 if (type ==
GV_LINE && (left_face != 0 || right_face != 0))
684 G_warning(
_(
"Inconsistency in topology: detected boundary "
685 "(should be line)"));
691 data = (
char *)PQgetvalue(pg_info->
res, pg_info->
next_line, 0);
704 if (!PQgetisnull(pg_info->
res, pg_info->
next_line, col_idx))
705 cat = atoi(PQgetvalue(pg_info->
res, pg_info->
next_line, col_idx));
734 unsigned char *hex_to_wkb(
const char *hex_data,
int *nbytes)
739 length = strlen(hex_data) / 2 + 1;
740 if (length > wkb_data_length) {
741 wkb_data_length = length;
742 wkb_data =
G_realloc(wkb_data, wkb_data_length);
745 *nbytes = length - 1;
746 for (i = 0; i < (*nbytes); i++) {
748 (
unsigned char)((hex_data[2 * i] >
'F' ? hex_data[2 * i] - 0x57
749 : hex_data[2 * i] >
'9' ? hex_data[2 * i] - 0x37
750 : hex_data[2 * i] - 0x30)
752 wkb_data[i] |= (
unsigned char)(hex_data[2 * i + 1] >
'F'
753 ? hex_data[2 * i + 1] - 0x57
754 : hex_data[2 * i + 1] >
'9'
755 ? hex_data[2 * i + 1] - 0x37
756 : hex_data[2 * i + 1] - 0x30);
759 wkb_data[(*nbytes)] = 0;
782 struct feat_parts *fparts)
784 int ret, byte_order, nbytes, is3D;
785 unsigned char *wkb_data;
786 unsigned int wkb_flags;
803 wkb_data = hex_to_wkb(data, &nbytes);
808 G_debug(3,
"Vect__cache_feature_pg(): invalid geometry");
809 G_warning(
_(
"Invalid WKB content: %d bytes"), nbytes);
813 G_debug(3,
"Vect__cache_feature_pg(): no geometry");
819 memcpy(&wkb_flags, wkb_data + 1, 4);
822 wkb_flags = SWAP32(wkb_flags);
824 if (wkb_flags & 0x40000000) {
825 G_warning(
_(
"Reading EWKB with 4-dimensional coordinates (XYZM) "
826 "is not supported"));
835 if (nbytes > 9 && ((byte_order ==
ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
837 memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
840 wkb_data[1] &= (~0x20);
842 wkb_data[4] &= (~0x20);
845 if (nbytes < 9 && nbytes != -1) {
855 is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
859 is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
861 G_debug(3,
"Vect__cache_feature_pg(): sf_type = %d", ftype);
881 ret = point_from_wkb(wkb_data, nbytes, byte_order, is3D,
883 add_fpart(fparts, ftype, 0, 1);
888 ret = linestring_from_wkb(wkb_data, nbytes, byte_order, is3D,
890 add_fpart(fparts, ftype, 0, 1);
892 else if (ftype ==
SF_POLYGON && !skip_polygon) {
896 ret = polygon_from_wkb(wkb_data, nbytes, byte_order, is3D,
cache,
898 add_fpart(fparts, ftype, 0, nrings);
902 ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order, is3D,
906 G_warning(
_(
"Unsupported feature type %d"), ftype);
933 int point_from_wkb(
const unsigned char *wkb_data,
int nbytes,
int byte_order,
938 if (nbytes < 21 && nbytes != -1)
942 memcpy(&
x, wkb_data + 5, 8);
943 memcpy(&y, wkb_data + 5 + 8, 8);
951 if (nbytes < 29 && nbytes != -1)
954 memcpy(&z, wkb_data + 5 + 16, 8);
968 return 5 + 8 * (with_z ==
WITH_Z ? 3 : 2);
985 int linestring_from_wkb(
const unsigned char *wkb_data,
int nbytes,
986 int byte_order,
int with_z,
struct line_pnts *line_p,
989 int npoints, point_size, buff_min_size,
offset;
998 if (is_ring && nbytes < 4 && nbytes != -1)
999 return error_corrupted_data(
NULL);
1002 memcpy(&npoints, wkb_data + (5 -
offset), 4);
1005 npoints = SWAP32(npoints);
1011 point_size = with_z ? 24 : 16;
1012 if (npoints < 0 || npoints > INT_MAX / point_size)
1013 return error_corrupted_data(
NULL);
1015 buff_min_size = point_size * npoints;
1017 if (nbytes != -1 && buff_min_size > nbytes - (9 -
offset))
1018 return error_corrupted_data(
_(
"Length of input WKB is too small"));
1024 for (i = 0; i < npoints; i++) {
1025 memcpy(&
x, wkb_data + (9 -
offset) + i * point_size, 8);
1026 memcpy(&y, wkb_data + (9 -
offset) + 8 + i * point_size, 8);
1028 memcpy(&z, wkb_data + (9 -
offset) + 16 + i * point_size, 8);
1061 int polygon_from_wkb(
const unsigned char *wkb_data,
int nbytes,
int byte_order,
1064 int data_offset, i, nsize, isize;
1068 if (nbytes < 9 && nbytes != -1)
1072 memcpy(nrings, wkb_data + 5, 4);
1074 *nrings = SWAP32(*nrings);
1079 num_of_rings = *nrings;
1086 if (nbytes != -1 && nbytes - 9 < num_of_rings * 4) {
1087 return error_corrupted_data(
_(
"Length of input WKB is too small"));
1092 nbytes -= data_offset;
1096 for (i = 0; i < num_of_rings; i++) {
1103 linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order, with_z,
1112 data_offset += isize;
1134 int geometry_collection_from_wkb(
const unsigned char *wkb_data,
int nbytes,
1135 int byte_order,
int with_z,
1137 struct feat_parts *fparts)
1139 int ipart, nparts, data_offset, nsize;
1140 unsigned char *wkb_subdata;
1143 if (nbytes < 9 && nbytes != -1)
1144 return error_corrupted_data(
NULL);
1147 memcpy(&nparts, wkb_data + 5, 4);
1149 nparts = SWAP32(nparts);
1151 if (nparts < 0 || nparts > INT_MAX / 9) {
1152 return error_corrupted_data(
NULL);
1154 G_debug(5,
"\t(geometry collections) parts: %d", nparts);
1157 if (nbytes != -1 && nbytes - 9 < nparts * 9) {
1158 return error_corrupted_data(
_(
"Length of input WKB is too small"));
1163 nbytes -= data_offset;
1169 for (ipart = 0; ipart < nparts; ipart++) {
1170 wkb_subdata = (
unsigned char *)wkb_data + data_offset;
1171 if (nbytes < 9 && nbytes != -1)
1172 return error_corrupted_data(
NULL);
1183 nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1186 add_fpart(fparts, ftype, cache->
lines_next, 1);
1191 nsize = linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1194 add_fpart(fparts, ftype, cache->
lines_next, 1);
1201 nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1203 add_fpart(fparts, ftype, idx, nrings);
1207 geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
1208 with_z, cache, fparts);
1211 G_warning(
_(
"Unsupported feature type %d"), ftype);
1218 data_offset += nsize;
1231 int error_corrupted_data(
const char *msg)
1254 int fetch_all,
int built_level)
1268 if (pg_info->
where) {
1276 snprintf(stmt,
sizeof(stmt),
1277 "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM "
1278 "\"%s\".\"%s\" WHERE \"%s\"=%s ORDER BY \"%s\"",
1286 snprintf(stmt,
sizeof(stmt),
1287 "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM "
1288 "\"%s\".\"%s\" ORDER BY \"%s\"",
1299 "DECLARE %s CURSOR FOR "
1300 "SELECT geom,id,type,fid FROM ("
1301 "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM "
1302 "\"%s\".node AS tt "
1303 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 1 AND (%s).id = "
1305 "WHERE containing_face IS NULL AND node_id NOT IN "
1306 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1307 "GROUP BY start_node UNION ALL "
1308 "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS "
1310 "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM "
1311 "\"%s\".node AS tt "
1312 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 3 AND (%s).id = %s "
1313 "WHERE containing_face IS NOT NULL AND node_id NOT IN "
1314 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1315 "GROUP BY start_node UNION ALL "
1316 "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS "
1318 "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM "
1319 "\"%s\".edge AS tt "
1320 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = "
1322 "WHERE left_face = 0 AND right_face = 0 UNION ALL "
1323 "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM "
1324 "\"%s\".edge AS tt "
1325 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = "
1327 "WHERE left_face != 0 OR right_face != 0 ) AS foo ORDER BY type,id",
1349 snprintf(stmt,
sizeof(stmt),
"FETCH ALL in %s", pg_info->
cursor_name);
1351 snprintf(stmt,
sizeof(stmt),
"FETCH %d in %s", CURSOR_PAGE,
1355 PQexec(pg_info->
conn, stmt);
1356 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
1357 error_tuples(pg_info);
1384 G_debug(3,
"Vect__open_cursor_line_pg(): fid range = %d-%d, type = %d", fid,
1385 fid + CURSOR_PAGE, type);
1396 snprintf(stmt,
sizeof(stmt),
1397 "DECLARE %s CURSOR FOR SELECT %s FROM \"%s\".\"%s\" "
1398 "WHERE %s BETWEEN %d AND %d ORDER BY %s",
1401 fid, fid + CURSOR_PAGE, pg_info->
fid_column);
1406 G_warning(
_(
"Unsupported feature type %d"), type);
1413 snprintf(stmt,
sizeof(stmt),
1414 "DECLARE %s CURSOR FOR SELECT geom,containing_face "
1415 " FROM \"%s\".node WHERE node_id BETWEEN %d AND %d ORDER "
1422 snprintf(stmt,
sizeof(stmt),
1423 "DECLARE %s CURSOR FOR SELECT geom,left_face,right_face "
1424 " FROM \"%s\".edge WHERE edge_id BETWEEN %d AND %d ORDER "
1436 snprintf(stmt,
sizeof(stmt),
"FETCH ALL in %s", pg_info->
cursor_name);
1437 pg_info->
res = PQexec(pg_info->
conn, stmt);
1438 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
1439 error_tuples(pg_info);
1457 PQclear(pg_info->
res);
1464 snprintf(stmt,
sizeof(stmt),
"CLOSE %s", pg_info->
cursor_name);
1493 snprintf(stmt,
sizeof(stmt),
1494 "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
1501 G_warning(
_(
"Unsupported feature type %d"), type);
1515 nodeid =
"containing_face";
1518 snprintf(stmt,
sizeof(stmt),
1519 "SELECT tt.geom,tt.containing_face,ft.%s FROM \"%s\".node "
1521 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = %d and "
1523 "WHERE node_id = %d",
1530 snprintf(stmt,
sizeof(stmt),
1531 "SELECT tt.geom,tt.left_face,tt.right_face,ft.%s FROM "
1532 "\"%s\".edge AS tt "
1533 "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 and "
1534 "(%s).id = edge_id "
1535 "WHERE edge_id = %d",
1545 pg_info->
res = PQexec(pg_info->
conn, stmt);
1546 if (!pg_info->
res || PQresultStatus(pg_info->
res) != PGRES_TUPLES_OK) {
1547 error_tuples(pg_info);
1571 G_debug(3,
"Vect__execute_pg(): %s", stmt);
1572 result = PQexec(conn, stmt);
1573 if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
1578 stmt_len = strlen(stmt);
1579 strncpy(stmt_prt, stmt, stmt_len > 511 ? 511 : stmt_len);
1580 stmt_prt[stmt_len > 511 ? 511 : stmt_len] =
'\0';
1581 G_warning(
_(
"Execution failed: %s (...)\nReason: %s"), stmt_prt,
1582 PQerrorMessage(conn));
1606 G_debug(3,
"Vect__execute_get_value_pg(): %s", stmt);
1607 result = PQexec(conn, stmt);
1608 if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
1609 PQntuples(result) != 1) {
1612 G_warning(
_(
"Execution failed: %s\nReason: %s"), stmt,
1613 PQerrorMessage(conn));
1617 ret = atoi(PQgetvalue(result, 0, 0));
1633 if (!incr && !cache->
lines) {
1649 for (i = cache->
lines_alloc - num; i < cache->lines_alloc; i++) {
1662 void add_fpart(
struct feat_parts *fparts,
SF_FeatureType ftype,
int idx,
1668 if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
1669 if (fparts->a_parts == 0)
1670 fparts->a_parts = 1;
1672 fparts->a_parts += fparts->n_parts;
1677 (
int *)
G_realloc(fparts->nlines, fparts->a_parts *
sizeof(
int));
1679 (
int *)
G_realloc(fparts->idx, fparts->a_parts *
sizeof(
int));
1682 fparts->ftype[fparts->n_parts] = ftype;
1683 fparts->idx[fparts->n_parts] = idx;
1684 fparts->nlines[fparts->n_parts] = nlines;
1718 for (i = 0; i <
list.n_values; i++) {
1719 if (
list.id[i] == centroid) {
1742 G_warning(
_(
"Unable to read features. Reason:\n%s"),
1743 PQresultErrorMessage(pg_info->
res));
1746 PQclear(pg_info->
res);
void G_free(void *)
Free allocated memory.
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
void G_free_tokens(char **)
Free memory allocated to tokens.
int G_asprintf(char **, const char *,...) __attribute__((format(printf
int G_number_of_tokens(char **)
Return number of tokens.
int G_debug(int, const char *,...) __attribute__((format(printf
char ** G_tokenize(const char *, const char *)
Tokenize string.
int Vect_reset_cats(struct line_cats *)
Reset category structure to make sure cats structure is clean to be re-used.
void Vect_line_box(const struct line_pnts *, struct bound_box *)
Get bounding box of line.
int Vect_cat_set(struct line_cats *, int, int)
Add new field/cat to category structure if doesn't exist yet.
int Vect_get_constraint_box(struct Map_info *, struct bound_box *)
Get constraint box.
int Vect_box_overlap(const struct bound_box *, const struct bound_box *)
Tests for overlap of two boxes.
int Vect_get_area_box(struct Map_info *, int, struct bound_box *)
Get bounding box of area.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
int Vect_select_lines_by_box(struct Map_info *, const struct bound_box *, int, struct boxlist *)
Select lines with bounding boxes by box.
void Vect_reset_line(struct line_pnts *)
Reset line.
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
int Vect_append_points(struct line_pnts *, const struct line_pnts *, int)
Appends points to the end of a line.
SF_FeatureType
Simple feature types.
#define GV_POINT
Feature types used in memory on run time (may change)
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
#define GV_FORWARD
Line direction indicator forward/backward.
int dig_init_boxlist(struct boxlist *, int)
#define ENDIAN_LITTLE
Endian check.
int V2_read_line_pg(struct Map_info *Map NOPG_UNUSED, struct line_pnts *line_p NOPG_UNUSED, struct line_cats *line_c NOPG_UNUSED, int line NOPG_UNUSED)
Read feature from PostGIS layer on topological level.
int Vect__close_cursor_pg(struct Format_info_pg *pg_info)
Close select cursor.
int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
Execute SQL statement and get value.
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.
SF_FeatureType get_feature(struct Map_info *, int, int)
Read feature geometry.
int V1_read_line_pg(struct Map_info *Map NOPG_UNUSED, struct line_pnts *line_p NOPG_UNUSED, struct line_cats *line_c NOPG_UNUSED, off_t offset NOPG_UNUSED)
Read feature from PostGIS layer at given offset (level 1 without topology)
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)
int V1_read_next_line_pg(struct Map_info *Map NOPG_UNUSED, struct line_pnts *line_p NOPG_UNUSED, struct line_cats *line_c NOPG_UNUSED)
Read next feature from PostGIS layer. Skip empty features (level 1 without topology)....
int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
Select feature (internal use only)
void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
Reallocate lines cache.
int V2_read_next_line_pg(struct Map_info *Map NOPG_UNUSED, struct line_pnts *line_p NOPG_UNUSED, struct line_cats *line_c NOPG_UNUSED)
Read next feature from PostGIS layer on topological level (simple feature access).
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)
struct dig_head head
Header info.
plus_t next_line
Feature id for sequential access.
int type
Feature type constraint.
int type_flag
Non-zero value to enable feature type constraint.
struct Map_info::@11 constraint
Constraints for sequential feature access.
int region_flag
Non-zero value to enable region constraint.
struct Format_info fInfo
Format info for non-native formats.
struct Plus_head plus
Plus info (topology, version, ...)
off_t offset
Offset in coor file for line.
void * topo
Topology info.
plus_t area
Area number, negative for duplicate centroid.
struct P_line ** Line
Array of vector geometries.
int built
Highest level of topology currently available.
List of bounding boxes with id.
off_t last_offset
Offset of last read line.
Feature geometry info - coordinates.
int n_points
Number of points.