123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- /*!
- \file lib/imagery/iclass_perimeter.c
- \brief Imagery library - functions for wx.iclass
- Computation based on training areas for supervised classification.
- Based on i.class module (GRASS 6).
- Vector map with training areas is used to determine corresponding
- cells by computing cells on area perimeter.
- Copyright (C) 1999-2007, 2011 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- \author David Satnik, Central Washington University (original author)
- \author Markus Neteler <neteler itc.it> (i.class module)
- \author Bernhard Reiter <bernhard intevation.de> (i.class module)
- \author Brad Douglas <rez touchofmadness.com>(i.class module)
- \author Glynn Clements <glynn gclements.plus.com> (i.class module)
- \author Hamish Bowman <hamish_b yahoo.com> (i.class module)
- \author Jan-Oliver Wagner <jan intevation.de> (i.class module)
- \author Anna Kratochvilova <kratochanna gmail.com> (rewriting for wx.iclass)
- \author Vaclav Petras <wenzeslaus gmail.com> (rewriting for wx.iclass)
- */
- #include <stdlib.h>
- #include <grass/vector.h>
- #include <grass/raster.h>
- #include <grass/glocale.h>
- #include "iclass_local_proto.h"
- #define extrema(x,y,z) (((x<y)&&(z<y))||((x>y)&&(z>y)))
- #define non_extrema(x,y,z) (((x<y)&&(y<z))||((x>y)&&(y>z)))
- /*!
- \brief Creates perimeters from vector areas of given category.
- \param Map vector map
- \param layer_name layer name (within vector map)
- \param category vector category (cat column value)
- \param[out] perimeters list of perimeters
- \param band_region region which determines perimeter cells
- \return number of areas of given cat
- \return -1 on error
- */
- int vector2perimeters(struct Map_info *Map, const char *layer_name,
- int category, IClass_perimeter_list * perimeters,
- struct Cell_head *band_region)
- {
- struct line_pnts *points;
- int nareas, nareas_cat, layer;
- int i, cat, ret;
- int j;
- G_debug(3, "iclass_vector2perimeters():layer = %s, category = %d",
- layer_name, category);
- layer = Vect_get_field_number(Map, layer_name);
- nareas = Vect_get_num_areas(Map);
- if (nareas == 0)
- return 0;
- nareas_cat = 0;
- /* find out, how many areas have given category */
- for (i = 1; i <= nareas; i++) {
- if (!Vect_area_alive(Map, i))
- continue;
- cat = Vect_get_area_cat(Map, i, layer);
- if (cat < 0) {
- /* no centroid, no category */
- }
- else if (cat == category) {
- nareas_cat++;
- }
- }
- if (nareas_cat == 0)
- return 0;
- perimeters->nperimeters = nareas_cat;
- perimeters->perimeters =
- (IClass_perimeter *) G_calloc(nareas_cat, sizeof(IClass_perimeter));
- j = 0; /* area with cat */
- for (i = 1; i <= nareas; i++) {
- if (!Vect_area_alive(Map, i))
- continue;
- cat = Vect_get_area_cat(Map, i, layer);
- if (cat < 0) {
- /* no centroid, no category */
- }
- else if (cat == category) {
- j++;
- points = Vect_new_line_struct(); /* Vect_destroy_line_struct */
- ret = Vect_get_area_points(Map, i, points);
- if (ret <= 0) {
- Vect_destroy_line_struct(points);
- free_perimeters(perimeters);
- G_warning(_("Get area %d failed"), i);
- return -1;
- }
- if (make_perimeter
- (points, &perimeters->perimeters[j - 1], band_region) <= 0) {
- Vect_destroy_line_struct(points);
- free_perimeters(perimeters);
- G_warning(_("Perimeter computation failed"));
- return -1;
- }
- Vect_destroy_line_struct(points);
- }
- }
- /* Vect_close(&Map); */
- return nareas_cat;
- }
- /*!
- \brief Frees all perimeters in list of perimeters.
- It also frees list of perimeters itself.
- \param perimeters list of perimeters
- */
- void free_perimeters(IClass_perimeter_list * perimeters)
- {
- int i;
- G_debug(5, "free_perimeters()");
- for (i = 0; i < perimeters->nperimeters; i++) {
- G_free(perimeters->perimeters[i].points);
- }
- G_free(perimeters->perimeters);
- }
- /*!
- \brief Creates one perimeter from vector area.
- \param points list of vertices represting area
- \param[out] perimeter perimeter
- \param band_region region which determines perimeter cells
- \return 1 on success
- \return 0 on error
- */
- int make_perimeter(struct line_pnts *points, IClass_perimeter * perimeter,
- struct Cell_head *band_region)
- {
- IClass_point *tmp_points;
- IClass_point *vertex_points;
- int i, first, prev, skip, next;
- int count, vertex_count;
- int np; /* perimeter estimate */
- G_debug(5, "iclass_make_perimeter()");
- count = points->n_points;
- tmp_points = (IClass_point *) G_calloc(count, sizeof(IClass_point)); /* TODO test */
- for (i = 0; i < count; i++) {
- G_debug(5, "iclass_make_perimeter(): points: x: %f y: %f",
- points->x[i], points->y[i]);
- /* This functions are no longer used because of the different behavior
- of Rast_easting_to_col depending whether location is LL or not.
- It makes problem in interactive scatter plot tool,
- which defines its own coordinates systems for the plots and
- therefore it requires the function to work always in same way
- without hidden dependency on location type.
- tmp_points[i].y = Rast_northing_to_row(points->y[i], band_region);
- tmp_points[i].x = Rast_easting_to_col(points->x[i], band_region);
- */
- tmp_points[i].y = (band_region->north - points->y[i]) / band_region->ns_res;
- tmp_points[i].x = (points->x[i] - band_region->west) / band_region->ew_res;
- }
- /* find first edge which is not horizontal */
- first = -1;
- prev = count - 1;
- for (i = 0; i < count; prev = i++) {
- /* non absurd polygon has vertexes with different y coordinate */
- if (tmp_points[i].y != tmp_points[prev].y) {
- first = i;
- break;
- }
- }
- if (first < 0) {
- G_free(tmp_points);
- G_warning(_("Absurd polygon."));
- return 0;
- }
- /* copy tmp to vertex list collapsing adjacent horizontal edges */
- /* vertex_count <= count, size of vertex_points is count */
- vertex_points = (IClass_point *) G_calloc(count, sizeof(IClass_point)); /* TODO test */
- skip = 0;
- vertex_count = 0;
- i = first; /* stmt not necssary */
- do {
- if (!skip) {
- vertex_points[vertex_count].x = tmp_points[i].x;
- vertex_points[vertex_count].y = tmp_points[i].y;
- vertex_count++;
- }
- prev = i++;
- if (i >= count)
- i = 0;
- if ((next = i + 1) >= count)
- next = 0;
- skip = ((tmp_points[prev].y == tmp_points[i].y) &&
- (tmp_points[next].y == tmp_points[i].y));
- }
- while (i != first);
- G_free(tmp_points);
- /* count points on the perimeter */
- np = 0;
- prev = vertex_count - 1;
- for (i = 0; i < vertex_count; prev = i++) {
- np += abs(vertex_points[prev].y - vertex_points[i].y);
- }
- /* allocate perimeter list */
- perimeter->points = (IClass_point *) G_calloc(np, sizeof(IClass_point));
- if (!perimeter->points) {
- G_free(vertex_points);
- G_warning(_("Outlined area is too large."));
- return 0;
- }
- /* store the perimeter points */
- perimeter->npoints = 0;
- prev = vertex_count - 1;
- for (i = 0; i < vertex_count; prev = i++) {
- edge2perimeter(perimeter, vertex_points[prev].x,
- vertex_points[prev].y, vertex_points[i].x,
- vertex_points[i].y);
- }
- /*
- * now decide which vertices should be included
- * local extrema are excluded
- * local non-extrema are included
- * vertices of horizontal edges which are pseudo-extrema
- * are excluded.
- * one vertex of horizontal edges which are pseudo-non-extrema
- * are included.
- */
- prev = vertex_count - 1;
- i = 0;
- do {
- next = i + 1;
- if (next >= vertex_count)
- next = 0;
- if (extrema
- (vertex_points[prev].y, vertex_points[i].y,
- vertex_points[next].y))
- skip = 1;
- else if (non_extrema
- (vertex_points[prev].y, vertex_points[i].y,
- vertex_points[next].y))
- skip = 0;
- else {
- skip = 0;
- if (++next >= vertex_count)
- next = 0;
- if (extrema
- (vertex_points[prev].y, vertex_points[i].y,
- vertex_points[next].y))
- skip = 1;
- }
- if (!skip)
- perimeter_add_point(perimeter, vertex_points[i].x,
- vertex_points[i].y);
- i = next;
- prev = i - 1;
- }
- while (i != 0);
- G_free(vertex_points);
- /* sort the edge points by row and then by col */
- qsort(perimeter->points, (size_t) perimeter->npoints,
- sizeof(IClass_point), edge_order);
- return 1;
- }
- /*!
- \brief Converts edge to cells.
- It rasterizes edge given by two vertices.
- Resterized points are added to perimeter.
- \param perimeter perimeter
- \param x0,y0 first edge point row and cell
- \param x1,y1 second edge point row and cell
- \return 1 on success
- \return 0 on error
- */
- int edge2perimeter(IClass_perimeter * perimeter, int x0, int y0, int x1,
- int y1)
- {
- float m;
- float x;
- if (y0 == y1)
- return 0;
- x = x0;
- m = (float)(x0 - x1) / (float)(y0 - y1);
- if (y0 < y1) {
- while (++y0 < y1) {
- x0 = (x += m) + .5;
- perimeter_add_point(perimeter, x0, y0);
- }
- }
- else {
- while (--y0 > y1) {
- x0 = (x -= m) + .5;
- perimeter_add_point(perimeter, x0, y0);
- }
- }
- return 1;
- }
- /*!
- \brief Adds point to perimeter.
- \a perimeter has to have allocated space for \c points member.
- \param perimeter perimeter
- \param x,y point row and cell
- */
- void perimeter_add_point(IClass_perimeter * perimeter, int x, int y)
- {
- int n;
- G_debug(5, "perimeter_add_point(): x: %d, y: %d", x, y);
- n = perimeter->npoints++;
- perimeter->points[n].x = x;
- perimeter->points[n].y = y;
- }
- /*!
- \brief Determines points order during sorting.
- \param aa first IClass_point
- \param bb second IClass_point
- */
- int edge_order(const void *aa, const void *bb)
- {
- const IClass_point *a = aa;
- const IClass_point *b = bb;
- if (a->y < b->y)
- return -1;
- if (a->y > b->y)
- return 1;
- if (a->x < b->x)
- return -1;
- if (a->x > b->x)
- return 1;
- return 0;
- }
|