[GRASS-SVN] r51975 - in grass/trunk: include/defs lib/vector/Vlib lib/vector/diglib
svn_grass at osgeo.org
svn_grass at osgeo.org
Tue Jun 5 05:44:18 PDT 2012
Author: martinl
Date: 2012-06-05 05:44:17 -0700 (Tue, 05 Jun 2012)
New Revision: 51975
Modified:
grass/trunk/include/defs/vector.h
grass/trunk/lib/vector/Vlib/box.c
grass/trunk/lib/vector/Vlib/build.c
grass/trunk/lib/vector/Vlib/build_nat.c
grass/trunk/lib/vector/Vlib/build_pg.c
grass/trunk/lib/vector/Vlib/build_sfa.c
grass/trunk/lib/vector/Vlib/close.c
grass/trunk/lib/vector/Vlib/open.c
grass/trunk/lib/vector/Vlib/open_pg.c
grass/trunk/lib/vector/Vlib/pg_local_proto.h
grass/trunk/lib/vector/Vlib/read.c
grass/trunk/lib/vector/Vlib/read_pg.c
grass/trunk/lib/vector/diglib/spindex.c
grass/trunk/lib/vector/diglib/spindex_rw.c
Log:
vlib: improve PostGIS topology support
read topology tables instead of feature table
introduce level 3 (PostGIS topology access)
Modified: grass/trunk/include/defs/vector.h
===================================================================
--- grass/trunk/include/defs/vector.h 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/include/defs/vector.h 2012-06-05 12:44:17 UTC (rev 51975)
@@ -517,6 +517,8 @@
struct line_cats *);
int V2_read_next_line_pg(struct Map_info *, struct line_pnts *,
struct line_cats *);
+int V3_read_next_line_pg(struct Map_info *, struct line_pnts *,
+ struct line_cats *);
int V1_delete_line_nat(struct Map_info *, off_t);
int V1_delete_line_ogr(struct Map_info *, off_t);
int V1_delete_line_pg(struct Map_info *, off_t);
@@ -547,6 +549,7 @@
/* Build topology */
int Vect_build_nat(struct Map_info *, int);
+void Vect__build_downgrade(struct Map_info *, int);
int Vect__build_sfa(struct Map_info *, int);
int Vect_build_ogr(struct Map_info *, int);
int Vect_build_pg(struct Map_info *, int);
Modified: grass/trunk/lib/vector/Vlib/box.c
===================================================================
--- grass/trunk/lib/vector/Vlib/box.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/box.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -302,23 +302,16 @@
Area = Plus->Area[area];
if (Area == NULL) { /* dead */
- Box->N = 0;
- Box->S = 0;
- Box->E = 0;
- Box->W = 0;
- Box->T = 0;
- Box->B = 0;
-
+ G_zero(Box, sizeof(struct bound_box));
return 0;
}
else {
-
Line = Plus->Line[abs(Area->lines[0])];
topo = (struct P_topo_b *)Line->topo;
Node = Plus->Node[topo->N1];
if (list == NULL) {
- list = Vect_new_boxlist(1);
+ list = Vect_new_boxlist(TRUE);
}
Vect_reset_boxlist(list);
@@ -332,7 +325,7 @@
dig_boxlist_add(list, area, bbox);
if (dig_find_area_box(Plus, list) == 0)
- G_fatal_error(_("Could not find area box"));
+ G_fatal_error(_("Unable to get bounding box for area %d"), area);
Box->N = list->box[0].N;
Box->S = list->box[0].S;
Modified: grass/trunk/lib/vector/Vlib/build.c
===================================================================
--- grass/trunk/lib/vector/Vlib/build.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/build.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -655,10 +655,69 @@
*/
int Vect_get_built(const struct Map_info *Map)
{
- return (Map->plus.built);
+ return Map->plus.built;
}
/*!
+ \brief Downgrade build level (for internal use only)
+
+ See Vect_build_nat(), Vect__build_sfa(), and Vect_build_pg() for
+ implementation issues.
+
+ \param Map pointer to Map_info
+*/
+void Vect__build_downgrade(struct Map_info *Map, int build)
+{
+ int line;
+ struct Plus_head *plus;
+ struct P_line *Line;
+
+ plus = &(Map->plus);
+
+ /* lower level request - release old sources (this also
+ initializes structures and numbers of elements) */
+ if (plus->built >= GV_BUILD_CENTROIDS && build < GV_BUILD_CENTROIDS) {
+ /* reset info about areas stored for centroids */
+ for (line = 1; line <= plus->n_lines; line++) {
+ Line = plus->Line[line];
+ if (Line && Line->type == GV_CENTROID) {
+ struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
+ topo->area = 0;
+ }
+ }
+ dig_free_plus_areas(plus);
+ dig_spidx_free_areas(plus);
+ dig_free_plus_isles(plus);
+ dig_spidx_free_isles(plus);
+ }
+
+ if (plus->built >= GV_BUILD_AREAS && build < GV_BUILD_AREAS) {
+ /* reset info about areas stored for lines */
+ for (line = 1; line <= plus->n_lines; line++) {
+ Line = plus->Line[line];
+ if (Line && Line->type == GV_BOUNDARY) {
+ struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
+ topo->left = 0;
+ topo->right = 0;
+ }
+ }
+ dig_free_plus_areas(plus);
+ dig_spidx_free_areas(plus);
+ dig_free_plus_isles(plus);
+ dig_spidx_free_isles(plus);
+ }
+
+ if (plus->built >= GV_BUILD_BASE && build < GV_BUILD_BASE) {
+ dig_free_plus_nodes(plus);
+ dig_spidx_free_nodes(plus);
+ dig_free_plus_lines(plus);
+ dig_spidx_free_lines(plus);
+ }
+
+ plus->built = build;
+}
+
+/*!
\brief Build partial topology for vector map.
Should only be used in special cases of vector processing.
@@ -701,14 +760,14 @@
G_debug(3, "Vect_build(): build = %d", build);
- /* If topology is already build (map on level2), set level to 1 so
- * that lines will be read by V1_read_ (all lines) */
+ /* If topology is already build (map on > level 2), set level to 1
+ * so that lines will be read by V1_read_ (all lines) */
Map->level = 1; /* may be not needed, because V1_read is used
directly by Vect_build_ */
if (Map->format != GV_FORMAT_OGR_DIRECT)
Map->support_updated = TRUE;
- if (Map->plus.Spidx_built == FALSE)
+ if (!Map->plus.Spidx_built)
Vect_open_sidx(Map, 2);
plus = &(Map->plus);
@@ -734,11 +793,12 @@
G_verbose_message(_("Topology was built"));
}
- Map->level = LEVEL_2;
+ if (Map->level == 1)
+ Map->level = LEVEL_2;
plus->mode = GV_MODE_WRITE;
if (build == GV_BUILD_ALL) {
- plus->cidx_up_to_date = 1; /* category index was build */
+ plus->cidx_up_to_date = TRUE; /* category index was build */
dig_cidx_sort(plus);
}
@@ -1040,8 +1100,8 @@
G_fatal_error(_("Unable to build spatial index from topology, "
"vector map is not opened at topology level 2"));
}
- if (!(Map->plus.Spidx_built)) {
- return (Vect_build_sidx_from_topo(Map));
+ if (!Map->plus.Spidx_built) {
+ return Vect_build_sidx_from_topo(Map);
}
return 0;
}
@@ -1082,8 +1142,8 @@
plus = &(Map->plus);
- if (plus->Spidx_built == FALSE) {
- G_warning("Spatial index not available, can not be saved");
+ if (!plus->Spidx_built) {
+ G_warning(_("Spatial index not available, can not be saved"));
return 0;
}
Modified: grass/trunk/lib/vector/Vlib/build_nat.c
===================================================================
--- grass/trunk/lib/vector/Vlib/build_nat.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/build_nat.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -50,55 +50,13 @@
return 1; /* Do nothing */
/* Check if upgrade or downgrade */
- if (build < plus->built) { /* lower level request */
-
- /* release old sources (this also initializes structures and numbers of elements) */
- if (plus->built >= GV_BUILD_CENTROIDS && build < GV_BUILD_CENTROIDS) {
- /* reset info about areas stored for centroids */
- int nlines = Vect_get_num_lines(Map);
-
- for (line = 1; line <= nlines; line++) {
- Line = plus->Line[line];
- if (Line && Line->type == GV_CENTROID) {
- struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
- topo->area = 0;
- }
- }
- dig_free_plus_areas(plus);
- dig_spidx_free_areas(plus);
- dig_free_plus_isles(plus);
- dig_spidx_free_isles(plus);
- }
-
-
- if (plus->built >= GV_BUILD_AREAS && build < GV_BUILD_AREAS) {
- /* reset info about areas stored for lines */
- int nlines = Vect_get_num_lines(Map);
-
- for (line = 1; line <= nlines; line++) {
- Line = plus->Line[line];
- if (Line && Line->type == GV_BOUNDARY) {
- struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
- topo->left = 0;
- topo->right = 0;
- }
- }
- dig_free_plus_areas(plus);
- dig_spidx_free_areas(plus);
- dig_free_plus_isles(plus);
- dig_spidx_free_isles(plus);
- }
- if (plus->built >= GV_BUILD_BASE && build < GV_BUILD_BASE) {
- dig_free_plus_nodes(plus);
- dig_spidx_free_nodes(plus);
- dig_free_plus_lines(plus);
- dig_spidx_free_lines(plus);
- }
-
- plus->built = build;
- return 1;
+ if (build < plus->built) {
+ /* -> downgrade */
+ Vect__build_downgrade(Map, build);
+ return 1;
}
+ /* -> upgrade */
Points = Vect_new_line_struct();
Cats = Vect_new_cats_struct();
@@ -106,9 +64,9 @@
register int npoints, c;
/*
- * We shall go through all primitives in coor file and
- * add new node for each end point to nodes structure
- * if the node with the same coordinates doesn't exist yet.
+ * We shall go through all primitives in coor file and add
+ * new node for each end point to nodes structure if the node
+ * with the same coordinates doesn't exist yet.
*/
/* register lines, create nodes */
Modified: grass/trunk/lib/vector/Vlib/build_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/build_pg.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/build_pg.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -85,12 +85,21 @@
pg_info->inTransaction = FALSE;
- if (build > GV_BUILD_NONE)
+ if (build > GV_BUILD_NONE) {
G_message(_("Using external data format '%s' (feature type '%s')"),
Vect_get_finfo_format_info(Map),
- Vect_get_finfo_geometry_type(Map));
+ Vect_get_finfo_geometry_type(Map));
+ if (!pg_info->toposchema_name)
+ G_message(_("Building pseudo-topology over simple features..."));
+ else
+ G_message(_("Building topology from PostGIS topology schema <%s>..."),
+ pg_info->toposchema_name);
+ }
- return Vect__build_sfa(Map, build);
+ if (!pg_info->toposchema_name)
+ return Vect__build_sfa(Map, build);
+
+ return build_topo(Map, build);
#else
G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
return 0;
@@ -98,9 +107,46 @@
}
#ifdef HAVE_POSTGRES
+/*!
+ \brief Build based on PostGIS topology
+
+ Currently only GV_BUILD_ALL is supported
+
+ \param Map pointer to Map_info struct
+ \param build build level
+
+ \return 1 on success
+ \return 0 on error
+*/
int build_topo(struct Map_info *Map, int build)
{
- G_fatal_error(_("Not implemented"));
+ struct Format_info_pg *pg_info;
+ struct Plus_head *plus;
+
+ pg_info = &(Map->fInfo.pg);
+ plus = &(Map->plus);
+
+ /* check if upgrade or downgrade */
+ if (build < plus->built) {
+ /* -> downgrade */
+ Vect__build_downgrade(Map, build);
+ return 1;
+ }
+
+ if (build != GV_BUILD_ALL) {
+ G_warning(_("Only %s is supported for PostGIS topology"),
+ "GV_BUILD_ALL");
+ return 0;
+ }
+
+ if (plus->built < GV_BUILD_BASE) {
+ if (load_plus(pg_info, plus, FALSE) != 0)
+ return 0;
+ }
+
+ plus->built = build;
+ Map->level = 3;
+
return 1;
}
#endif
Modified: grass/trunk/lib/vector/Vlib/build_sfa.c
===================================================================
--- grass/trunk/lib/vector/Vlib/build_sfa.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/build_sfa.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -315,7 +315,6 @@
void build_pg(struct Map_info *Map, int build)
{
int iFeature, ipart, fid, nrecords, npoints;
- char stmt[DB_SQL_MAX];
char *wkb_data;
struct Format_info_pg *pg_info;
@@ -329,20 +328,10 @@
init_parts(&parts);
G_zero(&fparts, sizeof(struct feat_parts));
- /* get records */
- sprintf(stmt, "SELECT %s,%s FROM %s.%s",
- pg_info->fid_column,
- pg_info->geom_column, pg_info->schema_name, pg_info->table_name);
- G_debug(2, "SQL: %s", stmt);
- pg_info->res = PQexec(pg_info->conn, stmt);
- if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
- PQclear(pg_info->res);
- pg_info->res = NULL;
- G_warning(_("Unable to get features:\n%s"),
- PQerrorMessage(pg_info->conn));
- return; /* reading failed */
- }
-
+ /* get all features */
+ if (set_initial_query(pg_info, TRUE) != 0)
+ return;
+
/* scan records */
npoints = 0;
nrecords = PQntuples(pg_info->res);
@@ -350,8 +339,8 @@
G_message(_("Registering primitives..."));
for (iFeature = 0; iFeature < nrecords; iFeature++) {
/* get feature id */
- fid = atoi(PQgetvalue(pg_info->res, iFeature, 0));
- wkb_data = PQgetvalue(pg_info->res, iFeature, 1);
+ fid = atoi(PQgetvalue(pg_info->res, iFeature, 1));
+ wkb_data = PQgetvalue(pg_info->res, iFeature, 0);
G_progress(iFeature + 1, 1e4);
@@ -699,70 +688,35 @@
plus = &(Map->plus);
/* check if upgrade or downgrade */
- if (build < plus->built) {
- /* lower level request, currently only GV_BUILD_NONE */
- if (plus->built >= GV_BUILD_CENTROIDS && build < GV_BUILD_CENTROIDS) {
- /* reset info about areas stored for centroids */
- for (line = 1; line <= plus->n_lines; line++) {
- Line = plus->Line[line];
- if (Line && Line->type == GV_CENTROID) {
- struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
- topo->area = 0;
- }
- }
- dig_free_plus_areas(plus);
- dig_spidx_free_areas(plus);
- dig_free_plus_isles(plus);
- dig_spidx_free_isles(plus);
- }
-
- if (plus->built >= GV_BUILD_AREAS && build < GV_BUILD_AREAS) {
- /* reset info about areas stored for lines */
- for (line = 1; line <= plus->n_lines; line++) {
- Line = plus->Line[line];
- if (Line && Line->type == GV_BOUNDARY) {
- struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
- topo->left = 0;
- topo->right = 0;
- }
- }
- dig_free_plus_areas(plus);
- dig_spidx_free_areas(plus);
- dig_free_plus_isles(plus);
- dig_spidx_free_isles(plus);
- }
-
- if (plus->built >= GV_BUILD_BASE && build < GV_BUILD_BASE) {
- dig_free_plus_nodes(plus);
- dig_spidx_free_nodes(plus);
- dig_free_plus_lines(plus);
- dig_spidx_free_lines(plus);
- }
+ if (build < plus->built) {
+ /* -> downgrade */
+ Vect__build_downgrade(Map, build);
+ return 1;
}
- else {
- if (plus->built < GV_BUILD_BASE) {
- if (Map->format == GV_FORMAT_OGR ||
- Map->format == GV_FORMAT_OGR_DIRECT) {
+
+ /* -> upgrade */
+ if (plus->built < GV_BUILD_BASE) {
+ if (Map->format == GV_FORMAT_OGR ||
+ Map->format == GV_FORMAT_OGR_DIRECT) {
#ifdef HAVE_OGR
- build_ogr(Map, build);
+ build_ogr(Map, build);
#else
- G_fatal_error(_("GRASS is not compiled with OGR support"));
+ G_fatal_error(_("GRASS is not compiled with OGR support"));
#endif
- }
- else if (Map->format == GV_FORMAT_POSTGIS) {
+ }
+ else if (Map->format == GV_FORMAT_POSTGIS) {
#ifdef HAVE_POSTGRES
- build_pg(Map, build);
+ build_pg(Map, build);
#else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
+ G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
#endif
- }
- else {
- G_fatal_error(_("%s: Native format unsupported"),
- "Vect__build_sfa()");
- }
- }
+ }
+ else {
+ G_fatal_error(_("%s: Native format unsupported"),
+ "Vect__build_sfa()");
+ }
}
-
+
plus->built = build;
return 1;
Modified: grass/trunk/lib/vector/Vlib/close.c
===================================================================
--- grass/trunk/lib/vector/Vlib/close.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/close.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -75,15 +75,15 @@
G_debug(1, "Vect_close(): name = %s, mapset = %s, format = %d, level = %d",
Map->name, Map->mapset, Map->format, Map->level);
- /* Store support files for vector maps in the current mapsset if
- in write mode on level 2 */
+ /* store support files for vector maps in the current mapset if in
+ write mode on level 2 */
if (strcmp(Map->mapset, G_mapset()) == 0 &&
Map->support_updated &&
Map->plus.built == GV_BUILD_ALL) {
char buf[GPATH_MAX];
char file_path[GPATH_MAX];
- /* Delete old support files if available */
+ /* delete old support files if available */
sprintf(buf, "%s/%s", GV_DIRECTORY, Map->name);
G_file_name(file_path, buf, GV_TOPO_ELEMENT, G_mapset());
@@ -110,8 +110,8 @@
Map->plus.coor_mtime = CInfo.mtime;
/* write out topo file */
- Vect_save_topo(Map);
-
+ Vect_save_topo(Map);
+
/* write out sidx file */
Map->plus.Spidx_new = TRUE;
Vect_save_sidx(Map);
@@ -122,7 +122,7 @@
/* write out fidx file */
if (Map->format == GV_FORMAT_OGR)
V2_close_ogr(Map);
- else if (Map->format == GV_FORMAT_POSTGIS)
+ else if (Map->format == GV_FORMAT_POSTGIS && Map->level == 2)
V2_close_pg(Map);
}
else {
@@ -135,7 +135,7 @@
fclose(Map->plus.spidx_fp.file);
}
- if (Map->level == 2 && Map->plus.release_support) {
+ if (Map->level > 1 && Map->plus.release_support) {
G_debug(1, "free topology");
dig_free_plus(&(Map->plus));
@@ -153,7 +153,7 @@
fclose(Map->hist_fp);
}
- /* Close level 1 files / data sources if not head_only */
+ /* close level 1 files / data sources if not head_only */
if (!Map->head_only) {
if (((*Close_array[Map->format][1]) (Map)) != 0) {
G_warning(_("Unable to close vector <%s>"),
Modified: grass/trunk/lib/vector/Vlib/open.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/open.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -26,9 +26,20 @@
#include <grass/glocale.h>
/*
- \brief Number of levels - without and with topology
+ \brief Number of levels
+
+ Native format:
+ - 1 without topology
+ - 2 with topology
+ OGR-links:
+ - 1 without topology
+ - 2 with pseudo-topology (simple features access)
+ PG-links:
+ - 1 without topology
+ - 2 with pseudo-topology (simple features access)
+ - 3 with topology (PostGIS Topology access)
*/
-#define MAX_OPEN_LEVEL 2
+#define MAX_OPEN_LEVEL 3
static int open_old_dummy()
{
@@ -231,7 +242,7 @@
G_debug(1, "Map name: %s", Map->name);
G_debug(1, "Map mapset: %s", Map->mapset);
- /* Read vector format information */
+ /* read vector format information */
if (ogr_mapset) {
format = GV_FORMAT_OGR_DIRECT;
}
@@ -273,19 +284,19 @@
G_debug(1, "Level request = %d", level_request);
/* There are only 2 possible open levels, 1 and 2. Try first to
- * open 'support' files (topo,sidx,cidx), these files are the same
- * for all formats. If it is not possible and requested level is
- * 2, return error, otherwise call Open_old_array[format][1], to
- * open remaining files/sources (level 1)
- */
+ open 'support' files (topo, sidx, cidx), these files are the same
+ for all formats. If it is not possible and requested level is
+ 2, return error, otherwise call Open_old_array[format][1], to
+ open remaining files/sources (level 1)
+ */
- /* Try to open support files if level was not requested or
+ /* try to open support files if level was not requested or
* requested level is 2 (format independent) */
- if (level_request == 0 || level_request == 2) {
+ if (level_request == 0 || level_request > 1) {
level = 2; /* we expect success */
+
/* open topo */
ret = -1;
-
if (Map->format == GV_FORMAT_POSTGIS)
/* try to read full-topology for PostGIS links */
ret = Vect_open_topo_pg(Map, head_only);
@@ -513,7 +524,8 @@
if (access(file_path, F_OK) == 0) /* cidx file exists? */
unlink(file_path);
- if (format == GV_FORMAT_OGR || format == GV_FORMAT_POSTGIS) {
+ if (format == GV_FORMAT_OGR ||
+ (format == GV_FORMAT_POSTGIS && level == 2)) {
G_file_name(file_path, buf, GV_FIDX_ELEMENT, G_mapset());
if (access(file_path, F_OK) == 0) /* fidx file exists? */
unlink(file_path);
@@ -1063,12 +1075,9 @@
ret = dig_load_plus(Plus, &fp, head_only);
fclose(fp.file);
- /* dig_file_free ( &fp); */
-
- if (ret == 0)
- return -1;
-
- return 0;
+ /* dig_file_free(&fp); */
+
+ return ret == 0 ? -1 : 0;
}
/*!
@@ -1082,7 +1091,7 @@
*/
int Vect_open_sidx(struct Map_info *Map, int mode)
{
- char buf[500], file_path[2000];
+ char buf[GPATH_MAX], file_path[GPATH_MAX];
int err;
struct Coor_info CInfo;
struct Plus_head *Plus;
@@ -1090,15 +1099,15 @@
G_debug(1, "Vect_open_sidx(): name = %s mapset= %s mode = %s", Map->name,
Map->mapset, mode == 0 ? "old" : (mode == 1 ? "update" : "new"));
- if (Map->plus.Spidx_built == TRUE) {
- G_warning("Spatial index already opened");
+ Plus = &(Map->plus);
+
+ if (Plus->Spidx_built) {
+ G_debug(1, "Spatial index already opened");
return 0;
}
- Plus = &(Map->plus);
+ dig_file_init(&(Plus->spidx_fp));
- dig_file_init(&(Map->plus.spidx_fp));
-
if (mode < 2) {
sprintf(buf, "%s/%s", GV_DIRECTORY, Map->name);
G_file_name(file_path, buf, GV_SIDX_ELEMENT, Map->mapset);
@@ -1106,10 +1115,10 @@
if (access(file_path, F_OK) != 0) /* does not exist */
return 1;
- Map->plus.spidx_fp.file =
+ Plus->spidx_fp.file =
G_fopen_old(buf, GV_SIDX_ELEMENT, Map->mapset);
- if (Map->plus.spidx_fp.file == NULL) { /* sidx file is not available */
+ if (Plus->spidx_fp.file == NULL) { /* sidx file is not available */
G_debug(1, "Cannot open spatial index file for vector '%s@%s'.",
Map->name, Map->mapset);
return -1;
@@ -1120,11 +1129,11 @@
Vect_coor_info(Map, &CInfo);
/* initialize spatial index */
- Map->plus.Spidx_new = FALSE;
+ Plus->Spidx_new = FALSE;
/* load head */
- if (dig_Rd_spidx_head(&(Map->plus.spidx_fp), Plus) == -1) {
- fclose(Map->plus.spidx_fp.file);
+ if (dig_Rd_spidx_head(&(Plus->spidx_fp), Plus) == -1) {
+ fclose(Plus->spidx_fp.file);
return -1;
}
@@ -1147,36 +1156,36 @@
if (err) {
G_warning(_("Please rebuild topology for vector map <%s@%s>"),
Map->name, Map->mapset);
- fclose(Map->plus.spidx_fp.file);
+ fclose(Plus->spidx_fp.file);
return -1;
}
}
if (mode) {
/* open new spatial index */
- Map->plus.Spidx_new = TRUE;
+ Plus->Spidx_new = TRUE;
/* file based or memory based */
if (getenv("GRASS_VECTOR_LOWMEM")) {
/* free old indices */
dig_spidx_free(Plus);
/* initialize file based indices */
- Map->plus.Spidx_file = 1;
+ Plus->Spidx_file = 1;
dig_spidx_init(Plus);
}
G_debug(1, "%s based spatial index",
- Map->plus.Spidx_file == 0 ? "Memory" : "File");
+ Plus->Spidx_file == 0 ? "Memory" : "File");
if (mode == 1) {
/* load spatial index for update */
- if (dig_Rd_spidx(&(Map->plus.spidx_fp), Plus) == -1) {
- fclose(Map->plus.spidx_fp.file);
+ if (dig_Rd_spidx(&(Plus->spidx_fp), Plus) == -1) {
+ fclose(Plus->spidx_fp.file);
return -1;
}
}
}
- Map->plus.Spidx_built = TRUE;
+ Plus->Spidx_built = TRUE;
return 0;
}
Modified: grass/trunk/lib/vector/Vlib/open_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open_pg.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/open_pg.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -23,7 +23,7 @@
#ifdef HAVE_POSTGRES
#include "pg_local_proto.h"
-static struct edge_data {
+struct edge_data {
int id;
int start_node;
int end_node;
@@ -37,17 +37,17 @@
static int check_schema(const struct Format_info_pg *);
static int create_table(struct Format_info_pg *, const struct field_info *);
static void connect_db(struct Format_info_pg *);
+static int check_topo(struct Format_info_pg *, struct Plus_head *);
static int parse_bbox(const char *, struct bound_box *, int);
static int num_of_records(const struct Format_info_pg *, const char *);
static int read_p_node(struct Plus_head *, int, int, struct Format_info_pg *);
-static int read_p_line(struct Plus_head *, int, const struct edge_data *, struct Format_info_pg *);
+static int read_p_line(struct Plus_head *, int, const struct edge_data *);
static int read_p_area(struct Plus_head *, int, int, struct Format_info_pg *);
-static int load_plus_head(struct Format_info_pg *, PGresult *, struct Plus_head *);
-static int load_plus(struct Format_info_pg *, PGresult *, struct Plus_head *);
+static int load_plus_head(struct Format_info_pg *, struct Plus_head *);
#endif
/*!
- \brief Open existing PostGIS feature table (level 1 - without topology)
+ \brief Open vector map - PostGIS feature table (level 1 - without topology)
\todo Check database instead of geometry_columns
@@ -127,8 +127,12 @@
if (!found) {
G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
pg_info->table_name);
+ return -1;
}
+ /* check for topo schema */
+ check_topo(pg_info, &(Map->plus));
+
return 0;
#else
G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
@@ -137,7 +141,7 @@
}
/*!
- \brief Open existing PostGIS layer (level 2 - feature index)
+ \brief Open vector map - PostGIS feature table (level 2 - feature index)
\param[in,out] Map pointer to Map_info structure
@@ -387,51 +391,20 @@
int Vect_open_topo_pg(struct Map_info *Map, int head_only)
{
#ifdef HAVE_POSTGRES
- char stmt[DB_SQL_MAX];
-
struct Plus_head *plus;
struct Format_info_pg *pg_info;
- PGresult *res;
-
plus = &(Map->plus);
pg_info = &(Map->fInfo.pg);
- /* connect database */
- if (!pg_info->conn)
- connect_db(pg_info);
+ /* check for topo schema */
+ if (check_topo(pg_info, plus) != 0)
+ return 1;
- /* check if topology layer/schema exists */
- sprintf(stmt,
- "SELECT t.name,t.hasz,l.feature_column FROM topology.layer "
- "AS l JOIN topology.topology AS t ON l.topology_id = t.id "
- "WHERE schema_name = '%s' AND table_name = '%s'",
- pg_info->schema_name, pg_info->table_name);
- G_debug(2, "SQL: %s", stmt);
-
- res = PQexec(pg_info->conn, stmt);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
- PQntuples(res) != 1) {
- G_debug(1, "Topology layers for '%s.%s' not found (%s)",
- pg_info->schema_name, pg_info->table_name,
- PQerrorMessage(pg_info->conn));
- if (res)
- PQclear(res);
- return 1;
- }
-
- pg_info->toposchema_name = G_store(PQgetvalue(res, 0, 0));
- pg_info->topogeom_column = G_store(PQgetvalue(res, 0, 2));
-
/* free and init plus structure */
dig_init_plus(plus);
- if (load_plus_head(pg_info, res, plus) != 0)
- return -1;
- if (head_only)
- return 0;
-
- return load_plus(pg_info, res, plus);
+ return load_plus(pg_info, plus, head_only);
#else
G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
return -1;
@@ -439,6 +412,14 @@
}
#ifdef HAVE_POSTGRES
+/*!
+ \brief Get key column for feature table
+
+ \param pg_info pointer to Format_info_pg
+
+ \return string buffer with key column name
+ \return NULL on missing key column
+*/
char *get_key_column(struct Format_info_pg *pg_info)
{
char *key_column;
@@ -478,10 +459,17 @@
return key_column;
}
+/*!
+ \brief Get simple feature type from string
+
+ \param type string
+
+ \return SF type
+*/
SF_FeatureType ftype_from_string(const char *type)
{
SF_FeatureType sf_type;
-
+
if (G_strcasecmp(type, "POINT") == 0)
return SF_POINT;
else if (G_strcasecmp(type, "LINESTRING") == 0)
@@ -496,14 +484,22 @@
return SF_MULTIPOLYGON;
else if (G_strcasecmp(type, "GEOMETRYCOLLECTION") == 0)
return SF_GEOMETRYCOLLECTION;
-
- return SF_UNKNOWN;
-
+ else
+ return SF_UNKNOWN;
+
G_debug(3, "ftype_from_string(): type='%s' -> %d", type, sf_type);
-
+
return sf_type;
}
+/*!
+ \brief Drop feature table
+
+ \param pg_info pointer to Format_info_pg
+
+ \return -1 on error
+ \return 0 on success
+*/
int drop_table(struct Format_info_pg *pg_info)
{
char stmt[DB_SQL_MAX];
@@ -518,6 +514,14 @@
return 0;
}
+/*!
+ \brief Creates new schema for feature table if not exists
+
+ \param pg_info pointer to Format_info_pg
+
+ \return -1 on error
+ \return 0 on success
+*/
int check_schema(const struct Format_info_pg *pg_info)
{
int i, found, nschema;
@@ -558,6 +562,15 @@
return 0;
}
+/*!
+ \brief Create new feature table
+
+ \param pg_info pointer to Format_info_pg
+ \param Fi pointer to field_info
+
+ \return -1 on error
+ \return 0 on success
+*/
int create_table(struct Format_info_pg *pg_info, const struct field_info *Fi)
{
int spatial_index, primary_key;
@@ -760,6 +773,11 @@
return 0;
}
+/*!
+ \brief Establish PG connection (pg_info->conninfo)
+
+ \param pg_info pointer to Format_info_pg
+*/
void connect_db(struct Format_info_pg *pg_info)
{
pg_info->conn = PQconnectdb(pg_info->conninfo);
@@ -771,6 +789,60 @@
}
/*!
+ \brief Check for topology schema (pg_info->toposchema_name)
+
+ \param pg_info pointer to Format_info_pg
+
+ \return 0 schema exists
+ \return 1 schema doesn't exists
+ */
+int check_topo(struct Format_info_pg *pg_info, struct Plus_head *plus)
+{
+ char stmt[DB_SQL_MAX];
+
+ PGresult *res;
+
+ /* connect database */
+ if (!pg_info->conn)
+ connect_db(pg_info);
+
+ if (pg_info->toposchema_name)
+ return 0;
+
+ /* check if topology layer/schema exists */
+ sprintf(stmt,
+ "SELECT t.name,t.hasz,l.feature_column FROM topology.layer "
+ "AS l JOIN topology.topology AS t ON l.topology_id = t.id "
+ "WHERE schema_name = '%s' AND table_name = '%s'",
+ pg_info->schema_name, pg_info->table_name);
+ G_debug(2, "SQL: %s", stmt);
+
+ res = PQexec(pg_info->conn, stmt);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
+ PQntuples(res) != 1) {
+ G_debug(1, "Topology layers for '%s.%s' not found (%s)",
+ pg_info->schema_name, pg_info->table_name,
+ PQerrorMessage(pg_info->conn));
+ if (res)
+ PQclear(res);
+ return 1;
+ }
+
+ pg_info->toposchema_name = G_store(PQgetvalue(res, 0, 0));
+ pg_info->topogeom_column = G_store(PQgetvalue(res, 0, 2));
+
+ G_debug(1, "PostGIS topology detected: schema = %s column = %s",
+ pg_info->toposchema_name, pg_info->topogeom_column);
+
+ /* check for 3D */
+ if (strcmp(PQgetvalue(res, 0, 1), "t") == 0)
+ plus->with_z = WITH_Z;
+ PQclear(res);
+
+ return 0;
+}
+
+/*!
\brief Parse BBOX string
\param value string buffer
@@ -986,8 +1058,7 @@
\return -1 on failure
*/
int read_p_line(struct Plus_head *plus, int n,
- const struct edge_data *data,
- struct Format_info_pg *pg_info)
+ const struct edge_data *data)
{
int tp;
struct P_line *line;
@@ -1120,24 +1191,21 @@
}
/*!
- \brief Read topo header info only
+ \brief Read topo (from PostGIS topology schema) header info only
\param[in,out] plus pointer to Plus_head struct
\return 0 on success
\return -1 on error
*/
-int load_plus_head(struct Format_info_pg *pg_info, PGresult *res, struct Plus_head *plus)
+int load_plus_head(struct Format_info_pg *pg_info, struct Plus_head *plus)
{
char stmt[DB_SQL_MAX];
+ PGresult *res;
+
plus->off_t_size = -1;
- /* check for 3D */
- if (strcmp(PQgetvalue(res, 0, 1), "t") == 0)
- plus->with_z = WITH_Z;
- PQclear(res);
-
/* get map bounding box */
sprintf(stmt,
"SELECT ST_3DExtent(%s) FROM \"%s\".\"%s\"",
@@ -1213,7 +1281,6 @@
"SELECT COUNT(*) FROM \"%s\".face WHERE mbr IS NOT NULL",
pg_info->toposchema_name);
plus->n_clines = num_of_records(pg_info, stmt);
- plus->n_lines += plus->n_plines; /* register isolated nodes as points */
G_debug(3, "Vect_open_topo_pg(): n_clines=%d", plus->n_clines);
/* TODO: nflines | n_klines */
@@ -1225,19 +1292,30 @@
}
/*!
- \brief Read topo info
+ \brief Read topo info (from PostGIS topology schema)
+ \param pg_info pointer to Format_info_pg
\param[in,out] plus pointer to Plus_head struct
-
+ \param head_only TRUE to read only header info
+
\return 0 on success
\return -1 on error
*/
-int load_plus(struct Format_info_pg *pg_info, PGresult *res, struct Plus_head *plus)
+int load_plus(struct Format_info_pg *pg_info, struct Plus_head *plus,
+ int head_only)
{
int i, id, ntuples;
char stmt[DB_SQL_MAX];
struct edge_data line_data;
+ PGresult *res;
+
+ if (load_plus_head(pg_info, plus) != 0)
+ return -1;
+
+ if (head_only)
+ return 0;
+
/* read nodes (GRASS Topo)
note: standalone nodes are ignored
*/
@@ -1258,6 +1336,7 @@
return -1;
}
+ G_debug(3, "load_plus(): n_nodes = %d", plus->n_nodes);
dig_alloc_nodes(plus, plus->n_nodes);
for (i = 1; i <= plus->n_nodes; i++) {
id = atoi(PQgetvalue(res, i - 1, 0));
@@ -1267,8 +1346,9 @@
/* read lines (GRASS Topo)
- standalone nodes -> points
- - edges -> lines/bouindaries
+ - edges -> lines/boundaries
*/
+ G_debug(3, "load_plus(): n_lines = %d", plus->n_lines);
dig_alloc_lines(plus, plus->n_lines);
/* read PostGIS Topo standalone nodes */
@@ -1282,7 +1362,7 @@
G_debug(2, "SQL: %s", stmt);
res = PQexec(pg_info->conn, stmt);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
- PQntuples(res) > plus->n_lines) {
+ PQntuples(res) > plus->n_plines) {
G_warning(_("Unable to read lines"));
if (res)
PQclear(res);
@@ -1294,7 +1374,7 @@
for (i = 0; i < ntuples; i++) {
/* process standalone nodes (PostGIS Topo) */
line_data.id = atoi(PQgetvalue(res, i, 0));
- read_p_line(plus, i + 1, &line_data, pg_info);
+ read_p_line(plus, i + 1, &line_data);
}
PQclear(res);
@@ -1321,7 +1401,7 @@
line_data.end_node = atoi(PQgetvalue(res, i, 2));
line_data.left_face = atoi(PQgetvalue(res, i, 3));
line_data.right_face = atoi(PQgetvalue(res, i, 4));
- read_p_line(plus, plus->n_plines + i + 1, &line_data, pg_info);
+ read_p_line(plus, plus->n_plines + i + 1, &line_data);
}
PQclear(res);
@@ -1339,14 +1419,14 @@
return -1;
}
+ G_debug(3, "load_plus(): n_areas = %d", plus->n_areas);
dig_alloc_areas(plus, plus->n_areas);
G_zero(&line_data, sizeof(struct edge_data));
for (i = 1; i <= plus->n_areas; i++) {
line_data.id = line_data.left_face = atoi(PQgetvalue(res, i - 1, 0));
read_p_area(plus, i, line_data.id, pg_info);
/* add centroids */
- read_p_line(plus, plus->n_lines - plus->n_clines + i,
- &line_data, pg_info);
+ read_p_line(plus, plus->n_lines - plus->n_clines + i, &line_data);
}
PQclear(res);
Modified: grass/trunk/lib/vector/Vlib/pg_local_proto.h
===================================================================
--- grass/trunk/lib/vector/Vlib/pg_local_proto.h 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/pg_local_proto.h 2012-06-05 12:44:17 UTC (rev 51975)
@@ -52,6 +52,8 @@
SF_FeatureType cache_feature(const char *, int,
struct Format_info_cache *,
struct feat_parts *);
+int set_initial_query(struct Format_info_pg *, int);
+int load_plus(struct Format_info_pg *, struct Plus_head *, int);
#endif /* HAVE_POSTGRES */
Modified: grass/trunk/lib/vector/Vlib/read.c
===================================================================
--- grass/trunk/lib/vector/Vlib/read.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/read.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -50,7 +50,7 @@
#endif
#ifdef HAVE_POSTGRES
, {
- read_dummy, V1_read_next_line_pg, V2_read_next_line_pg, V2_read_next_line_pg}
+ read_dummy, V1_read_next_line_pg, V2_read_next_line_pg, V3_read_next_line_pg}
#else
, {
read_dummy, format, format, format}
Modified: grass/trunk/lib/vector/Vlib/read_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/read_pg.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/Vlib/read_pg.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -44,7 +44,6 @@
struct Format_info_cache *,
struct feat_parts *);
static int error_corrupted_data(const char *);
-static int set_initial_query();
static void reallocate_cache(struct Format_info_cache *, int);
static void add_fpart(struct feat_parts *, SF_FeatureType, int, int);
static int read_centroid_pg(struct Format_info_pg *, int, struct line_pnts *);
@@ -86,7 +85,8 @@
}
/*!
- \brief Read next feature from PostGIS layer on topological level.
+ \brief Read next feature from PostGIS layer on topological level
+ (simple feature access).
This function implements sequential access.
@@ -201,6 +201,28 @@
}
/*!
+ \brief Read next feature from PostGIS layer on topological level
+ (PostGIS topology 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 V3_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p,
+ struct line_cats *line_c)
+{
+ return V2_read_next_line_pg(Map, line_p, line_c);
+}
+
+/*!
\brief Read feature from PostGIS layer at given offset (level 1 without topology)
This function implements random access on level 1.
@@ -300,7 +322,7 @@
return -1;
}
- G_debug(4, "V3_read_line_pg() line = %d type = %d offset = %llu",
+ G_debug(4, "V3_read_line_pg() line = %d type = %d offset = %lu",
line, Line->type, Line->offset);
if (!line_p && !line_c)
@@ -348,9 +370,6 @@
G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
return -1;
#endif
-
-
-
}
#ifdef HAVE_POSTGRES
@@ -478,7 +497,7 @@
if (fid < 1) {
/* next (read n features) */
if (!pg_info->res) {
- if (set_initial_query(pg_info) == -1)
+ if (set_initial_query(pg_info, FALSE) == -1)
return -1;
}
}
@@ -1102,30 +1121,53 @@
\return 0 on success
\return -1 on error
*/
-int set_initial_query(struct Format_info_pg *pg_info)
+int set_initial_query(struct Format_info_pg *pg_info, int fetch_all)
{
char stmt[DB_SQL_MAX];
if (execute(pg_info->conn, "BEGIN") == -1)
return -1;
- sprintf(stmt,
- "DECLARE %s_%s%p CURSOR FOR SELECT %s,%s FROM \"%s\".\"%s\"",
- pg_info->schema_name, pg_info->table_name, pg_info->conn,
- pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
- pg_info->table_name);
+ if (!pg_info->toposchema_name) {
+ /* simple feature access */
+ sprintf(stmt,
+ "DECLARE %s_%s%p CURSOR FOR SELECT %s,%s FROM \"%s\".\"%s\"",
+ pg_info->schema_name, pg_info->table_name, pg_info->conn,
+ pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
+ pg_info->table_name);
+ }
+ else {
+ /* topology access */
+ sprintf(stmt,
+ "DECLARE %s_%s%p CURSOR FOR "
+ "SELECT geom,row_number() OVER "
+ "(ORDER BY ST_GeometryType(geom) DESC) AS fid FROM ("
+ "SELECT geom FROM \"%s\".node WHERE 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 geom FROM \"%s\".edge) AS foo",
+ pg_info->schema_name, pg_info->table_name, pg_info->conn,
+ pg_info->toposchema_name, pg_info->toposchema_name,
+ pg_info->toposchema_name, pg_info->toposchema_name);
+ }
G_debug(2, "SQL: %s", stmt);
-
+
if (execute(pg_info->conn, stmt) == -1) {
execute(pg_info->conn, "ROLLBACK");
return -1;
}
- sprintf(stmt, "FETCH %d in %s_%s%p", CURSOR_PAGE,
- pg_info->schema_name, pg_info->table_name, pg_info->conn);
+ if (fetch_all)
+ sprintf(stmt, "FETCH ALL in %s_%s%p",
+ pg_info->schema_name, pg_info->table_name, pg_info->conn);
+ else
+ sprintf(stmt, "FETCH %d in %s_%s%p", CURSOR_PAGE,
+ pg_info->schema_name, pg_info->table_name, pg_info->conn);
pg_info->res = PQexec(pg_info->conn, stmt);
if (!pg_info->res) {
execute(pg_info->conn, "ROLLBACK");
+ G_warning(_("Unable to get features"));
return -1;
}
pg_info->next_line = 0;
Modified: grass/trunk/lib/vector/diglib/spindex.c
===================================================================
--- grass/trunk/lib/vector/diglib/spindex.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/diglib/spindex.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -821,7 +821,7 @@
}
/*!
- \brief Find box for area
+ \brief Find bounding box for given area
\param Plus pointer to Plus_head structure
\param[in,out] list list with area id and search box (in)/area box (out)
@@ -844,7 +844,7 @@
G_debug(3, "dig_find_line_box()");
if (list->n_values < 1)
- G_fatal_error(_("No line id given"));
+ G_fatal_error(_("Unable to get area bounding box. No feature id given."));
rect.boundary[0] = list->box[0].W;
rect.boundary[1] = list->box[0].S;
@@ -858,7 +858,7 @@
else
ret = rtree_search(Plus->Area_spidx, &rect, (void *)_set_item_box, list, Plus);
- return (ret);
+ return ret;
}
/*!
Modified: grass/trunk/lib/vector/diglib/spindex_rw.c
===================================================================
--- grass/trunk/lib/vector/diglib/spindex_rw.c 2012-06-04 21:15:02 UTC (rev 51974)
+++ grass/trunk/lib/vector/diglib/spindex_rw.c 2012-06-05 12:44:17 UTC (rev 51975)
@@ -93,7 +93,7 @@
else if (ptr->off_t_size == 8)
length = 117;
else
- G_fatal_error("topo must be written before sidx");
+ G_fatal_error(_("Topology file must be written before spatial index file"));
}
else if (ptr->spidx_port.off_t_size == 8) {
if (ptr->off_t_size == 4)
@@ -101,7 +101,7 @@
else if (ptr->off_t_size == 8)
length = 145;
else
- G_fatal_error("topo must be written before sidx");
+ G_fatal_error(_("Topology file must be written before spatial index file"));
}
/* bytes 7 - 10 : header size */
@@ -1335,8 +1335,6 @@
assert(t);
if (!stack_init) {
- struct Rect_Real *r;
-
for (i = 0; i < MAXLEVEL; i++) {
for (j = 0; j < MAXCARD; j++) {
s[i].sn.branch[j].rect.boundary = G_malloc(6 * sizeof(RectReal));
More information about the grass-commit
mailing list