GRASS 8 Programmer's Manual 8.6.0dev(2026)-1878fdfec5
Loading...
Searching...
No Matches
iclass_perimeter.c
Go to the documentation of this file.
1/*!
2 \file lib/imagery/iclass_perimeter.c
3
4 \brief Imagery library - functions for wx.iclass
5
6 Computation based on training areas for supervised classification.
7 Based on i.class module (GRASS 6).
8
9 Vector map with training areas is used to determine corresponding
10 cells by computing cells on area perimeter.
11
12 SPDX-FileCopyrightText: 1999-2007, 2011 GRASS Development Team
13 SPDX-License-Identifier: GPL-2.0-or-later
14
15 \author David Satnik, Central Washington University (original author)
16 \author Markus Neteler <neteler itc.it> (i.class module)
17 \author Bernhard Reiter <bernhard intevation.de> (i.class module)
18 \author Brad Douglas <rez touchofmadness.com>(i.class module)
19 \author Glynn Clements <glynn gclements.plus.com> (i.class module)
20 \author Hamish Bowman <hamish_b yahoo.com> (i.class module)
21 \author Jan-Oliver Wagner <jan intevation.de> (i.class module)
22 \author Anna Kratochvilova <kratochanna gmail.com> (rewriting for wx.iclass)
23 \author Vaclav Petras <wenzeslaus gmail.com> (rewriting for wx.iclass)
24 */
25
26#include <stdlib.h>
27
28#include <grass/vector.h>
29#include <grass/raster.h>
30#include <grass/glocale.h>
31
32#include "iclass_local_proto.h"
33
34#define extrema(x, y, z) (((x < y) && (z < y)) || ((x > y) && (z > y)))
35#define non_extrema(x, y, z) (((x < y) && (y < z)) || ((x > y) && (y > z)))
36
37/*!
38 \brief Creates perimeters from vector areas of given category.
39
40 \param Map vector map
41 \param layer_name layer name (within vector map)
42 \param category vector category (cat column value)
43 \param[out] perimeters list of perimeters
44 \param band_region region which determines perimeter cells
45
46 \return number of areas of given cat
47 \return -1 on error
48 */
49int vector2perimeters(struct Map_info *Map, const char *layer_name,
51 struct Cell_head *band_region)
52{
53 struct line_pnts *points;
54
55 int nareas, nareas_cat, layer;
56
57 int i, cat, ret;
58
59 int j;
60
61 G_debug(3, "iclass_vector2perimeters():layer = %s, category = %d",
62 layer_name, category);
63
64 layer = Vect_get_field_number(Map, layer_name);
66 if (nareas == 0)
67 return 0;
68
69 nareas_cat = 0;
70 /* find out, how many areas have given category */
71 for (i = 1; i <= nareas; i++) {
72 if (!Vect_area_alive(Map, i))
73 continue;
74 cat = Vect_get_area_cat(Map, i, layer);
75 if (cat < 0) {
76 /* no centroid, no category */
77 }
78 else if (cat == category) {
79 nareas_cat++;
80 }
81 }
82 if (nareas_cat == 0)
83 return 0;
84
85 perimeters->nperimeters = nareas_cat;
86 perimeters->perimeters =
88
89 j = 0; /* area with cat */
90 for (i = 1; i <= nareas; i++) {
91 if (!Vect_area_alive(Map, i))
92 continue;
93 cat = Vect_get_area_cat(Map, i, layer);
94 if (cat < 0) {
95 /* no centroid, no category */
96 }
97 else if (cat == category) {
98 j++;
99
100 points = Vect_new_line_struct(); /* Vect_destroy_line_struct */
101 ret = Vect_get_area_points(Map, i, points);
102
103 if (ret <= 0) {
106 G_warning(_("Get area %d failed"), i);
107 return -1;
108 }
109 if (make_perimeter(points, &perimeters->perimeters[j - 1],
110 band_region) <= 0) {
113 G_warning(_("Perimeter computation failed"));
114 return -1;
115 }
117 }
118 }
119
120 /* Vect_close(&Map); */
121
122 return nareas_cat;
123}
124
125/*!
126 \brief Frees all perimeters in list of perimeters.
127
128 It also frees list of perimeters itself.
129
130 \param perimeters list of perimeters
131 */
133{
134 int i;
135
136 G_debug(5, "free_perimeters()");
137
138 for (i = 0; i < perimeters->nperimeters; i++) {
139 G_free(perimeters->perimeters[i].points);
140 }
141 G_free(perimeters->perimeters);
142}
143
144/*!
145 \brief Creates one perimeter from vector area.
146
147 \param points list of vertices represting area
148 \param[out] perimeter perimeter
149 \param band_region region which determines perimeter cells
150
151 \return 1 on success
152 \return 0 on error
153 */
155 struct Cell_head *band_region)
156{
158
160
161 int i, first, prev, skip, next;
162
163 int count, vertex_count;
164
165 int np; /* perimeter estimate */
166
167 G_debug(5, "iclass_make_perimeter()");
168 count = points->n_points;
169
170 tmp_points =
171 (IClass_point *)G_calloc(count, sizeof(IClass_point)); /* TODO test */
172
173 for (i = 0; i < count; i++) {
174 G_debug(5, "iclass_make_perimeter(): points: x: %f y: %f", points->x[i],
175 points->y[i]);
176
177 /* This functions are no longer used because of the different behavior
178 of Rast_easting_to_col depending whether location is LL or not.
179 It makes problem in interactive scatter plot tool,
180 which defines its own coordinates systems for the plots and
181 therefore it requires the function to work always in same way
182 without hidden dependency on location type.
183
184 tmp_points[i].y = Rast_northing_to_row(points->y[i], band_region);
185 tmp_points[i].x = Rast_easting_to_col(points->x[i], band_region);
186 */
187
188 tmp_points[i].y =
189 (band_region->north - points->y[i]) / band_region->ns_res;
190 tmp_points[i].x =
191 (points->x[i] - band_region->west) / band_region->ew_res;
192 }
193
194 /* find first edge which is not horizontal */
195
196 first = -1;
197 prev = count - 1;
198 for (i = 0; i < count; prev = i++) {
199 /* non absurd polygon has vertices with different y coordinates */
200 if (tmp_points[i].y != tmp_points[prev].y) {
201 first = i;
202 break;
203 }
204 }
205 if (first < 0) {
207 G_warning(_("Invalid polygon"));
208 return 0;
209 }
210
211 /* copy tmp to vertex list collapsing adjacent horizontal edges */
212
213 /* vertex_count <= count, size of vertex_points is count */
215 (IClass_point *)G_calloc(count, sizeof(IClass_point)); /* TODO test */
216 skip = 0;
217 vertex_count = 0;
218 i = first; /* stmt not necessary */
219
220 do {
221 if (!skip) {
224 vertex_count++;
225 }
226
227 prev = i++;
228 if (i >= count)
229 i = 0;
230 if ((next = i + 1) >= count)
231 next = 0;
232
233 skip = ((tmp_points[prev].y == tmp_points[i].y) &&
234 (tmp_points[next].y == tmp_points[i].y));
235 } while (i != first);
236
238
239 /* count points on the perimeter */
240
241 np = 0;
242 prev = vertex_count - 1;
243 for (i = 0; i < vertex_count; prev = i++) {
244 np += abs(vertex_points[prev].y - vertex_points[i].y);
245 }
246
247 /* allocate perimeter list */
248
249 perimeter->points = (IClass_point *)G_calloc(np, sizeof(IClass_point));
250 if (!perimeter->points) {
252 G_warning(_("Outlined area is too large."));
253 return 0;
254 }
255
256 /* store the perimeter points */
257
258 perimeter->npoints = 0;
259 prev = vertex_count - 1;
260 for (i = 0; i < vertex_count; prev = i++) {
263 }
264
265 /*
266 * now decide which vertices should be included
267 * local extrema are excluded
268 * local non-extrema are included
269 * vertices of horizontal edges which are pseudo-extrema
270 * are excluded.
271 * one vertex of horizontal edges which are pseudo-non-extrema
272 * are included.
273 */
274
275 prev = vertex_count - 1;
276 i = 0;
277 do {
278 next = i + 1;
279 if (next >= vertex_count)
280 next = 0;
281
282 if (extrema(vertex_points[prev].y, vertex_points[i].y,
283 vertex_points[next].y))
284 skip = 1;
285 else if (non_extrema(vertex_points[prev].y, vertex_points[i].y,
286 vertex_points[next].y))
287 skip = 0;
288 else {
289 skip = 0;
290 if (++next >= vertex_count)
291 next = 0;
292 if (extrema(vertex_points[prev].y, vertex_points[i].y,
293 vertex_points[next].y))
294 skip = 1;
295 }
296
297 if (!skip)
299 vertex_points[i].y);
300
301 i = next;
302 prev = i - 1;
303 } while (i != 0);
304
306
307 /* sort the edge points by row and then by col */
308 qsort(perimeter->points, (size_t)perimeter->npoints, sizeof(IClass_point),
309 edge_order);
310
311 return 1;
312}
313
314/*!
315 \brief Converts edge to cells.
316
317 It rasterizes edge given by two vertices.
318 Resterized points are added to perimeter.
319
320 \param perimeter perimeter
321 \param x0,y0 first edge point row and cell
322 \param x1,y1 second edge point row and cell
323
324 \return 1 on success
325 \return 0 on error
326 */
327int edge2perimeter(IClass_perimeter *perimeter, int x0, int y0, int x1, int y1)
328{
329 float m;
330
331 float x;
332
333 if (y0 == y1)
334 return 0;
335
336 x = x0;
337 m = (float)(x0 - x1) / (float)(y0 - y1);
338
339 if (y0 < y1) {
340 while (++y0 < y1) {
341 x0 = (x += m) + .5;
343 }
344 }
345 else {
346 while (--y0 > y1) {
347 x0 = (x -= m) + .5;
349 }
350 }
351
352 return 1;
353}
354
355/*!
356 \brief Adds point to perimeter.
357
358 \a perimeter has to have allocated space for \c points member.
359
360 \param perimeter perimeter
361 \param x,y point row and cell
362 */
364{
365 int n;
366
367 G_debug(5, "perimeter_add_point(): x: %d, y: %d", x, y);
368
369 n = perimeter->npoints++;
370 perimeter->points[n].x = x;
371 perimeter->points[n].y = y;
372}
373
374/*!
375 \brief Determines points order during sorting.
376
377 \param aa first IClass_point
378 \param bb second IClass_point
379 */
380int edge_order(const void *aa, const void *bb)
381{
382 const IClass_point *a = aa;
383
384 const IClass_point *b = bb;
385
386 if (a->y < b->y)
387 return -1;
388 if (a->y > b->y)
389 return 1;
390
391 if (a->x < b->x)
392 return -1;
393 if (a->x > b->x)
394 return 1;
395
396 return 0;
397}
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
#define G_calloc(m, n)
Definition defs/gis.h:137
void G_warning(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition line.c:75
plus_t Vect_get_num_areas(struct Map_info *)
Get number of areas in vector map.
Definition level_two.c:85
int Vect_area_alive(struct Map_info *, int)
Check if area is alive or dead (topological level required)
int Vect_get_field_number(struct Map_info *, const char *)
Get field number of given field.
Definition field.c:601
int Vect_get_area_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points (outer ring) of given area.
int Vect_get_area_cat(struct Map_info *, int, int)
Find FIRST category of given field and area.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:43
#define _(str)
Definition glocale.h:10
void perimeter_add_point(IClass_perimeter *perimeter, int x, int y)
Adds point to perimeter.
int vector2perimeters(struct Map_info *Map, const char *layer_name, int category, IClass_perimeter_list *perimeters, struct Cell_head *band_region)
Creates perimeters from vector areas of given category.
int edge2perimeter(IClass_perimeter *perimeter, int x0, int y0, int x1, int y1)
Converts edge to cells.
void free_perimeters(IClass_perimeter_list *perimeters)
Frees all perimeters in list of perimeters.
int make_perimeter(struct line_pnts *points, IClass_perimeter *perimeter, struct Cell_head *band_region)
Creates one perimeter from vector area.
#define extrema(x, y, z)
#define non_extrema(x, y, z)
int edge_order(const void *aa, const void *bb)
Determines points order during sorting.
int count
double b
Definition r_raster.c:37
2D/3D raster map header (used also for region)
Definition gis.h:443
Vector map info.
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.