2
\file lib/vector/Vlib/build_sfa.c
4
\brief Vector library - Building pseudo-topology for simple feature access
6
Higher level functions for reading/writing/manipulating vectors.
10
- other types : index of the first record (which is FID) in offset array.
12
(C) 2001-2012 by the GRASS Development Team
14
This program is free software under the GNU General Public License
15
(>=v2). Read the file COPYING that comes with GRASS for details.
18
\author Piero Cavalieri
19
\author Various updates for GRASS 7 by Martin Landa <landa.martin gmail.com>
24
#include <grass/gis.h>
25
#include <grass/vector.h>
26
#include <grass/glocale.h>
29
\brief This structure keeps info about geometry parts above current
30
geometry, path to curent geometry in the feature. First 'part' number
31
however is feature id */
39
static void init_parts(struct geom_parts *);
40
static void reset_parts(struct geom_parts *);
41
static void free_parts(struct geom_parts *);
42
static void add_part(struct geom_parts *, int);
43
static void del_part(struct geom_parts *);
44
static void add_parts_to_offset(struct Format_info_offset *,
46
static int add_line(struct Plus_head *, struct Format_info_offset *,
47
int, struct line_pnts *,
48
int, struct geom_parts *);
51
#include "pg_local_proto.h"
53
static int add_geometry_pg(struct Plus_head *,
54
struct Format_info_pg *,
55
struct feat_parts *, int,
58
static void build_pg(struct Map_info *, int);
64
static int add_geometry_ogr(struct Plus_head *,
65
struct Format_info_ogr *,
66
OGRGeometryH, int, int,
69
static void build_ogr(struct Map_info *, int);
75
void init_parts(struct geom_parts * parts)
77
G_zero(parts, sizeof(struct geom_parts));
83
void reset_parts(struct geom_parts * parts)
91
void free_parts(struct geom_parts * parts)
94
G_zero(parts, sizeof(struct geom_parts));
98
\brief Add new part number to parts
100
void add_part(struct geom_parts *parts, int part)
102
if (parts->a_parts == parts->n_parts) {
103
parts->a_parts += 10;
104
parts->part = (int *) G_realloc((void *)parts->part,
105
parts->a_parts * sizeof(int));
107
parts->part[parts->n_parts] = part;
112
\brief Remove last part
114
void del_part(struct geom_parts *parts)
120
\brief Add parts to offset
122
void add_parts_to_offset(struct Format_info_offset *offset,
123
struct geom_parts *parts)
127
if (offset->array_num + parts->n_parts >= offset->array_alloc) {
128
offset->array_alloc += parts->n_parts + 1000;
129
offset->array = (int *)G_realloc(offset->array,
130
offset->array_alloc * sizeof(int));
132
j = offset->array_num;
133
for (i = 0; i < parts->n_parts; i++) {
134
G_debug(4, "add offset %d", parts->part[i]);
135
offset->array[j] = parts->part[i];
138
offset->array_num += parts->n_parts;
142
\brief Add line to support structures
144
int add_line(struct Plus_head *plus, struct Format_info_offset *offset,
145
int type, struct line_pnts *Points,
146
int FID, struct geom_parts *parts)
150
struct bound_box box;
152
if (type != GV_CENTROID) {
153
/* beginning in the offset array */
154
offset_value = offset->array_num;
157
/* TODO : could be used to statore category ? */
158
/* because centroids are read from topology, not from layer */
162
G_debug(4, "Register line: FID = %d offset = %ld", FID, offset_value);
163
dig_line_box(Points, &box);
164
line = dig_add_line(plus, type, Points, &box, offset_value);
165
G_debug(4, "Line registered with line = %d", line);
169
Vect_box_copy(&(plus->box), &box);
171
Vect_box_extend(&(plus->box), &box);
173
if (type != GV_BOUNDARY) {
174
dig_cidx_add_cat(plus, 1, (int)FID, line, type);
177
dig_cidx_add_cat(plus, 0, 0, line, type);
180
/* because centroids are read from topology, not from layer */
181
if (type != GV_CENTROID)
182
add_parts_to_offset(offset, parts);
189
\brief Recursively add geometry (PostGIS) to topology
191
int add_geometry_pg(struct Plus_head *plus,
192
struct Format_info_pg *pg_info,
193
struct feat_parts *fparts, int ipart,
194
int FID, int build, struct geom_parts *parts)
196
int line, i, idx, area, isle, outer_area, ret;
198
double area_size, x, y;
199
SF_FeatureType ftype;
200
struct bound_box box;
201
struct Format_info_offset *offset;
202
struct line_pnts *line_i;
204
ftype = fparts->ftype[ipart];
206
G_debug(4, "add_geometry_pg() FID = %d ftype = %d", FID, ftype);
208
offset = &(pg_info->offset);
215
line_i = pg_info->cache.lines[fparts->idx[ipart]];
216
add_line(plus, offset, GV_POINT, line_i,
220
G_debug(4, "LineString");
221
line_i = pg_info->cache.lines[fparts->idx[ipart]];
222
add_line(plus, offset, GV_LINE, line_i,
226
G_debug(4, "Polygon");
228
/* register boundaries */
229
idx = fparts->idx[ipart];
230
for (i = 0; i < fparts->nlines[ipart]; i++) {
231
line_i = pg_info->cache.lines[idx++];
232
G_debug(4, "part %d", i);
234
line = add_line(plus, offset, GV_BOUNDARY,
238
if (build < GV_BUILD_AREAS)
241
/* add area (each inner ring is also area) */
242
dig_line_box(line_i, &box);
243
dig_find_area_poly(line_i, &area_size);
245
if (area_size > 0) /* area clockwise */
250
area = dig_add_area(plus, 1, lines, &box);
252
/* each area is also isle */
253
lines[0] = -lines[0]; /* island is counter clockwise */
255
isle = dig_add_isle(plus, 1, lines, &box);
257
if (build < GV_BUILD_ATTACH_ISLES)
260
if (i == 0) { /* outer ring */
263
else { /* inner ring */
266
Isle = plus->Isle[isle];
267
Isle->area = outer_area;
269
dig_area_add_isle(plus, outer_area, isle);
273
if (build >= GV_BUILD_CENTROIDS) {
274
/* create virtual centroid */
275
ret = Vect_get_point_in_poly_isl((const struct line_pnts *) pg_info->cache.lines[fparts->idx[ipart]],
276
(const struct line_pnts **) &pg_info->cache.lines[fparts->idx[ipart]] + 1,
277
fparts->nlines[ipart] - 1, &x, &y);
279
G_warning(_("Unable to calculate centroid for area %d"),
284
struct P_topo_c *topo;
286
struct line_pnts *line_c;
288
G_debug(4, " Centroid: %f, %f", x, y);
289
line_c = Vect_new_line_struct();
290
Vect_append_point(line_c, x, y, 0.0);
291
line = add_line(plus, offset, GV_CENTROID, line_c, FID, parts);
293
Line = plus->Line[line];
294
topo = (struct P_topo_c *)Line->topo;
295
topo->area = outer_area;
297
/* register centroid to area */
298
Area = plus->Area[outer_area];
299
Area->centroid = line;
300
Vect_destroy_line_struct(line_c);
305
G_warning(_("Feature type %d not supported"), ftype);
313
\brief Build pseudo-topology for PostGIS layers
315
void build_pg(struct Map_info *Map, int build)
317
int iFeature, ipart, fid, nrecords, npoints;
320
struct Format_info_pg *pg_info;
322
struct feat_parts fparts;
323
struct geom_parts parts;
325
pg_info = &(Map->fInfo.pg);
327
/* initialize data structures */
329
G_zero(&fparts, sizeof(struct feat_parts));
331
/* get all features */
332
if (Vect__open_cursor_next_line_pg(pg_info, TRUE, Map->plus.built) != 0)
337
nrecords = PQntuples(pg_info->res);
338
G_debug(4, "build_pg(): nrecords = %d", nrecords);
339
G_message(_("Registering primitives..."));
340
for (iFeature = 0; iFeature < nrecords; iFeature++) {
342
fid = atoi(PQgetvalue(pg_info->res, iFeature, 1));
344
continue; /* PostGIS Topology: skip features with negative
345
* fid (isles, universal face, ...) */
347
wkb_data = PQgetvalue(pg_info->res, iFeature, 0);
349
G_progress(iFeature + 1, 1e4);
351
/* cache feature (lines) */
352
if (SF_NONE == Vect__cache_feature_pg(wkb_data, FALSE, FALSE,
353
&(pg_info->cache), &fparts)) {
354
G_warning(_("Feature %d without geometry skipped"),
359
/* register all parts */
361
add_part(&parts, fid);
362
for (ipart = 0; ipart < fparts.n_parts; ipart++) {
363
if (fparts.nlines[ipart] < 1) {
364
G_warning(_("Feature %d without geometry skipped"), fid);
368
npoints += pg_info->cache.lines[ipart]->n_points;
370
G_debug(4, "Feature: fid = %d part = %d", fid, ipart);
372
if (fparts.n_parts > 1)
373
add_part(&parts, ipart);
374
add_geometry_pg(&(Map->plus), pg_info, &fparts, ipart,
376
if (fparts.n_parts > 1)
380
/* read next feature from cache */
381
pg_info->cache.lines_next = 0;
385
G_message(_n("One primitive registered", "%d primitives registered", Map->plus.n_lines), Map->plus.n_lines);
386
G_message(_n("One vertex registered", "%d vertices registered", npoints), npoints);
388
Map->plus.built = GV_BUILD_BASE;
390
PQclear(pg_info->res);
393
/* free allocated space */
396
#endif /* HAVE_POSTGRES */
400
\brief Recursively add geometry (OGR) to topology
402
int add_geometry_ogr(struct Plus_head *plus,
403
struct Format_info_ogr *ogr_info,
404
OGRGeometryH hGeom, int FID, int build,
405
struct geom_parts *parts)
407
int i, ret, npoints, line;
408
int area, isle, outer_area;
410
double area_size, x, y;
411
int eType, nRings, iPart, nParts, nPoints;
413
struct bound_box box;
415
struct Format_info_offset *offset;
417
OGRGeometryH hGeom2, hRing;
419
G_debug(4, "add_geometry_ogr() FID = %d", FID);
421
offset = &(ogr_info->offset);
423
/* allocate space in cache */
424
if (!ogr_info->cache.lines) {
425
ogr_info->cache.lines_alloc = 1;
426
ogr_info->cache.lines = (struct line_pnts **) G_malloc(sizeof(struct line_pnts *));
428
ogr_info->cache.lines_types = (int *) G_malloc(sizeof(int));
429
ogr_info->cache.lines[0] = Vect_new_line_struct();
430
ogr_info->cache.lines_types[0] = -1;
433
npoints = outer_area = 0;
434
eType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
435
G_debug(4, "OGR type = %d", eType);
441
ogr_info->cache.lines_types[0] = GV_POINT;
442
Vect_reset_line(ogr_info->cache.lines[0]);
443
Vect_append_point(ogr_info->cache.lines[0], OGR_G_GetX(hGeom, 0),
444
OGR_G_GetY(hGeom, 0), OGR_G_GetZ(hGeom, 0));
445
add_line(plus, offset, GV_POINT, ogr_info->cache.lines[0], FID, parts);
446
npoints += ogr_info->cache.lines[0]->n_points;
450
G_debug(4, "LineString");
452
ogr_info->cache.lines_types[0] = GV_LINE;
453
nPoints = OGR_G_GetPointCount(hGeom);
454
Vect_reset_line(ogr_info->cache.lines[0]);
455
for (i = 0; i < nPoints; i++) {
456
Vect_append_point(ogr_info->cache.lines[0],
457
OGR_G_GetX(hGeom, i), OGR_G_GetY(hGeom, i),
458
OGR_G_GetZ(hGeom, i));
460
add_line(plus, offset, GV_LINE, ogr_info->cache.lines[0], FID, parts);
461
npoints += ogr_info->cache.lines[0]->n_points;
465
G_debug(4, "Polygon");
467
nRings = OGR_G_GetGeometryCount(hGeom);
468
G_debug(4, "Number of rings: %d", nRings);
470
/* alloc space for islands if needed */
471
if (nRings > ogr_info->cache.lines_alloc) {
472
ogr_info->cache.lines_alloc += nRings;
473
ogr_info->cache.lines = (struct line_pnts **) G_realloc(ogr_info->cache.lines,
474
ogr_info->cache.lines_alloc *
475
sizeof(struct line_pnts *));
476
ogr_info->cache.lines_types = (int *) G_realloc(ogr_info->cache.lines_types,
477
ogr_info->cache.lines_alloc * sizeof(int));
479
for (i = ogr_info->cache.lines_alloc - nRings; i < ogr_info->cache.lines_alloc; i++) {
480
ogr_info->cache.lines[i] = Vect_new_line_struct();
481
ogr_info->cache.lines_types[i] = -1;
486
for (iPart = 0; iPart < nRings; iPart++) {
487
ogr_info->cache.lines_types[iPart] = GV_BOUNDARY;
488
hRing = OGR_G_GetGeometryRef(hGeom, iPart);
489
nPoints = OGR_G_GetPointCount(hRing);
490
G_debug(4, " ring %d : nPoints = %d", iPart, nPoints);
492
Vect_reset_line(ogr_info->cache.lines[iPart]);
493
for (i = 0; i < nPoints; i++) {
494
Vect_append_point(ogr_info->cache.lines[iPart],
495
OGR_G_GetX(hRing, i), OGR_G_GetY(hRing, i),
496
OGR_G_GetZ(hRing, i));
498
npoints += ogr_info->cache.lines[iPart]->n_points;
500
/* register boundary */
501
add_part(parts, iPart);
502
line = add_line(plus, offset, GV_BOUNDARY, ogr_info->cache.lines[iPart], FID, parts);
505
if (build < GV_BUILD_AREAS)
508
/* add area (each inner ring is also area) */
509
dig_line_box(ogr_info->cache.lines[iPart], &box);
510
dig_find_area_poly(ogr_info->cache.lines[iPart], &area_size);
512
if (area_size > 0) /* area clockwise */
517
area = dig_add_area(plus, 1, lines, &box);
519
/* each area is also isle */
520
lines[0] = -lines[0]; /* island is counter clockwise */
522
isle = dig_add_isle(plus, 1, lines, &box);
524
if (build < GV_BUILD_ATTACH_ISLES)
527
if (iPart == 0) { /* outer ring */
530
else { /* inner ring */
533
Isle = plus->Isle[isle];
534
Isle->area = outer_area;
536
dig_area_add_isle(plus, outer_area, isle);
540
if (build >= GV_BUILD_CENTROIDS) {
541
/* create virtual centroid */
542
ret = Vect_get_point_in_poly_isl((const struct line_pnts *) ogr_info->cache.lines[0],
543
(const struct line_pnts **) ogr_info->cache.lines + 1,
546
G_warning(_("Unable to calculate centroid for area %d"),
551
struct P_topo_c *topo;
553
G_debug(4, " Centroid: %f, %f", x, y);
554
Vect_reset_line(ogr_info->cache.lines[0]);
555
Vect_append_point(ogr_info->cache.lines[0], x, y, 0.0);
556
line = add_line(plus, offset, GV_CENTROID, ogr_info->cache.lines[0],
559
Line = plus->Line[line];
560
topo = (struct P_topo_c *)Line->topo;
561
topo->area = outer_area;
563
/* register centroid to area */
564
Area = plus->Area[outer_area];
565
Area->centroid = line;
571
case wkbMultiLineString:
572
case wkbMultiPolygon:
573
case wkbGeometryCollection:
574
nParts = OGR_G_GetGeometryCount(hGeom);
575
G_debug(4, "%d geoms -> next level", nParts);
577
/* alloc space for parts if needed */
578
if (nParts > ogr_info->cache.lines_alloc) {
579
ogr_info->cache.lines_alloc += nParts;
580
ogr_info->cache.lines = (struct line_pnts **) G_realloc(ogr_info->cache.lines,
581
ogr_info->cache.lines_alloc *
582
sizeof(struct line_pnts *));
583
ogr_info->cache.lines_types = (int *) G_realloc(ogr_info->cache.lines_types,
584
ogr_info->cache.lines_alloc * sizeof(int));
586
for (i = ogr_info->cache.lines_alloc - nParts; i < ogr_info->cache.lines_alloc; i++) {
587
ogr_info->cache.lines[i] = Vect_new_line_struct();
588
ogr_info->cache.lines_types[i] = -1;
592
/* go thru all parts */
593
for (i = 0; i < nParts; i++) {
595
hGeom2 = OGR_G_GetGeometryRef(hGeom, i);
596
npoints += add_geometry_ogr(plus, ogr_info, hGeom2,
603
G_warning(_("OGR feature type %d not supported"), eType);
610
void build_ogr(struct Map_info *Map, int build)
612
int iFeature, FID, npoints, nskipped;
614
struct Format_info_ogr *ogr_info;
616
OGRFeatureH hFeature;
619
struct geom_parts parts;
621
ogr_info = &(Map->fInfo.ogr);
623
/* initialize data structures */
626
/* Note: Do not use OGR_L_GetFeatureCount (it may scan all features) */
627
OGR_L_ResetReading(ogr_info->layer);
628
npoints = iFeature = nskipped = 0;
629
G_message(_("Registering primitives..."));
630
while ((hFeature = OGR_L_GetNextFeature(ogr_info->layer)) != NULL) {
631
G_debug(3, " Feature %d", iFeature);
633
G_progress(++iFeature, 1e4);
635
hGeom = OGR_F_GetGeometryRef(hFeature);
637
G_debug(3, "Feature %d without geometry skipped", iFeature);
638
OGR_F_Destroy(hFeature);
643
FID = (int) OGR_F_GetFID(hFeature);
644
if (FID == OGRNullFID) {
645
G_debug(3, "OGR feature %d without ID skipped", iFeature);
646
OGR_F_Destroy(hFeature);
650
G_debug(4, " FID = %d", FID);
653
add_part(&parts, FID);
654
npoints += add_geometry_ogr(&(Map->plus), ogr_info, hGeom,
657
OGR_F_Destroy(hFeature);
661
G_message(_n("One primitive registered", "%d primitives registered", Map->plus.n_lines), Map->plus.n_lines);
662
G_message(_n("One vertex registered", "%d vertices registered", npoints), npoints);
665
G_warning(_n("One feature without geometry skipped", "%d features without geometry skipped", nskipped), nskipped);
667
Map->plus.built = GV_BUILD_BASE;
671
#endif /* HAVE_OGR */
674
\brief Build pseudo-topology (for simple features) - internal use only
676
See Vect_build_ogr() and Vect_build_pg() for implemetation issues.
681
- GV_BUILD_ATTACH_ISLES
685
\param Map pointer to Map_info structure
686
\param build build level
691
int Vect__build_sfa(struct Map_info *Map, int build)
693
struct Plus_head *plus;
697
/* check if upgrade or downgrade */
698
if (build < plus->built) {
700
Vect__build_downgrade(Map, build);
705
if (plus->built < GV_BUILD_BASE) {
706
if (Map->format == GV_FORMAT_OGR ||
707
Map->format == GV_FORMAT_OGR_DIRECT) {
709
build_ogr(Map, build);
711
G_fatal_error(_("GRASS is not compiled with OGR support"));
714
else if (Map->format == GV_FORMAT_POSTGIS) {
716
build_pg(Map, build);
718
G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
722
G_fatal_error(_("%s: Native format unsupported"),
723
"Vect__build_sfa()");
733
\brief Dump feature index to file
735
\param Map pointer to Map_info struct
736
\param out file for output (stdout/stderr for example)
741
int Vect_fidx_dump(const struct Map_info *Map, FILE *out)
744
const struct Format_info_offset *offset;
746
if (Map->format != GV_FORMAT_OGR &&
747
Map->format != GV_FORMAT_POSTGIS) {
748
G_warning(_("Feature index is built only for non-native formats. "
749
"Nothing to dump."));
753
if (Map->format == GV_FORMAT_OGR)
754
offset = &(Map->fInfo.ogr.offset);
756
offset = &(Map->fInfo.pg.offset);
758
fprintf(out, "---------- FEATURE INDEX DUMP ----------\n");
760
fprintf(out, "format: %s\n", Vect_maptype_info(Map));
761
if (Vect_maptype(Map) == GV_FORMAT_POSTGIS &&
762
Map->fInfo.pg.toposchema_name)
763
fprintf(out, "topology: PostGIS\n");
765
fprintf(out, "topology: pseudo\n");
766
fprintf(out, "feature type: %s\n",
767
Vect_get_finfo_geometry_type(Map));
768
fprintf(out, "number of features: %d\n\noffset : value (fid or part idx):\n",
769
Vect_get_num_lines(Map));
770
for (i = 0; i < offset->array_num; i++) {
771
fprintf(out, "%6d : %d\n", i, offset->array[i]);