[GRASS-SVN] r57737 - sandbox/turek/scatter_plot
svn_grass at osgeo.org
svn_grass at osgeo.org
Wed Sep 18 08:58:50 PDT 2013
Author: turek
Date: 2013-09-18 08:58:49 -0700 (Wed, 18 Sep 2013)
New Revision: 57737
Modified:
sandbox/turek/scatter_plot/testing_patch.diff
Log:
scatter plot: class list improvements
Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff 2013-09-18 13:49:10 UTC (rev 57736)
+++ sandbox/turek/scatter_plot/testing_patch.diff 2013-09-18 15:58:49 UTC (rev 57737)
@@ -1,1305 +1,6 @@
-Index: lib/imagery/scatt_sccats.c
-===================================================================
---- lib/imagery/scatt_sccats.c (revision 0)
-+++ lib/imagery/scatt_sccats.c (working copy)
-@@ -0,0 +1,405 @@
-+/*!
-+ \file lib/imagery/scatt_cat_rast.c
-+
-+ \brief Imagery library - functions for manipulation with scatter plot structs.
-+
-+ Copyright (C) 2013 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 Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
-+ */
-+
-+#include <grass/raster.h>
-+#include <grass/imagery.h>
-+#include <grass/gis.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <math.h>
-+#include <string.h>
-+
-+/*!
-+ \brief Compute band ids from scatter plot id.
-+
-+ Scatter plot id describes which bands defines the scatter plot.
-+
-+ Let say we have 3 bands, their ids are 0, 1 and 2.
-+ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
-+ All scatter plots:
-+ scatt_id b_1_id b_2_id
-+ 0 0 1
-+ 1 0 2
-+ 2 1 2
-+
-+ \param scatt_id scatter plot id
-+ \param n_bands number of bands
-+ \param [out] b_1_id id of band1
-+ \param[out] b_2_id id of band2
-+
-+ \return 0
-+ */
-+int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
-+{
-+ int n_b1 = n_bands - 1;
-+
-+ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
-+
-+ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
-+
-+ return 0;
-+}
-+
-+
-+/*!
-+ \brief Compute scatter plot id from band ids.
-+
-+ See also I_id_scatt_to_bands().
-+
-+ \param n_bands number of bands
-+ \param b_1_id id of band1
-+ \param b_1_id id of band2
-+ \param [out] scatt_id scatter plot id
-+
-+ \return 0
-+ */
-+int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
-+{
-+ int n_b1 = n_bands - 1;
-+
-+ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
-+
-+ return 0;
-+}
-+
-+/*!
-+ \brief Initialize structure for storing scatter plots data.
-+
-+ \param cats pointer to scCats struct
-+ \param n_bands number of bands
-+ \param type SC_SCATT_DATA - stores scatter plots
-+ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
-+ */
-+void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
-+{
-+ int i_cat;
-+
-+ cats->type = type;
-+
-+ cats->n_cats = 100;
-+ cats->n_a_cats = 0;
-+
-+ cats->n_bands = n_bands;
-+ cats->n_scatts = (n_bands - 1) * n_bands / 2;
-+
-+ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
-+ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
-+
-+ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
-+ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
-+
-+ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
-+ cats->cats_idxs[i_cat] = -1;
-+
-+ return;
-+}
-+
-+/*!
-+ \brief Free data of struct scCats, the structure itself remains alocated.
-+
-+ \param cats pointer to existing scCats struct
-+ */
-+void I_sc_free_cats(struct scCats * cats)
-+{
-+ int i_cat;
-+
-+ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
-+ {
-+ if(cats->cats_arr[i_cat])
-+ {
-+ G_free(cats->cats_arr[i_cat]->scatt_idxs);
-+ G_free(cats->cats_arr[i_cat]->scatts_bands);
-+ G_free(cats->cats_arr[i_cat]->scatts_arr);
-+ G_free(cats->cats_arr[i_cat]);
-+ }
-+ }
-+
-+ G_free(cats->cats_ids);
-+ G_free(cats->cats_idxs);
-+ G_free(cats->cats_arr);
-+
-+ cats->n_cats = 0;
-+ cats->n_a_cats = 0;
-+ cats->n_bands = 0;
-+ cats->n_scatts = 0;
-+ cats->type = -1;
-+
-+ return;
-+}
-+
-+#if 0
-+void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
-+{
-+ a_cats_ids = cats->cats_ids;
-+ * n_a_cats = cats->n_a_cats;
-+}
-+#endif
-+
-+/*!
-+ \brief Add category.
-+
-+ Category represents group of scatter plots.
-+
-+ \param cats pointer to scCats struct
-+
-+ \return assigned category id (starts with 0)
-+ \return -1 if maximum nuber of categories was reached
-+ */
-+int I_sc_add_cat(struct scCats * cats)
-+{
-+ int i_scatt, i_cat_id, cat_id;
-+ int n_a_cats = cats->n_a_cats;
-+
-+ if(cats->n_a_cats >= cats->n_cats)
-+ return -1;
-+
-+ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
-+ if(cats->cats_idxs[i_cat_id] < 0) {
-+ cat_id = i_cat_id;
-+ break;
-+ }
-+
-+ cats->cats_ids[n_a_cats] = cat_id;
-+ cats->cats_idxs[cat_id] = n_a_cats;
-+
-+ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
-+
-+ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
-+ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
-+
-+ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
-+
-+ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
-+
-+ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
-+ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
-+ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
-+
-+ ++cats->n_a_cats;
-+
-+ return cat_id;
-+}
-+
-+#if 0
-+int I_sc_delete_cat(struct scCats * cats, int cat_id)
-+{
-+ int cat_idx, i_cat;
-+
-+ if(cat_id < 0 || cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
-+ G_free(cats->cats_arr[cat_idx]->scatts_bands);
-+ G_free(cats->cats_arr[cat_idx]->scatts_arr);
-+ G_free(cats->cats_arr[cat_idx]);
-+
-+ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
-+ {
-+ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
-+ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
-+ }
-+ cats->cats_idxs[cat_id] = -1;
-+
-+ --cats->n_a_cats;
-+
-+ return 0;
-+}
-+#endif
-+
-+/*!
-+ \brief Insert scatter plot data .
-+ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
-+
-+ \param cats pointer to scCats struct
-+ \param cat_id id number of category.
-+ \param scatt_id id number of scatter plot.
-+
-+ \return 0 on success
-+ \return -1 on failure
-+ */
-+int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+ int band_1, band_2, cat_idx, n_a_scatts;
-+ struct scScatts * scatts;
-+
-+ if(cat_id < 0 || cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
-+ return -1;
-+
-+ scatts = cats->cats_arr[cat_idx];
-+ if(scatts->scatt_idxs[scatt_id] >= 0)
-+ return -1;
-+
-+ if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
-+ return -1;
-+
-+ if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
-+ return -1;
-+
-+ n_a_scatts = scatts->n_a_scatts;
-+
-+ scatts->scatt_idxs[scatt_id] = n_a_scatts;
-+
-+ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
-+
-+ scatts->scatts_bands[n_a_scatts * 2] = band_1;
-+ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
-+
-+ scatts->scatts_arr[n_a_scatts] = scatt_data;
-+ ++scatts->n_a_scatts;
-+
-+ return 0;
-+}
-+
-+#if 0
-+int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
-+{
-+ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
-+ struct scScatts * scatts;
-+
-+ if(cat_id < 0 && cat_id >= cats->n_cats)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
-+ return -1;
-+
-+ scatts = cats->cats_arr[cat_idx];
-+ if(scatts->scatt_idxs[scatt_id] < 0)
-+ return -1;
-+
-+ scatt_data = scatts->scatts_arr[scatt_idx];
-+
-+ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
-+ {
-+ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
-+ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
-+ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
-+ }
-+ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
-+
-+ scatts->scatt_idxs[scatt_id] = -1;
-+
-+ scatt_data = scatts->scatts_arr[scatt_id];
-+ scatts->n_a_scatts--;
-+
-+ return 0;
-+}
-+
-+int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
-+{
-+ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
-+ int cat_idx, scatt_idx, ret;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ if(cat_idx < 0)
-+ return -1;
-+
-+ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
-+ return -1;
-+
-+ cat_idx = cats->cats_idxs[cat_id];
-+ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
-+
-+ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
-+
-+ return 0;
-+}
-+#endif
-+
-+/*!
-+ \brief Insert scatter plot data.
-+
-+ \param scatt_data pointer to existing struct scdScattData
-+ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
-+ \param n_vals number of data values
-+ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
-+ */
-+void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
-+{
-+ scatt_data->n_vals = n_vals;
-+
-+ if(type == SC_SCATT_DATA)
-+ {
-+ if(data)
-+ scatt_data->scatt_vals_arr = (unsigned int *) data;
-+ else {
-+ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
-+ memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
-+ }
-+ scatt_data->b_conds_arr = NULL;
-+ }
-+ else if(type == SC_SCATT_CONDITIONS)
-+ {
-+ if(data)
-+ scatt_data->b_conds_arr = (unsigned char *) data;
-+ else {
-+ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
-+ memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
-+ }
-+ scatt_data->scatt_vals_arr = NULL;
-+ }
-+
-+ return;
-+}
-+
-+
-+#if 0
-+void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
-+{
-+
-+ Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
-+ Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
-+
-+ return;
-+}
-+s
-+void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
-+{
-+ if(!scatt_data->b_conds_arr)
-+ return scatt_data->b_conds_arr;
-+ else if(!scatt_data->scatt_vals_arr)
-+ return scatt_data->scatt_vals_arr;
-+
-+ return NULL;
-+}
-+
-+int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
-+{
-+ if(val_idx < 0 && val_idx > scatt_data->n_vals)
-+ return -1;
-+
-+ if(scatt_data->b_conds_arr)
-+ scatt_data->b_conds_arr[val_idx] = val;
-+ else if(scatt_data->scatt_vals_arr)
-+ scatt_data->scatt_vals_arr[val_idx] = val;
-+ else
-+ return -1;
-+
-+ return 0;
-+}
-+#endif
-Index: lib/imagery/scatt.c
-===================================================================
---- lib/imagery/scatt.c (revision 0)
-+++ lib/imagery/scatt.c (working copy)
-@@ -0,0 +1,871 @@
-+/*!
-+ \file lib/imagery/scatt.c
-+
-+ \brief Imagery library - functions for wx Scatter Plot Tool.
-+
-+ Low level functions used by wx Scatter Plot Tool.
-+
-+ Copyright (C) 2013 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 Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
-+ */
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <math.h>
-+#include <string.h>
-+
-+#include <grass/gis.h>
-+#include <grass/vector.h>
-+#include <grass/raster.h>
-+#include <grass/imagery.h>
-+#include <grass/glocale.h>
-+
-+#include "iclass_local_proto.h"
-+
-+struct rast_row
-+{
-+ CELL * row;
-+ char * null_row;
-+ struct Range rast_range; /*Range of whole raster.*/
-+};
-+
-+/*!
-+ \brief Create pgm header.
-+
-+ Scatter plot internally generates pgm files. These pgms have header in format created by this function.
-+
-+ \param region region to be pgm header generated for
-+ \param [out] header header of pgm file
-+ */
-+static int get_cat_rast_header(struct Cell_head * region, char * header){
-+ return sprintf(header, "P5\n%d\n%d\n1\n", region->cols, region->rows);
-+}
-+
-+/*!
-+ \brief Create category raster conditions file.
-+
-+ \param cat_rast_region region to be file generated for
-+ \param cat_rast path of generated category raster file
-+ */
-+int I_create_cat_rast(struct Cell_head * cat_rast_region, const char * cat_rast)
-+{
-+ FILE * f_cat_rast;
-+ char cat_rast_header[1024];//TODO magic number
-+ int i_row, i_col;
-+ int head_nchars;
-+
-+ unsigned char * row_data;
-+
-+ f_cat_rast = fopen(cat_rast, "wb");
-+ if(!f_cat_rast) {
-+ G_warning("Unable to create category raster condition file <%s>.", cat_rast);
-+ return -1;
-+ }
-+
-+ head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
-+
-+ fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cat_rast);
-+ if (ferror(f_cat_rast)){
-+ fclose(f_cat_rast);
-+ G_warning(_("Unable to write header into category raster condition file <%s>."), cat_rast);
-+ return -1;
-+ }
-+
-+ row_data = (unsigned char *) G_malloc(cat_rast_region->cols * sizeof(unsigned char));
-+ for(i_col = 0; i_col < cat_rast_region->cols; i_col++)
-+ row_data[i_col] = 0 & 255;
-+
-+ for(i_row = 0; i_row < cat_rast_region->rows; i_row++) {
-+ fwrite(row_data, sizeof(unsigned char), (cat_rast_region->cols)/sizeof(unsigned char), f_cat_rast);
-+ if (ferror(f_cat_rast))
-+ {
-+ fclose(f_cat_rast);
-+ G_warning(_("Unable to write into category raster condition file <%s>."), cat_rast);
-+ return -1;
-+ }
-+ }
-+
-+ fclose(f_cat_rast);
-+ return 0;
-+}
-+
-+static int print_reg(struct Cell_head * intersec, const char * pref, int dbg_level)
-+{
-+ G_debug(dbg_level, "%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south,
-+ intersec->east, intersec->west, intersec->ns_res, intersec->ew_res);
-+}
-+
-+/*!
-+ \brief Find intersection region of two regions.
-+
-+ \param A pointer to intersected region
-+ \param B pointer to intersected region
-+ \param [out] intersec pointer to intersection region of regions A B (relevant params of the region are: south, north, east, west)
-+
-+ \return 0 if interection exists
-+ \return -1 if regions does not intersect
-+ */
-+static int regions_intersecion(struct Cell_head * A, struct Cell_head * B, struct Cell_head * intersec)
-+{
-+
-+ if(B->north < A->south) return -1;
-+ else if(B->north > A->north) intersec->north = A->north;
-+ else intersec->north = B->north;
-+
-+ if(B->south > A->north) return -1;
-+ else if(B->south < A->south) intersec->south = A->south;
-+ else intersec->south = B->south;
-+
-+ if(B->east < A->west) return -1;
-+ else if(B->east > A->east) intersec->east = A->east;
-+ else intersec->east = B->east;
-+
-+ if(B->west > A->east) return -1;
-+ else if(B->west < A->west) intersec->west = A->west;
-+ else intersec->west = B->west;
-+
-+ if(intersec->north == intersec->south) return -1;
-+
-+ if(intersec->east == intersec->west) return -1;
-+
-+ return 0;
-+
-+}
-+
-+/*!
-+ \brief Get rows and cols numbers, which defines intersection of the regions.
-+
-+ \param A pointer to intersected region
-+ \param B pointer to intersected region (A and B must have same resolution)
-+ \param [out] A_bounds rows and cols numbers of A stored in south, north, east, west, which defines intersection of A and B
-+ \param [out] B_bounds rows and cols numbers of B stored in south, north, east, west, which defines intersection of A and B
-+
-+ \return 0 if interection exists
-+ \return -1 if regions do not intersect
-+ \return -2 resolution of regions is not same
-+*/
-+static int get_rows_and_cols_bounds(struct Cell_head * A, struct Cell_head * B, struct Cell_head * A_bounds, struct Cell_head * B_bounds)
-+{
-+ float ns_res, ew_res;
-+
-+ struct Cell_head intersec;
-+
-+ /* TODO is it right check? */
-+ if(abs(A->ns_res - B->ns_res) > GRASS_EPSILON) {
-+ G_debug(0, "'get_rows_and_cols_bounds' ns_res does not fit, A->ns_res: %f B->ns_res: %f", A->ns_res, B->ns_res);
-+ return -2;
-+ }
-+
-+ if(abs(A->ew_res - B->ew_res) > GRASS_EPSILON) {
-+ G_debug(0, "'get_rows_and_cols_bounds' ew_res does not fit, A->ew_res: %f B->ew_res: %f", A->ew_res, B->ew_res);
-+ return -2;
-+ }
-+
-+ ns_res = A->ns_res;
-+ ew_res = A->ew_res;
-+
-+ if(regions_intersecion(A, B, &intersec) == -1)
-+ return -1;
-+
-+ A_bounds->north = ceil((A->north - intersec.north - ns_res * 0.5) / ns_res);
-+ A_bounds->south = ceil((A->north - intersec.south - ns_res * 0.5) / ns_res);
-+
-+ A_bounds->east = ceil((intersec.east - A->west - ew_res * 0.5) / ew_res);
-+ A_bounds->west = ceil((intersec.west - A->west - ew_res * 0.5) / ew_res);
-+
-+ B_bounds->north = ceil((B->north - intersec.north - ns_res * 0.5) / ns_res);
-+ B_bounds->south = ceil((B->north - intersec.south - ns_res * 0.5) / ns_res);
-+
-+ B_bounds->east = ceil((intersec.east - B->west - ew_res * 0.5) / ew_res);
-+ B_bounds->west = ceil((intersec.west - B->west - ew_res * 0.5) / ew_res);
-+
-+ return 0;
-+}
-+
-+/*!
-+ \brief Insert raster map patch into pgm file.
-+ Warning: calls Rast_set_window
-+
-+ \param patch_rast name of raster map
-+ \param cat_rast_region region of category raster file
-+ \param cat_rast path to category raster file
-+
-+ \return 0 on success
-+ \return -1 on failure
-+*/
-+int I_insert_patch_to_cat_rast(const char * patch_rast, struct Cell_head * cat_rast_region, const char * cat_rast)
-+{
-+
-+ FILE * f_cat_rast;
-+ struct Cell_head patch_region, patch_bounds, cat_rast_bounds;
-+ char cat_rast_header[1024];//TODO magic number
-+ int i_row, i_col, ncols, nrows, cat_rast_col, patch_col, val;
-+ int head_nchars, ret;
-+ int fd_patch_rast, init_shift, step_shift;
-+ unsigned char * patch_data;
-+
-+ char * null_chunk_row;
-+
-+ const char *mapset;
-+
-+ struct Cell_head patch_lines, cat_rast_lines;
-+
-+ unsigned char * row_data;
-+
-+ f_cat_rast = fopen(cat_rast, "rb+");
-+ if(!f_cat_rast) {
-+ G_warning(_("Unable to open category raster condtions file <%s>."), cat_rast);
-+ return -1;
-+ }
-+
-+ head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
-+ if ((mapset = G_find_raster(patch_rast,"")) == NULL) {
-+ fclose(f_cat_rast);
-+ G_warning(_("Unable to find patch raster <%s>."), patch_rast);
-+ return -1;
-+ }
-+
-+ Rast_get_cellhd(patch_rast, mapset, &patch_region);
-+ Rast_set_window(&patch_region);
-+
-+ if ((fd_patch_rast = Rast_open_old(patch_rast, mapset)) < 0) {
-+ fclose(f_cat_rast);
-+ return -1;
-+ }
-+
-+ ret = get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds);
-+ if(ret == -2) {
-+ G_warning(_("Resolutions of patch <%s> and patched file <%s> are not same."), patch_rast, cat_rast);
-+
-+ Rast_close(fd_patch_rast);
-+ fclose(f_cat_rast);
-+
-+ return -1;
-+ }
-+ else if (ret == -1){
-+
-+ Rast_close(fd_patch_rast);
-+ fclose(f_cat_rast);
-+
-+ return 0;
-+ }
-+
-+ ncols = cat_rast_bounds.east - cat_rast_bounds.west;
-+ nrows = cat_rast_bounds.south - cat_rast_bounds.north;
-+
-+ patch_data = (unsigned char *) G_malloc(ncols * sizeof(unsigned char));
-+
-+ init_shift = head_nchars + cat_rast_region->cols * cat_rast_bounds.north + cat_rast_bounds.west;
-+
-+ if(fseek(f_cat_rast, init_shift, SEEK_SET) != 0) {
-+ G_warning(_("Corrupted category raster conditions file <%s> (fseek failed)"), cat_rast);
-+
-+ Rast_close(fd_patch_rast);
-+ G_free(null_chunk_row);
-+ fclose(f_cat_rast);
-+
-+ return -1;
-+ }
-+
-+ step_shift = cat_rast_region->cols - ncols;
-+
-+ null_chunk_row = Rast_allocate_null_buf();
-+
-+ for(i_row = 0; i_row < nrows; i_row++) {
-+ Rast_get_null_value_row (fd_patch_rast, null_chunk_row, i_row + patch_bounds.north);
-+
-+ for(i_col = 0; i_col < ncols; i_col++) {
-+ patch_col = patch_bounds.west + i_col;
-+
-+ if(null_chunk_row[patch_col] != 1)
-+ patch_data[i_col] = 1 & 255;
-+ else {
-+ patch_data[i_col] = 0 & 255;
-+ }
-+ }
-+
-+ fwrite(patch_data, sizeof(unsigned char), (ncols)/sizeof(unsigned char), f_cat_rast);
-+ if (ferror(f_cat_rast))
-+ {
-+ G_warning(_("Unable to write into category raster conditions file <%s>"), cat_rast);
-+
-+ Rast_close(fd_patch_rast);
-+ G_free(null_chunk_row);
-+ fclose(f_cat_rast);
-+
-+ return -1;
-+ }
-+ if(fseek(f_cat_rast, step_shift, SEEK_CUR) != 0) {
-+ G_warning(_("Corrupted category raster conditions file <%s> (fseek failed)"), cat_rast);
-+
-+ Rast_close(fd_patch_rast);
-+ G_free(null_chunk_row);
-+ fclose(f_cat_rast);
-+
-+ return -1;
-+ }
-+ }
-+
-+ Rast_close(fd_patch_rast);
-+ G_free(null_chunk_row);
-+ fclose(f_cat_rast);
-+ return 0;
-+}
-+
-+/*!
-+ \brief Updates scatter plots data in category by pixels which meets category conditions.
-+
-+ \param bands_rows data represents data describig one row from raster band
-+ \param belongs_pix array which defines which pixels belongs to category (1 value) and which not (0 value)
-+ \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, which are modified according to values in belongs_pix
-+*/
-+static inline void update_cat_scatt_plts(struct rast_row * bands_rows, unsigned short * belongs_pix, struct scScatts * scatts)
-+{
-+ int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunk_rows_pix, max_arr_idx;
-+
-+ CELL * b_1_row;
-+ CELL * b_2_row;
-+ char * b_1_null_row,* b_2_null_row;
-+ struct rast_row b_1_rast_row, b_2_rast_row;
-+
-+ struct Range b_1_range, b_2_range;
-+ int b_1_range_size;
-+
-+ int row_size = Rast_window_cols();
-+
-+ int * scatts_bands = scatts->scatts_bands;
-+
-+ for(i_scatt = 0; i_scatt < scatts->n_a_scatts; i_scatt++)
-+ {
-+ b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
-+ b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
-+
-+ b_1_row = b_1_rast_row.row;
-+ b_2_row = b_2_rast_row.row;
-+
-+ b_1_null_row = b_1_rast_row.null_row;
-+ b_2_null_row = b_2_rast_row.null_row;
-+
-+ b_1_range = b_1_rast_row.rast_range;
-+ b_2_range = b_2_rast_row.rast_range;
-+
-+ b_1_range_size = b_1_range.max - b_1_range.min + 1;
-+ max_arr_idx = (b_1_range.max - b_1_range.min + 1) * (b_2_range.max - b_2_range.min + 1);
-+
-+ for(i_chunk_rows_pix = 0; i_chunk_rows_pix < row_size; i_chunk_rows_pix++)
-+ {
-+ /* pixel does not belongs to scatter plot or has null value in one of the bands */
-+ if(!belongs_pix[i_chunk_rows_pix] ||
-+ b_1_null_row[i_chunk_rows_pix] == 1 ||
-+ b_2_null_row[i_chunk_rows_pix] == 1)
-+ continue;
-+
-+ /* index in scatter plot array */
-+ array_idx = b_1_row[i_chunk_rows_pix] - b_1_range.min + (b_2_row[i_chunk_rows_pix] - b_2_range.min) * b_1_range_size;
-+
-+ if(array_idx < 0 || array_idx >= max_arr_idx) {
-+ G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
-+ continue;
-+ }
-+
-+ /* increment scatter plot value */
-+ ++scatts->scatts_arr[i_scatt]->scatt_vals_arr[array_idx];
-+ }
-+ }
-+}
-+
-+/*!
-+ \brief Computes scatter plots data from chunk_rows.
-+
-+ \param scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
-+ \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are selected areas (condtitions)stored
-+
-+ \param chunk_rows data arrays of chunk_rows from analyzed raster bands (all data in chunk_rows, null_chunk_rows and belongs_pix arrays represents same region)
-+ \param null_chunk_rows null data arrays of chunk_rows from analyzed raster bands
-+ \param row_size size of data in chunk_rows, null_chunk_rows and belongs_pix arrays
-+ \param f_cats_rasts_conds file which stores selected areas (conditions) from mapwindow see I_create_cat_rast() and I_pa
-+ \param fd_cats_rasts array of openedraster maps which represents all selected pixels for category
-+ \param region analysis region
-+
-+ \return 0 on success
-+ \return -1 on failure
-+*/
-+static inline int compute_scatts_from_chunk_row(struct Cell_head *region, struct scCats * scatt_conds,
-+ FILE ** f_cats_rasts_conds, struct rast_row * bands_rows,
-+ struct scCats * scatts, int * fd_cats_rasts)
-+{
-+
-+ int i_rows_pix, i_cat, i_scatt, n_a_scatts, n_pixs;
-+ int cat_id, scatt_plts_cat_idx, array_idx, max_arr_idx;
-+ char * b_1_null_row,* b_2_null_row;
-+ struct rast_row b_1_rast_row, b_2_rast_row;
-+ CELL * cat_rast_row;
-+
-+ struct scScatts * scatts_conds;
-+ struct scScatts * scatts_scatt_plts;
-+ struct scdScattData * conds;
-+
-+ struct Range b_1_range, b_2_range;
-+ int b_1_range_size;
-+
-+ int * scatts_bands;
-+ struct scdScattData ** scatts_arr;
-+
-+ CELL * b_1_row;
-+ CELL * b_2_row;
-+ unsigned char * i_scatt_conds;
-+
-+ int row_size = Rast_window_cols();
-+
-+ unsigned short * belongs_pix = (unsigned short *) G_malloc(row_size * sizeof(unsigned short));
-+ unsigned char * rast_pixs = (unsigned char *) G_malloc(row_size * sizeof(unsigned char));
-+ cat_rast_row = Rast_allocate_c_buf();
-+
-+
-+ for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
-+ {
-+ scatts_conds = scatt_conds->cats_arr[i_cat];
-+
-+ cat_id = scatt_conds->cats_ids[i_cat];
-+
-+ scatt_plts_cat_idx = scatts->cats_idxs[cat_id];
-+ if(scatt_plts_cat_idx < 0)
-+ continue;
-+ scatts_scatt_plts = scatts->cats_arr[scatt_plts_cat_idx];
-+
-+ G_zero(belongs_pix, row_size * sizeof(unsigned short));
-+
-+ /* if category has no conditions defined, scatter plots without any constraint are computed (default scatter plots) */
-+ if(!scatts_conds->n_a_scatts && !f_cats_rasts_conds[i_cat]) {
-+ for(i_scatt = 0; i_scatt < scatts_scatt_plts->n_a_scatts; i_scatt++)
-+ {
-+ /* all pixels belongs */
-+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
-+ belongs_pix[i_rows_pix] = 1;
-+ }
-+ }
-+ /* compute belonging pixels for defined conditions */
-+ else
-+ {
-+ scatts_bands = scatts_conds->scatts_bands;
-+
-+ /* check conditions from category raster condtitions file */
-+ if(f_cats_rasts_conds[i_cat]) {
-+ n_pixs = fread(rast_pixs, sizeof(unsigned char), (row_size)/sizeof(unsigned char), f_cats_rasts_conds[i_cat]);
-+
-+ if (ferror(f_cats_rasts_conds[i_cat]))
-+ {
-+ G_free(rast_pixs);
-+ G_free(belongs_pix);
-+ G_warning(_("Unable to read from category raster condtition file."));
-+ return -1;
-+ }
-+ if (n_pixs != n_pixs) {
-+ G_free(rast_pixs);
-+ G_free(belongs_pix);
-+ G_warning(_("Invalid size of category raster conditions file."));
-+ return -1;
-+
-+ }
-+
-+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
-+ {
-+ if(rast_pixs[i_rows_pix] != 0 & 255)
-+ belongs_pix[i_rows_pix] = 1;
-+ }
-+ }
-+
-+ /* check condtions defined in scatter plots*/
-+ for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
-+ {
-+ b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
-+ b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
-+
-+ b_1_row = b_1_rast_row.row;
-+ b_2_row = b_2_rast_row.row;
-+
-+ b_1_null_row = b_1_rast_row.null_row;
-+ b_2_null_row = b_2_rast_row.null_row;
-+
-+ b_1_range = b_1_rast_row.rast_range;
-+ b_2_range = b_2_rast_row.rast_range;
-+
-+ b_1_range_size = b_1_range.max - b_1_range.min + 1;
-+ max_arr_idx = (b_1_range.max - b_1_range.min + 1) * (b_2_range.max - b_2_range.min + 1);
-+
-+ i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
-+
-+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
-+ {
-+ /* pixels already belongs to category from category raster conditions file or contains null value in one of the bands */
-+ if(belongs_pix[i_rows_pix] ||
-+ b_1_null_row[i_rows_pix] == 1 ||
-+ b_2_null_row[i_rows_pix] == 1)
-+ continue;
-+
-+ array_idx = b_1_row[i_rows_pix] - b_1_range.min + (b_2_row[i_rows_pix] - b_2_range.min) * b_1_range_size;
-+ if(array_idx < 0 || array_idx >= max_arr_idx) {
-+ G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
-+ continue;
-+ }
-+ /* pixels meets condtion defined in scatter plot */
-+ if(i_scatt_conds[array_idx])
-+ belongs_pix[i_rows_pix] = 1;
-+ }
-+ }
-+ }
-+
-+ /* update category raster with belonging pixels */
-+ if(fd_cats_rasts[i_cat] >= 0) {
-+ Rast_set_null_value(cat_rast_row, Rast_window_cols(), CELL_TYPE);
-+
-+ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
-+ if(belongs_pix[i_rows_pix])
-+ cat_rast_row[i_rows_pix] = belongs_pix[i_rows_pix];
-+
-+ Rast_put_c_row (fd_cats_rasts[i_cat], cat_rast_row);
-+ }
-+
-+ /* update scatter plots with belonging pixels */
-+ update_cat_scatt_plts(bands_rows, belongs_pix, scatts_scatt_plts);
-+ }
-+
-+ G_free(cat_rast_row);
-+ G_free(rast_pixs);
-+ G_free(belongs_pix);
-+
-+ return 0;
-+}
-+
-+/*!
-+ \brief Get list if bands needed to be opened for analysis from scCats struct.
-+*/
-+static void get_needed_bands(struct scCats * cats, int * b_needed_bands)
-+{
-+ // results in b_needed_bands - array of bools - if item has value 1, band (defined by item index) is needed to be opened
-+ int i_cat, i_scatt, cat_id;
-+
-+ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
-+ {
-+ for(i_scatt = 0; i_scatt < cats->cats_arr[i_cat]->n_a_scatts; i_scatt++)
-+ {
-+ G_debug(3, "Active scatt %d in catt %d", i_scatt, i_cat);
-+
-+ b_needed_bands[cats->cats_arr[i_cat]->scatts_bands[i_scatt * 2]] = 1;
-+ b_needed_bands[cats->cats_arr[i_cat]->scatts_bands[i_scatt * 2 + 1]] = 1;
-+ }
-+ }
-+ return;
-+}
-+
-+/*!
-+ \brief Helper function for clean up.
-+*/
-+static void free_compute_scatts_data(int * fd_bands, struct rast_row * bands_rows, int n_a_bands, int * bands_ids,
-+ int * fd_cats_rasts, FILE ** f_cats_rasts_conds, int n_a_cats)
-+{
-+ int i, band_id;
-+
-+ for(i = 0; i < n_a_bands; i++)
-+ {
-+ band_id = bands_ids[i];
-+ if(band_id >= 0) {
-+ Rast_close(fd_bands[i]);
-+ G_free(bands_rows[band_id].row);
-+ G_free(bands_rows[band_id].null_row);
-+ }
-+ }
-+
-+ if(f_cats_rasts_conds)
-+ for(i = 0; i < n_a_cats; i++)
-+ if(f_cats_rasts_conds[i])
-+ fclose(f_cats_rasts_conds[i]);
-+
-+ if(fd_cats_rasts)
-+ for(i = 0; i < n_a_cats; i++)
-+ if(fd_cats_rasts[i] >= 0)
-+ Rast_close(fd_cats_rasts[i]);
-+
-+}
-+
-+/*!
-+ \brief Compute scatter plots data.
-+
-+ If category has not defined no category raster condition file and no scatter plot with consdtion,
-+ default scatter plot is computed.
-+ Warning: calls Rast_set_window
-+
-+ \param region analysis region, beaware that all input data must be prepared for this region (bands (their ranges), cats_rasts_conds rasters...)
-+ \param region function calls Rast_set_window for this region
-+ \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are stored selected areas (conditions) in scatter plots
-+ \param cats_rasts_conds paths to category raster conditions files representing selected areas (conditions) in rasters for every category
-+ \param cats_rasts_conds index in array represents corresponding category id
-+ \param cats_rasts_conds for manupulation with category raster conditions file see also I_id_scatt_to_bands() and I_insert_patch_to_cat_rast()
-+ \param bands names of analyzed bands, order of bands is defined by their id
-+ \param n_bands number of bands
-+ \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
-+ \param [out] cats_rasts array of raster maps names where will be stored all selected pixels for every category
-+
-+ \return 0 on success
-+ \return -1 on failure
-+*/
-+int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** cats_rasts_conds,
-+ const char ** bands, int n_bands, struct scCats * scatts, const char ** cats_rasts)
-+{
-+ const char *mapset;
-+ char header[1024];
-+
-+ int fd_cats_rasts[scatt_conds->n_a_cats];
-+ FILE * f_cats_rasts_conds[scatt_conds->n_a_cats];
-+
-+ struct rast_row bands_rows[n_bands];
-+
-+ RASTER_MAP_TYPE data_type;
-+
-+ int nrows, i_band, n_a_bands, band_id,
-+ i, i_row, head_nchars, i_cat, id_cat;
-+
-+ int fd_bands[n_bands];
-+ int bands_ids[n_bands];
-+ int b_needed_bands[n_bands];
-+
-+ Rast_set_window(region);
-+
-+ for(i_band = 0; i_band < n_bands; i_band++)
-+ fd_bands[i_band] = -1;
-+
-+ for(i_band = 0; i_band < n_bands; i_band++)
-+ bands_ids[i_band] = -1;
-+
-+ if (n_bands != scatts->n_bands ||
-+ n_bands != scatt_conds->n_bands)
-+ return -1;
-+
-+ memset(b_needed_bands, 0, (size_t)n_bands * sizeof(int));
-+
-+ get_needed_bands(scatt_conds, &b_needed_bands[0]);
-+ get_needed_bands(scatts, &b_needed_bands[0]);
-+
-+ n_a_bands = 0;
-+
-+ /* open band rasters, which are needed for computation */
-+ for(band_id = 0; band_id < n_bands; band_id++)
-+ {
-+ if(b_needed_bands[band_id])
-+ {
-+ G_debug(3, "Opening raster no. %d with name: %s", band_id, bands[band_id]);
-+
-+ if ((mapset = G_find_raster2(bands[band_id],"")) == NULL) {
-+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
-+ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
-+ G_warning(_("Unbale to read find raster <%s>"), bands[band_id]);
-+ return -1;
-+ }
-+
-+ if ((fd_bands[n_a_bands] = Rast_open_old(bands[band_id], mapset)) < 0) {
-+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
-+ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
-+ G_warning(_("Unbale to open raster <%s>"), bands[band_id]);
-+ return -1;
-+ }
-+
-+ data_type = Rast_get_map_type(fd_bands[n_a_bands]);
-+ if(data_type != CELL_TYPE) {
-+ G_warning(_("Raster <%s> type is not <%s>"), bands[band_id], "CELL");
-+ return -1;
-+ }
-+
-+ bands_rows[band_id].row = Rast_allocate_c_buf();
-+ bands_rows[band_id].null_row = Rast_allocate_null_buf();
-+
-+ if(Rast_read_range(bands[band_id], mapset, &bands_rows[band_id].rast_range) != 1){
-+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
-+ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
-+ G_warning(_("Unable to read range of raster <%s>"), bands[band_id]);
-+ return -1;
-+ }
-+
-+ bands_ids[n_a_bands] = band_id;
-+ ++n_a_bands;
-+ }
-+ }
-+
-+ /* open category rasters condition files and category rasters */
-+ for(i_cat = 0; i_cat < scatts->n_a_cats; i_cat++)
-+ {
-+ id_cat = scatts->cats_ids[i_cat];
-+ if(cats_rasts[id_cat]) {
-+ fd_cats_rasts[i_cat] = Rast_open_new(cats_rasts[id_cat], CELL_TYPE);
-+ }
-+ else
-+ fd_cats_rasts[i_cat] = -1;
-+
-+ if(cats_rasts_conds[id_cat]) {
-+ f_cats_rasts_conds[i_cat] = fopen(cats_rasts_conds[id_cat], "r");
-+ if(!f_cats_rasts_conds[i_cat]) {
-+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids,
-+ f_cats_rasts_conds, f_cats_rasts_conds, scatt_conds->n_a_cats);
-+ G_warning(_("Unable to open category raster condtition file <%s>"), bands[band_id]);
-+ return -1;
-+ }
-+ }
-+ else
-+ f_cats_rasts_conds[i_cat] = NULL;
-+ }
-+
-+ head_nchars = get_cat_rast_header(region, header);
-+ for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
-+ if(f_cats_rasts_conds[i_cat])
-+ if( fseek(f_cats_rasts_conds[i_cat] , head_nchars, SEEK_SET) != 0) {
-+ G_warning(_("Corrupted category raster conditions file (fseek failed)"));
-+ return -1;
-+ }
-+
-+ nrows = Rast_window_rows();
-+
-+ /* analyze bands by rows */
-+ for (i_row = 0; i_row < nrows; i_row++)
-+ {
-+ for(i_band = 0; i_band < n_a_bands; i_band++)
-+ {
-+ band_id = bands_ids[i_band];
-+ Rast_get_c_row(fd_bands[i_band], bands_rows[band_id].row, i_row);
-+ Rast_get_null_value_row (fd_bands[i_band], bands_rows[band_id].null_row, i_row);
-+ }
-+ if(compute_scatts_from_chunk_row(region, scatt_conds, f_cats_rasts_conds, bands_rows, scatts, fd_cats_rasts) == -1)
-+ {
-+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids, fd_cats_rasts,
-+ f_cats_rasts_conds, scatt_conds->n_a_cats);
-+ return -1;
-+ }
-+
-+ }
-+ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids,
-+ fd_cats_rasts, f_cats_rasts_conds, scatt_conds->n_a_cats);
-+ return 0;
-+}
-+
-+/*!
-+ \brief Merge arrays according to opacity.
-+ Every pixel in array must be represented by 4 values (RGBA).
-+
-+
-+ \param merged_arr array which will be overlayd with overlay_arr
-+ \param overlay_arr array to be merged_arr overlayed with
-+ \param rows number of rows for the both arrays
-+ \param cols number of columns for the both arrays
-+ \param alpha transparency of the overlay array for merging
-+
-+ \return 0
-+*/
-+int I_merge_arrays(unsigned char * merged_arr, unsigned char * overlay_arr, unsigned rows, unsigned cols, double alpha)
-+{
-+ unsigned int i_row, i_col, i_b;
-+ unsigned int row_idx, col_idx, idx;
-+ unsigned int c_a_i, c_a;
-+
-+ for(i_row = 0; i_row < rows; i_row++)
-+ {
-+ row_idx = i_row * cols;
-+ for(i_col = 0; i_col < cols; i_col++)
-+ {
-+ col_idx = 4 * (row_idx + i_col);
-+ idx = col_idx + 3;
-+
-+ c_a = overlay_arr[idx] * alpha;
-+ c_a_i = 255 - c_a;
-+
-+ merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * 255) / 255;
-+
-+ for(i_b = 0; i_b < 3; i_b++)
-+ {
-+ idx = col_idx + i_b;
-+ merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * (int)overlay_arr[idx]) / 255;
-+ }
-+ }
-+ }
-+ return 0;
-+}
-+
-+
-+int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned vals_size, unsigned char * colmap, unsigned char * col_vals){
-+ unsigned int i_val;
-+ int v, i, i_cm;
-+
-+ for(i_val = 0; i_val < vals_size; i_val++){
-+ i_cm = 4 * i_val;
-+
-+ v = vals[i_val];
-+
-+ if(vals_mask && vals_mask[i_val])
-+ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[258 * 4 + i];
-+ else if(v > 255)
-+ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[257 * 4 + i];
-+ else if(v < 0)
-+ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[256 * 4 + i];
-+ else
-+ for(i = 0; i < 4; i++){ col_vals[i_cm + i] = colmap[v * 4 + i];
-+ }
-+ }
-+ return 0;
-+}
-+
-+
-+int I_rasterize(double * polygon, int pol_n_pts, unsigned char * rast, unsigned char val, struct Cell_head *rast_region){
-+ int i;
-+ int x0, x1, y;
-+ int row, row_idx, i_col;
-+
-+ IClass_perimeter perimeter;
-+
-+ struct line_pnts *pol;
-+ pol = Vect_new_line_struct();
-+
-+ for(i = 0; i < pol_n_pts; i++) {
-+ Vect_append_point(pol,
-+ polygon[i*2],
-+ polygon[i*2 + 1],
-+ 0.0);
-+ }
-+
-+ Rast_set_window(rast_region);
-+
-+ make_perimeter(pol, &perimeter, rast_region);
-+ for (i = 1; i < perimeter.npoints; i += 2) {
-+ y = perimeter.points[i].y;
-+ if (y != perimeter.points[i - 1].y) {
-+ G_warning(_("prepare_signature: scan line %d has odd number of points."),
-+ (i + 1) / 2);
-+ return 1;
-+ }
-+
-+ x0 = perimeter.points[i - 1].x;
-+ x1 = perimeter.points[i].x;
-+
-+ if (x0 > x1) {
-+ G_warning(_("signature: perimeter points out of order."));
-+ return 1;
-+ }
-+
-+ row = (rast_region->rows - y);
-+ if(row < 0 || row >= rast_region->rows) {
-+ continue;
-+ }
-+
-+ row_idx = rast_region->cols * row;
-+
-+ for(i_col = x0; i_col <= x1; i_col++) {
-+ if(i_col < 0 || i_col >= rast_region->cols) {
-+ continue;
-+ }
-+ rast[row_idx + i_col] = val;
-+ }
-+ }
-+
-+ Vect_destroy_line_struct(pol);
-+ G_free(perimeter.points);
-+ return 0;
-+}
-Index: include/defs/vedit.h
-===================================================================
---- include/defs/vedit.h (revision 57728)
-+++ include/defs/vedit.h (working copy)
-@@ -33,6 +33,8 @@
- int Vedit_merge_lines(struct Map_info *, struct ilist *);
-
- /* move.c */
-+int Vedit_move_areas(struct Map_info *, struct Map_info **, int,
-+ struct ilist *, double, double, double, int, double);
- int Vedit_move_lines(struct Map_info *, struct Map_info **, int,
- struct ilist *, double, double, double, int, double);
-
Index: include/defs/imagery.h
===================================================================
---- include/defs/imagery.h (revision 57728)
+--- include/defs/imagery.h (revision 57734)
+++ include/defs/imagery.h (working copy)
@@ -111,6 +111,28 @@
FILE *I_fopen_subgroup_ref_new(const char *, const char *);
@@ -1330,9 +31,22 @@
/* sig.c */
int I_init_signatures(struct Signature *, int);
int I_new_signature(struct Signature *);
+Index: include/defs/vedit.h
+===================================================================
+--- include/defs/vedit.h (revision 57734)
++++ include/defs/vedit.h (working copy)
+@@ -33,6 +33,8 @@
+ int Vedit_merge_lines(struct Map_info *, struct ilist *);
+
+ /* move.c */
++int Vedit_move_areas(struct Map_info *, struct Map_info **, int,
++ struct ilist *, double, double, double, int, double);
+ int Vedit_move_lines(struct Map_info *, struct Map_info **, int,
+ struct ilist *, double, double, double, int, double);
+
Index: include/imagery.h
===================================================================
---- include/imagery.h (revision 57728)
+--- include/imagery.h (revision 57734)
+++ include/imagery.h (working copy)
@@ -135,6 +135,56 @@
@@ -1391,575 +105,88 @@
#define SIGNATURE_TYPE_MIXED 1
#define GROUPFILE "CURGROUP"
-Index: gui/icons/grass/polygon.png
+Index: gui/wxpython/vnet/toolbars.py
===================================================================
-Cannot display: file marked as a binary type.
-svn:mime-type = application/octet-stream
-Index: gui/icons/grass/polygon.png
-===================================================================
---- gui/icons/grass/polygon.png (revision 57728)
-+++ gui/icons/grass/polygon.png (working copy)
-
-Property changes on: gui/icons/grass/polygon.png
-___________________________________________________________________
-Added: svn:mime-type
-## -0,0 +1 ##
-+application/octet-stream
-\ No newline at end of property
-Index: gui/wxpython/vdigit/wxdigit.py
-===================================================================
---- gui/wxpython/vdigit/wxdigit.py (revision 57728)
-+++ gui/wxpython/vdigit/wxdigit.py (working copy)
-@@ -17,7 +17,7 @@
- (and NumPy would be an excellent candidate for acceleration via
- e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
+--- gui/wxpython/vnet/toolbars.py (revision 57734)
++++ gui/wxpython/vnet/toolbars.py (working copy)
+@@ -19,9 +19,9 @@
--(C) 2007-2011 by the GRASS Development Team
-+(C) 2007-2011, 2013 by the GRASS Development Team
+ import wx
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
-@@ -27,6 +27,8 @@
+-from icon import MetaIcon
++from icons.icon import MetaIcon
+ from gui_core.toolbars import BaseToolbar, BaseIcons
+-from core.gcmd import RunCommand
++from core.gcmd import RunCommand
+ from core.utils import _
- import grass.script.core as grass
+ class PointListToolbar(BaseToolbar):
+Index: gui/wxpython/vnet/dialogs.py
+===================================================================
+--- gui/wxpython/vnet/dialogs.py (revision 57734)
++++ gui/wxpython/vnet/dialogs.py (working copy)
+@@ -1218,7 +1218,7 @@
+ pos = (row, 1))
-+from grass.pydispatch.signal import Signal
-+
- from core.gcmd import GError
- from core.debug import Debug
- from core.settings import UserSettings
-@@ -176,7 +178,21 @@
-
- if self.poMapInfo:
- self.InitCats()
--
-+
-+ self.emit_signals = False
-+
-+ # signals which describes features changes during digitization,
-+ # activate them using EmitSignals method
-+ #TODO signal for errors?
-+ self.featureAdded = Signal('IVDigit.featureAdded')
-+ self.areasDeleted = Signal('IVDigit.areasDeleted')
-+ self.vertexMoved = Signal('IVDigit.vertexMoved')
-+ self.vertexAdded = Signal('IVDigit.vertexAdded')
-+ self.vertexRemoved = Signal('IVDigit.vertexRemoved')
-+ self.featuresDeleted = Signal('IVDigit.featuresDeleted')
-+ self.featuresMoved = Signal('IVDigit.featuresMoved')
-+ self.lineEdited = Signal('IVDigit.lineEdited')
-+
- def __del__(self):
- Debug.msg(1, "IVDigit.__del__()")
- Vect_destroy_line_struct(self.poPoints)
-@@ -188,7 +204,12 @@
- Vect_close(self.poBgMapInfo)
- self.poBgMapInfo = self.popoBgMapInfo = None
- del self.bgMapInfo
--
-+
-+ def EmitSignals(self, emit):
-+ """!Activate/deactivate signals which describes features changes during digitization.
-+ """
-+ self.emit_signals = emit
-+
- def CloseBackgroundMap(self):
- """!Close background vector map"""
- if not self.poBgMapInfo:
-@@ -394,7 +415,6 @@
-
- @return tuple (number of added features, feature ids)
- """
--
- layer = self._getNewFeaturesLayer()
- cat = self._getNewFeaturesCat()
-
-@@ -419,10 +439,14 @@
- return (-1, None)
-
- self.toolbar.EnableUndo()
--
-- return self._addFeature(vtype, points, layer, cat,
-- self._getSnapMode(), self._display.GetThreshold())
--
-+
-+ ret = self._addFeature(vtype, points, layer, cat,
-+ self._getSnapMode(), self._display.GetThreshold())
-+ if ret[0] > -1 and self.emit_signals:
-+ self.featureAdded.emit(new_bboxs = [self._createBbox(points)], new_areas_cats = [[{layer : [cat]}, None]])
-+
-+ return ret
-+
- def DeleteSelectedLines(self):
- """!Delete selected features
+ row += 1
+- gridSizer.Add(item = self.settings["invert_colors"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
++ gridSizer.Add(item=self.settings["invert_colors"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
-@@ -434,16 +458,27 @@
- # collect categories for deleting if requested
- deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
- catDict = dict()
-+
-+ old_bboxs = []
-+ old_areas_cats = []
- if deleteRec:
- for i in self._display.selected['ids']:
-+
- if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
- self._error.ReadLine(i)
-
-- cats = self.poCats.contents
-- for j in range(cats.n_cats):
-- if cats.field[j] not in catDict.keys():
-- catDict[cats.field[j]] = list()
-- catDict[cats.field[j]].append(cats.cat[j])
-+ if self.emit_signals:
-+ ret = self._getLineAreaBboxCats(i)
-+ if ret:
-+ old_bboxs += ret[0]
-+ old_areas_cats += ret[1]
-+
-+ # catDict was not used -> put into comment
-+ #cats = self.poCats.contents
-+ #for j in range(cats.n_cats):
-+ # if cats.field[j] not in catDict.keys():
-+ # catDict[cats.field[j]] = list()
-+ # catDict[cats.field[j]].append(cats.cat[j])
-
- poList = self._display.GetSelectedIList()
- nlines = Vedit_delete_lines(self.poMapInfo, poList)
-@@ -456,7 +491,10 @@
- self._deleteRecords(catDict)
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.featuresDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
-+
- return nlines
-
- def _deleteRecords(self, cats):
-@@ -512,22 +550,173 @@
+ gridSizer.AddGrowableCol(1)
+ styleBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
+Index: gui/wxpython/Makefile
+===================================================================
+--- gui/wxpython/Makefile (revision 57734)
++++ gui/wxpython/Makefile (working copy)
+@@ -13,7 +13,7 @@
+ $(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
+ gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapwin/*.py mapdisp/*.py \
+ mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* timeline/* vdigit/* \
+- vnet/*.py web_services/*.py wxplot/*.py) \
++ vnet/*.py web_services/*.py wxplot/*.py scatt_plot/*.py) \
+ gis_set.py gis_set_error.py wxgui.py README
- @return number of deleted
- """
-+ if len(self._display.selected['ids']) < 1:
-+ return 0
-+
- poList = self._display.GetSelectedIList()
- cList = poList.contents
-
- nareas = 0
-+ old_bboxs = []
-+ old_areas_cats = []
-+
- for i in range(cList.n_values):
-+
- if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
- continue
--
-+
-+ if self.emit_signals:
-+ area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
-+ if area > 0:
-+ bbox, cats = self._getaAreaBboxCats(area)
-+ old_bboxs += bbox
-+ old_areas_cats += cats
-+
- nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
-
- if nareas > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
-+ if self.emit_signals:
-+ self.areasDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
-+
-+ return nareas
-+
-+ def _getLineAreaBboxCats(self, ln_id):
-+ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+
-+ if ltype == GV_CENTROID:
-+ #TODO centroid opttimization, can be adited also its area -> it will appear two times in new_ lists
-+ return self._getCentroidAreaBboxCats(ln_id)
-+ else:
-+ return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
-+
-+
-+ def _getCentroidAreaBboxCats(self, centroid):
-+ if not Vect_line_alive(self.poMapInfo, centroid):
-+ return None
-+
-+ area = Vect_get_centroid_area(self.poMapInfo, centroid)
-+ if area > 0:
-+ return self._getaAreaBboxCats(area)
-+ else:
-+ return None
-+
-+ def _getaAreaBboxCats(self, area):
-+
-+ po_b_list = Vect_new_list()
-+ Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
-+ b_list = po_b_list.contents
-+
-+ geoms = []
-+ areas_cats = []
-+
-+ if b_list.n_values > 0:
-+ for i_line in range(b_list.n_values):
-+
-+ line = b_list.value[i_line];
-+
-+ geoms.append(self._getBbox(abs(line)))
-+ areas_cats.append(self._getLineAreasCategories(abs(line)))
-
-- return nareas
-+ Vect_destroy_list(po_b_list);
-+
-+ return geoms, areas_cats
-+
-+ def _getLineAreasCategories(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return []
-+
-+ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
-+ if ltype != GV_BOUNDARY:
-+ return []
-+
-+ cats = [None, None]
-+
-+ left = c_int()
-+ right = c_int()
-+
-+ if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
-+ areas = [left.value, right.value]
-+
-+ for i, a in enumerate(areas):
-+ if a > 0:
-+ centroid = Vect_get_area_centroid(self.poMapInfo, a)
-+ if centroid <= 0:
-+ continue
-+ c = self._getCategories(centroid)
-+ if c:
-+ cats[i] = c
-+
-+ return cats
-+
-+ def _getCategories(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return none
-+
-+ poCats = Vect_new_cats_struct()
-+ if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
-+ Vect_destroy_cats_struct(poCats)
-+ return None
-+
-+ cCats = poCats.contents
-+
-+ cats = {}
-+ for j in range(cCats.n_cats):
-+ if cats.has_key(cCats.field[j]):
-+ cats[cCats.field[j]].append(cCats.cat[j])
-+ else:
-+ cats[cCats.field[j]] = [cCats.cat[j]]
-
-+ Vect_destroy_cats_struct(poCats)
-+ return cats
-+
-+ def _getBbox(self, ln_id):
-+ if not Vect_line_alive (self.poMapInfo, ln_id):
-+ return None
-+
-+ poPoints = Vect_new_line_struct()
-+ if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
-+ Vect_destroy_line_struct(poPoints)
-+ return []
-+
-+ geom = self._convertGeom(poPoints)
-+ bbox = self._createBbox(geom)
-+ Vect_destroy_line_struct(poPoints)
-+ return bbox
-+
-+ def _createBbox(self, points):
-+
-+ bbox = {}
-+ for pt in points:
-+ if not bbox.has_key('maxy'):
-+ bbox['maxy'] = pt[1]
-+ bbox['miny'] = pt[1]
-+ bbox['maxx'] = pt[0]
-+ bbox['minx'] = pt[0]
-+ continue
-+
-+ if bbox['maxy'] < pt[1]:
-+ bbox['maxy'] = pt[1]
-+ elif bbox['miny'] > pt[1]:
-+ bbox['miny'] = pt[1]
-+
-+ if bbox['maxx'] < pt[0]:
-+ bbox['maxx'] = pt[0]
-+ elif bbox['minx'] > pt[0]:
-+ bbox['minx'] = pt[0]
-+ return bbox
-+
-+ def _convertGeom(self, poPoints):
-+
-+ Points = poPoints.contents
-+
-+ pts_geom = []
-+ for j in range(Points.n_points):
-+ pts_geom.append((Points.x[j], Points.y[j]))
-+
-+ return pts_geom
-+
- def MoveSelectedLines(self, move):
- """!Move selected features
+ DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) \
+@@ -21,8 +21,9 @@
-@@ -536,16 +725,45 @@
- if not self._checkMap():
- return -1
-
-+ nsel = len(self._display.selected['ids'])
-+ if nsel < 1:
-+ return -1
-+
- thresh = self._display.GetThreshold()
- snap = self._getSnapMode()
-
- poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ old_bboxs = []
-+ old_areas_cats = []
-+ for sel_id in self._display.selected['ids']:
-+ ret = self._getLineAreaBboxCats(sel_id)
-+ if ret:
-+ old_bboxs += ret[0]
-+ old_areas_cats += ret[1]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
- poList,
- move[0], move[1], 0,
- snap, thresh)
-+
- Vect_destroy_list(poList)
--
-+
-+ if nlines > 0 and self.emit_signals:
-+ new_bboxs = []
-+ new_areas_cats = []
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ ret = self._getLineAreaBboxCats(new_id)
-+ if ret:
-+ new_bboxs += ret[0]
-+ new_areas_cats += ret[1]
-+
- if nlines > 0 and self._settings['breakLines']:
- for i in range(1, nlines):
- self._breakLineAtIntersection(nlines + i, None, changeset)
-@@ -553,7 +771,13 @@
- if nlines > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.featuresMoved.emit(new_bboxs = new_bboxs,
-+ old_bboxs = old_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_areas_cats = new_areas_cats)
-+
- return nlines
+ PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
+ gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
+- mapswipe vdigit wxplot web_services rlisetup vnet timeline)
++ mapswipe vdigit wxplot web_services rlisetup vnet timeline scatt_plot)
- def MoveSelectedVertex(self, point, move):
-@@ -571,12 +795,21 @@
-
- if len(self._display.selected['ids']) != 1:
- return -1
--
+
-+ # move only first found vertex in bbox
-+ poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ cList = poList.contents
-+ old_bboxs = [self._getBbox(cList.value[0])]
-+ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- Vect_reset_line(self.poPoints)
- Vect_append_point(self.poPoints, point[0], point[1], 0.0)
--
-- # move only first found vertex in bbox
-- poList = self._display.GetSelectedIList()
-+
- moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
- poList, self.poPoints,
- self._display.GetThreshold(type = 'selectThresh'),
-@@ -584,7 +817,17 @@
- move[0], move[1], 0.0,
- 1, self._getSnapMode())
- Vect_destroy_list(poList)
--
-+
-+ if moved > 0 and self.emit_signals:
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+
-+ new_bboxs = []
-+ new_areas_cats = []
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ new_bboxs.append(self._getBbox(new_id))
-+ new_areas_cats.append(self._getLineAreasCategories(new_id))
-+
- if moved > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
- None)
-@@ -592,7 +835,13 @@
- if moved > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.vertexMoved.emit(new_bboxs = new_bboxs,
-+ new_areas_cats = new_areas_cats,
-+ old_areas_cats = old_areas_cats,
-+ old_bboxs = old_bboxs)
-+
- return moved
+ DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
- def AddVertex(self, coords):
-@@ -681,6 +930,10 @@
- self._error.ReadLine(line)
- return -1
-
-+ if self.emit_signals:
-+ old_bboxs = [self._getBbox(line)]
-+ old_areas_cats = [self._getLineAreasCategories(line)]
-+
- # build feature geometry
- Vect_reset_line(self.poPoints)
- for p in coords:
-@@ -696,6 +949,9 @@
-
- newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
- self.poPoints, self.poCats)
-+ if newline > 0 and self.emit_signals:
-+ new_geom = [self._getBbox(newline)]
-+ new_areas_cats = [self._getLineAreasCategories(newline)]
-
- if newline > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(newline, None)
-@@ -703,7 +959,13 @@
- if newline > 0:
- self._addChangeset()
- self.toolbar.EnableUndo()
--
-+
-+ if self.emit_signals:
-+ self.lineEdited.emit(old_bboxs = old_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_bboxs = new_bboxs,
-+ new_areas_cats = new_areas_cats)
-+
- return newline
-
- def FlipLine(self):
-@@ -1514,6 +1776,16 @@
- return 0
-
- poList = self._display.GetSelectedIList()
-+
-+ if self.emit_signals:
-+ cList = poList.contents
-+
-+ old_bboxs = [self._getBbox(cList.value[0])]
-+ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
-+
-+ Vect_set_updated(self.poMapInfo, 1)
-+ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
-+
- Vect_reset_line(self.poPoints)
- Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
-
-@@ -1525,15 +1797,35 @@
- else:
- ret = Vedit_remove_vertex(self.poMapInfo, poList,
- self.poPoints, thresh)
-+
- Vect_destroy_list(poList)
-+
-+ if ret > 0 and self.emit_signals:
-+ new_bboxs = []
-+ new_areas_cats = []
-+
-+ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
-+ for i in range(n_up_lines_old, n_up_lines):
-+ new_id = Vect_get_updated_line(self.poMapInfo, i)
-+ new_areas_cats.append(self._getLineAreasCategories(new_id))
-+ new_bboxs.append(self._getBbox(new_id))
-
- if not add and ret > 0 and self._settings['breakLines']:
- self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
- None)
--
-+
- if ret > 0:
- self._addChangeset()
--
-+
-+ if ret > 0 and self.emit_signals:
-+ if add:
-+ self.vertexAdded.emit(old_bboxs = old_bboxs, new_bboxs = new_bboxs)
-+ else:
-+ self.vertexRemoved.emit(old_bboxs = old_bboxs,
-+ new_bboxs = new_bboxs,
-+ old_areas_cats = old_areas_cats,
-+ new_areas_cats = new_areas_cats)
-+
- return 1
-
- def GetLineCats(self, line):
-Index: gui/wxpython/vdigit/toolbars.py
+ default: $(DSTFILES)
+Index: gui/wxpython/iclass/dialogs.py
===================================================================
---- gui/wxpython/vdigit/toolbars.py (revision 57728)
-+++ gui/wxpython/vdigit/toolbars.py (working copy)
-@@ -17,6 +17,7 @@
- import wx
+--- gui/wxpython/iclass/dialogs.py (revision 57734)
++++ gui/wxpython/iclass/dialogs.py (working copy)
+@@ -403,8 +403,23 @@
+ except UnicodeEncodeError:
+ GMessage(parent = self, message = _("Please use only ASCII characters."))
+ return
++ if attr == 'color':
++ try:
++ colors = map(int, text.split(":"))
++ except:
++ return
- from grass.script import core as grass
-+from grass.pydispatch.signal import Signal
++ if len(colors) != 3:
++ return
- from gui_core.toolbars import BaseToolbar, BaseIcons
- from gui_core.dialogs import CreateNewVector
-@@ -42,6 +43,8 @@
- self.digit = None
- self._giface = giface
-
-+ self.editingStarted = Signal("VDigitToolbar.editingStarted")
++ for i, c in enumerate(colors):
++ if c < 0:
++ colors[i] = 0
++ elif c > 255:
++ colors[i] = 255
+
- # currently selected map layer for editing (reference to MapLayer instance)
- self.mapLayer = None
- # list of vector layers from Layer Manager (only in the current mapset)
-@@ -860,6 +863,7 @@
- alpha = int(opacity * 255)
- self.digit.GetDisplay().UpdateSettings(alpha = alpha)
++ text = ":".join(map(str, colors))
++
+ cat = self.stats_data.GetCategories()[row]
+ self.stats_data.GetStatistics(cat).SetStatistics(stats = {attr : text})
-+ self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
- return True
-
- def StopEditing(self):
-Index: gui/wxpython/iclass/dialogs.py
-===================================================================
---- gui/wxpython/iclass/dialogs.py (revision 57728)
-+++ gui/wxpython/iclass/dialogs.py (working copy)
-@@ -469,13 +469,19 @@
+@@ -469,13 +484,19 @@
toolbar.SetCategories(catNames = catNames, catIdx = cats)
if name in catNames:
toolbar.choice.SetStringSelection(name)
@@ -1980,9 +207,39 @@
# don't forget to update maps, histo, ...
def GetSelectedIndices(self, state = wx.LIST_STATE_SELECTED):
+@@ -560,6 +581,29 @@
+ def OnGetItemAttr(self, item):
+ return None
+
++ def OnGetItemAttr(self, item):
++ back_c = wx.Colour(*map(int, self.OnGetItemText(item, 1).split(':')))
++ text_c = wx.Colour(*ContrastColor(back_c))
++
++ # if it is in scope of the method, gui falls, using self solved it
++ self.l = wx.ListItemAttr(colText = text_c, colBack = back_c)
++ return self.l
++
++def ContrastColor(color):
++ """!Decides which value shoud have text to be contrast with backgroud color (bright bg -> black, dark bg -> white)
++
++ @todo could be useful by other apps, consider to move it to gui_core
++ """
++ #gacek, http://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color
++ a = 1 - ( 0.299 * color[0] + 0.587 * color[1] + 0.114 * color[2])/255;
++
++ if a < 0.5:
++ d = 0
++ else:
++ d = 255
++
++ return (d, d, d)
++
+ class IClassSignatureFileDialog(wx.Dialog):
+ def __init__(self, parent, group, subgroup,
+ file = None, title = _("Save signature file"), id = wx.ID_ANY,
Index: gui/wxpython/iclass/toolbars.py
===================================================================
---- gui/wxpython/iclass/toolbars.py (revision 57728)
+--- gui/wxpython/iclass/toolbars.py (revision 57734)
+++ gui/wxpython/iclass/toolbars.py (working copy)
@@ -46,9 +46,7 @@
'importAreas' : MetaIcon(img = 'layer-import',
@@ -2009,7 +266,7 @@
"""!IClass toolbar
Index: gui/wxpython/iclass/frame.py
===================================================================
---- gui/wxpython/iclass/frame.py (revision 57728)
+--- gui/wxpython/iclass/frame.py (revision 57734)
+++ gui/wxpython/iclass/frame.py (working copy)
@@ -64,6 +64,8 @@
IClassExportAreasDialog, IClassMapDialog
@@ -2231,7 +488,7 @@
import core.render as render
Index: gui/wxpython/iclass/plots.py
===================================================================
---- gui/wxpython/iclass/plots.py (revision 57728)
+--- gui/wxpython/iclass/plots.py (revision 57734)
+++ gui/wxpython/iclass/plots.py (working copy)
@@ -19,6 +19,7 @@
import wx.lib.plot as plot
@@ -2378,67 +635,11 @@
def DrawCoincidencePlots(self):
"""!Draw coincidence plots"""
for bandIdx in range(len(self.bandList)):
-Index: gui/wxpython/mapdisp/toolbars.py
-===================================================================
---- gui/wxpython/mapdisp/toolbars.py (revision 57728)
-+++ gui/wxpython/mapdisp/toolbars.py (working copy)
-@@ -49,6 +49,8 @@
- label = _('Create histogram of raster map')),
- 'vnet' : MetaIcon(img = 'vector-tools',
- label = _('Vector network analysis tool')),
-+ 'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
-+ label = _("Interactive scatter plot tool")),
- }
-
- NvizIcons = {
-@@ -239,7 +241,9 @@
- (MapIcons["scatter"], self.parent.OnScatterplot),
- (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
- (BaseIcons["histogramD"], self.parent.OnHistogram),
-- (MapIcons["vnet"], self.parent.OnVNet)))
-+ (MapIcons["vnet"], self.parent.OnVNet),
-+ (MapIcons["interact_scatter"],
-+ self.parent.OnIScatt)))
-
- def OnDecoration(self, event):
- """!Decorations overlay menu
-Index: gui/wxpython/mapdisp/frame.py
-===================================================================
---- gui/wxpython/mapdisp/frame.py (revision 57728)
-+++ gui/wxpython/mapdisp/frame.py (working copy)
-@@ -225,6 +225,7 @@
- #
- self.dialogs = {}
- self.dialogs['attributes'] = None
-+ self.dialogs['scatt_plot'] = None
- self.dialogs['category'] = None
- self.dialogs['barscale'] = None
- self.dialogs['legend'] = None
-@@ -1168,6 +1169,19 @@
- """!Returns toolbar with zooming tools"""
- return self.toolbars['map']
-
-+ def OnIScatt(self, event):
-+ """!Init interactive scatterplot tools
-+ """
-+ if self.dialogs['scatt_plot']:
-+ self.dialogs['scatt_plot'].Raise()
-+ return
-+
-+ from scatt_plot.frame import IScattDialog
-+ self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
-+
-+ self.dialogs['scatt_plot'].CenterOnScreen()
-+ self.dialogs['scatt_plot'].Show()
-+
- def OnVNet(self, event):
- """!Dialog for v.net* modules
- """
Index: gui/wxpython/scatt_plot/controllers.py
===================================================================
--- gui/wxpython/scatt_plot/controllers.py (revision 0)
+++ gui/wxpython/scatt_plot/controllers.py (working copy)
-@@ -0,0 +1,891 @@
+@@ -0,0 +1,893 @@
+"""!
+ at package scatt_plot.controllers
+
@@ -2468,7 +669,7 @@
+from scatt_plot.gthreading import gThread
+from scatt_plot.scatt_core import Core, idBandsToidScatt
+from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
-+from iclass.dialogs import IClassGroupDialog
++from iclass.dialogs import IClassGroupDialog
+
+import grass.script as grass
+from grass.pydispatch.signal import Signal
@@ -3211,6 +1412,8 @@
+ self.SyncCats()
+
+ def UpdateCategoryRaster(self, cat_id, attrs, render = True):
++ if not self.scatt_mgr.data_set:
++ return
+
+ cat_rast = self.scatt_mgr.core.GetCatRast(cat_id)
+ if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
@@ -5225,7 +3428,7 @@
===================================================================
--- gui/wxpython/scatt_plot/frame.py (revision 0)
+++ gui/wxpython/scatt_plot/frame.py (working copy)
-@@ -0,0 +1,714 @@
+@@ -0,0 +1,815 @@
+"""!
+ at package scatt_plot.dialogs
+
@@ -5251,13 +3454,14 @@
+from core.gcmd import GException, GError, RunCommand
+
+from gui_core.gselect import Select
-+from gui_core.dialogs import SetOpacityDialog
++from gui_core.dialogs import SetOpacityDialog, SimpleDialog
+
+from scatt_plot.controllers import ScattsManager
+from scatt_plot.toolbars import MainToolbar, EditingToolbar, CategoryToolbar
+from scatt_plot.scatt_core import idScattToidBands
+from scatt_plot.plots import ScatterPlotWidget
+from vnet.dialogs import VnetStatusbar
++from iclass.dialogs import ContrastColor
+
+try:
+ from agw import aui
@@ -5356,15 +3560,16 @@
+ cats_mgr=self.scatt_mgr.GetCategoriesManager(),
+ sel_cats_in_iscatt=self._selCatInIScatt())
+
-+ self.catsPanel.SetMaxSize((-1, 150))
++ self.catsPanel.SetMinSize((-1, 100))
++ self.catsPanel.SetInitialSize((-1, 150))
+
+ box_capt = wx.StaticBox(parent=self.catsPanel, id=wx.ID_ANY,
-+ label=' %s ' % _("Classes manager for scatter plots"),)
++ label=' %s ' % _("Classes"),)
+ catsSizer = wx.StaticBoxSizer(box_capt, wx.VERTICAL)
+
+ self.toolbars['categoryToolbar'] = self._createCategoryToolbar(self.catsPanel)
+
-+ catsSizer.Add(item=self.cats_list, proportion=1, flag=wx.EXPAND)
++ catsSizer.Add(item=self.cats_list, proportion=1, flag=wx.EXPAND | wx.TOP, border = 5)
+ if self.toolbars['categoryToolbar']:
+ catsSizer.Add(item=self.toolbars['categoryToolbar'], proportion=0)
+
@@ -5563,16 +3768,16 @@
+ return self.scatt_mgr
+
+class CategoryListCtrl(wx.ListCtrl,
-+ listmix.ListCtrlAutoWidthMixin,
-+ listmix.TextEditMixin):
++ listmix.ListCtrlAutoWidthMixin):
++ #listmix.TextEditMixin):
+
+ def __init__(self, parent, cats_mgr, sel_cats_in_iscatt, id = wx.ID_ANY):
+
+ wx.ListCtrl.__init__(self, parent, id,
+ style = wx.LC_REPORT|wx.LC_VIRTUAL|wx.LC_HRULES|
-+ wx.LC_VRULES|wx.LC_SINGLE_SEL)
-+ self.columns = ((_('Class name'), 'name'),
-+ (_('Color'), 'color'))
++ wx.LC_VRULES|wx.LC_SINGLE_SEL|wx.LC_NO_HEADER)
++ self.columns = ((_('Class name'), 'name'), )
++ #(_('Color'), 'color'))
+
+ self.sel_cats_in_iscatt = sel_cats_in_iscatt
+
@@ -5585,12 +3790,12 @@
+
+ listmix.ListCtrlAutoWidthMixin.__init__(self)
+
-+ listmix.TextEditMixin.__init__(self)
++ #listmix.TextEditMixin.__init__(self)
+
+ self.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnCategoryRightUp) #wxMSW
+ self.Bind(wx.EVT_RIGHT_UP, self.OnCategoryRightUp) #wxGTK
+
-+ self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
++ #self.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnEdit)
+ self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSel)
+
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
@@ -5626,8 +3831,8 @@
+ for i, col in enumerate(columns):
+ self.InsertColumn(i, col[0])#wx.LIST_FORMAT_RIGHT
+
-+ self.SetColumnWidth(0, 100)
-+ self.SetColumnWidth(1, 100)
++ #self.SetColumnWidth(0, 100)
++ #self.SetColumnWidth(1, 100)
+
+ def AddCategory(self):
+
@@ -5678,7 +3883,7 @@
+ lastFound = index
+ indices.append(index)
+ return indices
-+
++ """
+ def OnEdit(self, event):
+ currentItem = event.m_itemIndex
+ currentCol = event.m_col
@@ -5695,8 +3900,8 @@
+ wx.CallAfter(self.SetFocus)
+
+ event.Skip()
++ """
+
-+
+ def DeselectAll(self):
+ """!Deselect all items"""
+ indexList = self.GetSelectedIndices()
@@ -5716,8 +3921,16 @@
+ return -1
+
+ def OnGetItemAttr(self, item):
-+ return None
++ cat_id = self.cats_mgr.GetCategories()[item]
++ c = self.cats_mgr.GetCategoryAttrs(cat_id)['color']
+
++ back_c = wx.Colour(*map(int, c.split(':')))
++ text_c = wx.Colour(*ContrastColor(back_c))
++
++ # if it is in scope of the method, gui falls, using self solved it
++ self.l = wx.ListItemAttr(colText=text_c, colBack=back_c)
++ return self.l
++
+ def OnCategoryRightUp(self, event):
+ """!Show context menu on right click"""
+ item, flags = self.HitTest((event.GetX(), event.GetY()))
@@ -5733,6 +3946,41 @@
+
+ menu = wx.Menu()
+
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move to top"))
++ self.Bind(wx.EVT_MENU, self.OnMoveTop, id=item_id)
++ if cat_idx == 0:
++ menu.Enable(item_id, False)
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move to bottom"))
++ self.Bind(wx.EVT_MENU, self.OnMoveBottom, id=item_id)
++ if cat_idx == len(cats) - 1:
++ menu.Enable(item_id, False)
++
++ menu.AppendSeparator()
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move category up"))
++ self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
++ if cat_idx == 0:
++ menu.Enable(item_id, False)
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Move category down"))
++ self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
++ if cat_idx == len(cats) - 1:
++ menu.Enable(item_id, False)
++
++ menu.AppendSeparator()
++
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Export class raster"))
++ self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
++
++ menu.AppendSeparator()
++
+ if showed:
+ text = _("Hide")
+ else:
@@ -5743,27 +3991,19 @@
+ self.Bind(wx.EVT_MENU, lambda event : self._setCatAttrs(cat_id=cat_id,
+ attrs={'show' : not showed}),
+ id=item_id)
-+ menu.AppendSeparator()
+
-+ if cat_idx != 0:
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move category up"))
-+ self.Bind(wx.EVT_MENU, self.OnMoveUp, id=item_id)
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Rename class"))
++ self.Bind(wx.EVT_MENU, self.OnRename, id=item_id)
+
-+ if cat_idx != len(cats) - 1:
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Move category down"))
-+ self.Bind(wx.EVT_MENU, self.OnMoveDown, id=item_id)
++ item_id = wx.NewId()
++ menu.Append(item_id, text=_("Set color"))
++ self.Bind(wx.EVT_MENU, self.OnSetColor, id=item_id)
+
-+ menu.AppendSeparator()
-+
+ item_id = wx.NewId()
+ menu.Append(item_id, text=_("Change opacity level"))
+ self.Bind(wx.EVT_MENU, self.OnPopupOpacityLevel, id=item_id)
+
-+ item_id = wx.NewId()
-+ menu.Append(item_id, text=_("Export raster"))
-+ self.Bind(wx.EVT_MENU, self.OnExportCatRast, id=item_id)
+
+
+ self.PopupMenu(menu)
@@ -5792,6 +4032,34 @@
+ self.cats_mgr.ChangePosition(cat_id, cat_idx + 1)
+ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
+
++ def OnMoveTop(self, event):
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++ self.cats_mgr.ChangePosition(cat_id, 0)
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def OnMoveBottom(self, event):
++ cat_idx = self.rightClickedItemIdx
++ cats = self.cats_mgr.GetCategories()
++ cat_id = cats[cat_idx]
++ self.cats_mgr.ChangePosition(cat_id, len(cats) - 1)
++ self.RefreshItems(0, len(self.cats_mgr.GetCategories()))
++
++ def OnSetColor(self, event):
++ """!Popup opacity level indicator"""
++ cat_idx = self.rightClickedItemIdx
++ cat_id = self.cats_mgr.GetCategories()[cat_idx]
++
++ dlg = wx.ColourDialog(self)
++ dlg.GetColourData().SetChooseFull(True)
++
++ if dlg.ShowModal() == wx.ID_OK:
++ color = dlg.GetColourData().GetColour().Get()
++ color = ':'.join(map(str, color))
++ self.cats_mgr.SetCategoryAttrs(cat_id, {"color" : color})
++
++ dlg.Destroy()
++
+ def OnPopupOpacityLevel(self, event):
+ """!Popup opacity level indicator"""
+
@@ -5812,12 +4080,48 @@
+
+ dlg.Destroy()
+
++ def OnRename(self, event):
++ cat_id = self.cats_mgr.GetCategories()[self.rightClickedItemIdx]
++ cat_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++
++ dlg = RenameClassDialog(self, old_name=cat_attrs['name'])
++ dlg.CentreOnParent()
++
++ while True:
++ if dlg.ShowModal() == wx.ID_OK:
++ name = dlg.GetNewName().strip()
++ if not name:
++ GMessage(parent=self, message=_("Empty name was inserted."))
++ else:
++ self.cats_mgr.SetCategoryAttrs(cat_id, {"name" : name})
++ break
++ else:
++ break
++
++ dlg.Destroy()
++
+ def _setCatAttrs(self, cat_id, attrs):
+ self.cats_mgr.setCategoryAttrs.disconnect(self.Update)
+ self.cats_mgr.SetCategoryAttrs(cat_id, attrs)
+ self.cats_mgr.setCategoryAttrs.connect(self.Update)
+
++class RenameClassDialog(SimpleDialog):
++ def __init__(self, parent, old_name, title=("Change class name")):
++ SimpleDialog.__init__(self, parent, title)
+
++ self.name = wx.TextCtrl(self.panel, id = wx.ID_ANY)
++ self.name.SetValue(old_name)
++
++ self.dataSizer.Add(self.name, proportion = 0,
++ flag = wx.EXPAND | wx.ALL, border = 1)
++
++ self.panel.SetSizer(self.sizer)
++ self.name.SetMinSize((200, -1))
++ self.sizer.Fit(self)
++
++ def GetNewName(self):
++ return self.name.GetValue()
++
+class AddScattPlotDialog(wx.Dialog):
+
+ def __init__(self, parent, bands, id = wx.ID_ANY):
@@ -6905,56 +5209,1909 @@
+ #output_queue.put((merged_img, full_extend))
+ return merged_img, full_extend
\ No newline at end of file
-Index: gui/wxpython/vnet/dialogs.py
+Index: gui/wxpython/mapdisp/toolbars.py
===================================================================
---- gui/wxpython/vnet/dialogs.py (revision 57728)
-+++ gui/wxpython/vnet/dialogs.py (working copy)
-@@ -1218,7 +1218,7 @@
- pos = (row, 1))
+--- gui/wxpython/mapdisp/toolbars.py (revision 57734)
++++ gui/wxpython/mapdisp/toolbars.py (working copy)
+@@ -49,6 +49,8 @@
+ label = _('Create histogram of raster map')),
+ 'vnet' : MetaIcon(img = 'vector-tools',
+ label = _('Vector network analysis tool')),
++ 'interact_scatter' : MetaIcon(img = 'layer-raster-profile',
++ label = _("Interactive scatter plot tool")),
+ }
- row += 1
-- gridSizer.Add(item = self.settings["invert_colors"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
-+ gridSizer.Add(item=self.settings["invert_colors"], flag=wx.ALIGN_CENTER_VERTICAL, pos=(row, 0))
+ NvizIcons = {
+@@ -239,7 +241,9 @@
+ (MapIcons["scatter"], self.parent.OnScatterplot),
+ (MapIcons["histogram"], self.parent.OnHistogramPyPlot),
+ (BaseIcons["histogramD"], self.parent.OnHistogram),
+- (MapIcons["vnet"], self.parent.OnVNet)))
++ (MapIcons["vnet"], self.parent.OnVNet),
++ (MapIcons["interact_scatter"],
++ self.parent.OnIScatt)))
+
+ def OnDecoration(self, event):
+ """!Decorations overlay menu
+Index: gui/wxpython/mapdisp/frame.py
+===================================================================
+--- gui/wxpython/mapdisp/frame.py (revision 57734)
++++ gui/wxpython/mapdisp/frame.py (working copy)
+@@ -225,6 +225,7 @@
+ #
+ self.dialogs = {}
+ self.dialogs['attributes'] = None
++ self.dialogs['scatt_plot'] = None
+ self.dialogs['category'] = None
+ self.dialogs['barscale'] = None
+ self.dialogs['legend'] = None
+@@ -1168,6 +1169,19 @@
+ """!Returns toolbar with zooming tools"""
+ return self.toolbars['map']
- gridSizer.AddGrowableCol(1)
- styleBoxSizer.Add(item = gridSizer, flag = wx.EXPAND)
-Index: gui/wxpython/vnet/toolbars.py
++ def OnIScatt(self, event):
++ """!Init interactive scatterplot tools
++ """
++ if self.dialogs['scatt_plot']:
++ self.dialogs['scatt_plot'].Raise()
++ return
++
++ from scatt_plot.frame import IScattDialog
++ self.dialogs['scatt_plot'] = IScattDialog(parent=self, giface=self._giface)
++
++ self.dialogs['scatt_plot'].CenterOnScreen()
++ self.dialogs['scatt_plot'].Show()
++
+ def OnVNet(self, event):
+ """!Dialog for v.net* modules
+ """
+Index: gui/wxpython/vdigit/wxdigit.py
===================================================================
---- gui/wxpython/vnet/toolbars.py (revision 57728)
-+++ gui/wxpython/vnet/toolbars.py (working copy)
-@@ -19,9 +19,9 @@
+--- gui/wxpython/vdigit/wxdigit.py (revision 57734)
++++ gui/wxpython/vdigit/wxdigit.py (working copy)
+@@ -17,7 +17,7 @@
+ (and NumPy would be an excellent candidate for acceleration via
+ e.g. OpenCL or CUDA; I'm surprised it hasn't happened already).
- import wx
+-(C) 2007-2011 by the GRASS Development Team
++(C) 2007-2011, 2013 by the GRASS Development Team
--from icon import MetaIcon
-+from icons.icon import MetaIcon
- from gui_core.toolbars import BaseToolbar, BaseIcons
--from core.gcmd import RunCommand
-+from core.gcmd import RunCommand
- from core.utils import _
+ This program is free software under the GNU General Public License
+ (>=v2). Read the file COPYING that comes with GRASS for details.
+@@ -27,6 +27,8 @@
- class PointListToolbar(BaseToolbar):
-Index: gui/wxpython/Makefile
+ import grass.script.core as grass
+
++from grass.pydispatch.signal import Signal
++
+ from core.gcmd import GError
+ from core.debug import Debug
+ from core.settings import UserSettings
+@@ -176,7 +178,21 @@
+
+ if self.poMapInfo:
+ self.InitCats()
+-
++
++ self.emit_signals = False
++
++ # signals which describes features changes during digitization,
++ # activate them using EmitSignals method
++ #TODO signal for errors?
++ self.featureAdded = Signal('IVDigit.featureAdded')
++ self.areasDeleted = Signal('IVDigit.areasDeleted')
++ self.vertexMoved = Signal('IVDigit.vertexMoved')
++ self.vertexAdded = Signal('IVDigit.vertexAdded')
++ self.vertexRemoved = Signal('IVDigit.vertexRemoved')
++ self.featuresDeleted = Signal('IVDigit.featuresDeleted')
++ self.featuresMoved = Signal('IVDigit.featuresMoved')
++ self.lineEdited = Signal('IVDigit.lineEdited')
++
+ def __del__(self):
+ Debug.msg(1, "IVDigit.__del__()")
+ Vect_destroy_line_struct(self.poPoints)
+@@ -188,7 +204,12 @@
+ Vect_close(self.poBgMapInfo)
+ self.poBgMapInfo = self.popoBgMapInfo = None
+ del self.bgMapInfo
+-
++
++ def EmitSignals(self, emit):
++ """!Activate/deactivate signals which describes features changes during digitization.
++ """
++ self.emit_signals = emit
++
+ def CloseBackgroundMap(self):
+ """!Close background vector map"""
+ if not self.poBgMapInfo:
+@@ -394,7 +415,6 @@
+
+ @return tuple (number of added features, feature ids)
+ """
+-
+ layer = self._getNewFeaturesLayer()
+ cat = self._getNewFeaturesCat()
+
+@@ -419,10 +439,14 @@
+ return (-1, None)
+
+ self.toolbar.EnableUndo()
+-
+- return self._addFeature(vtype, points, layer, cat,
+- self._getSnapMode(), self._display.GetThreshold())
+-
++
++ ret = self._addFeature(vtype, points, layer, cat,
++ self._getSnapMode(), self._display.GetThreshold())
++ if ret[0] > -1 and self.emit_signals:
++ self.featureAdded.emit(new_bboxs = [self._createBbox(points)], new_areas_cats = [[{layer : [cat]}, None]])
++
++ return ret
++
+ def DeleteSelectedLines(self):
+ """!Delete selected features
+
+@@ -434,16 +458,27 @@
+ # collect categories for deleting if requested
+ deleteRec = UserSettings.Get(group = 'vdigit', key = 'delRecord', subkey = 'enabled')
+ catDict = dict()
++
++ old_bboxs = []
++ old_areas_cats = []
+ if deleteRec:
+ for i in self._display.selected['ids']:
++
+ if Vect_read_line(self.poMapInfo, None, self.poCats, i) < 0:
+ self._error.ReadLine(i)
+
+- cats = self.poCats.contents
+- for j in range(cats.n_cats):
+- if cats.field[j] not in catDict.keys():
+- catDict[cats.field[j]] = list()
+- catDict[cats.field[j]].append(cats.cat[j])
++ if self.emit_signals:
++ ret = self._getLineAreaBboxCats(i)
++ if ret:
++ old_bboxs += ret[0]
++ old_areas_cats += ret[1]
++
++ # catDict was not used -> put into comment
++ #cats = self.poCats.contents
++ #for j in range(cats.n_cats):
++ # if cats.field[j] not in catDict.keys():
++ # catDict[cats.field[j]] = list()
++ # catDict[cats.field[j]].append(cats.cat[j])
+
+ poList = self._display.GetSelectedIList()
+ nlines = Vedit_delete_lines(self.poMapInfo, poList)
+@@ -456,7 +491,10 @@
+ self._deleteRecords(catDict)
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.featuresDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
++
+ return nlines
+
+ def _deleteRecords(self, cats):
+@@ -512,22 +550,173 @@
+
+ @return number of deleted
+ """
++ if len(self._display.selected['ids']) < 1:
++ return 0
++
+ poList = self._display.GetSelectedIList()
+ cList = poList.contents
+
+ nareas = 0
++ old_bboxs = []
++ old_areas_cats = []
++
+ for i in range(cList.n_values):
++
+ if Vect_get_line_type(self.poMapInfo, cList.value[i]) != GV_CENTROID:
+ continue
+-
++
++ if self.emit_signals:
++ area = Vect_get_centroid_area(self.poMapInfo, cList.value[i]);
++ if area > 0:
++ bbox, cats = self._getaAreaBboxCats(area)
++ old_bboxs += bbox
++ old_areas_cats += cats
++
+ nareas += Vedit_delete_area_centroid(self.poMapInfo, cList.value[i])
+
+ if nareas > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
++ if self.emit_signals:
++ self.areasDeleted.emit(old_bboxs = old_bboxs, old_areas_cats = old_areas_cats)
++
++ return nareas
++
++ def _getLineAreaBboxCats(self, ln_id):
++ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
++
++ if ltype == GV_CENTROID:
++ #TODO centroid opttimization, can be adited also its area -> it will appear two times in new_ lists
++ return self._getCentroidAreaBboxCats(ln_id)
++ else:
++ return [self._getBbox(ln_id)], [self._getLineAreasCategories(ln_id)]
++
++
++ def _getCentroidAreaBboxCats(self, centroid):
++ if not Vect_line_alive(self.poMapInfo, centroid):
++ return None
++
++ area = Vect_get_centroid_area(self.poMapInfo, centroid)
++ if area > 0:
++ return self._getaAreaBboxCats(area)
++ else:
++ return None
++
++ def _getaAreaBboxCats(self, area):
++
++ po_b_list = Vect_new_list()
++ Vect_get_area_boundaries(self.poMapInfo, area, po_b_list);
++ b_list = po_b_list.contents
++
++ geoms = []
++ areas_cats = []
++
++ if b_list.n_values > 0:
++ for i_line in range(b_list.n_values):
++
++ line = b_list.value[i_line];
++
++ geoms.append(self._getBbox(abs(line)))
++ areas_cats.append(self._getLineAreasCategories(abs(line)))
+
+- return nareas
++ Vect_destroy_list(po_b_list);
++
++ return geoms, areas_cats
++
++ def _getLineAreasCategories(self, ln_id):
++ if not Vect_line_alive (self.poMapInfo, ln_id):
++ return []
++
++ ltype = Vect_read_line(self.poMapInfo, None, None, ln_id)
++ if ltype != GV_BOUNDARY:
++ return []
++
++ cats = [None, None]
++
++ left = c_int()
++ right = c_int()
++
++ if Vect_get_line_areas(self.poMapInfo, ln_id, pointer(left), pointer(right)) == 1:
++ areas = [left.value, right.value]
++
++ for i, a in enumerate(areas):
++ if a > 0:
++ centroid = Vect_get_area_centroid(self.poMapInfo, a)
++ if centroid <= 0:
++ continue
++ c = self._getCategories(centroid)
++ if c:
++ cats[i] = c
++
++ return cats
++
++ def _getCategories(self, ln_id):
++ if not Vect_line_alive (self.poMapInfo, ln_id):
++ return none
++
++ poCats = Vect_new_cats_struct()
++ if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0:
++ Vect_destroy_cats_struct(poCats)
++ return None
++
++ cCats = poCats.contents
++
++ cats = {}
++ for j in range(cCats.n_cats):
++ if cats.has_key(cCats.field[j]):
++ cats[cCats.field[j]].append(cCats.cat[j])
++ else:
++ cats[cCats.field[j]] = [cCats.cat[j]]
+
++ Vect_destroy_cats_struct(poCats)
++ return cats
++
++ def _getBbox(self, ln_id):
++ if not Vect_line_alive (self.poMapInfo, ln_id):
++ return None
++
++ poPoints = Vect_new_line_struct()
++ if Vect_read_line(self.poMapInfo, poPoints, None, ln_id) < 0:
++ Vect_destroy_line_struct(poPoints)
++ return []
++
++ geom = self._convertGeom(poPoints)
++ bbox = self._createBbox(geom)
++ Vect_destroy_line_struct(poPoints)
++ return bbox
++
++ def _createBbox(self, points):
++
++ bbox = {}
++ for pt in points:
++ if not bbox.has_key('maxy'):
++ bbox['maxy'] = pt[1]
++ bbox['miny'] = pt[1]
++ bbox['maxx'] = pt[0]
++ bbox['minx'] = pt[0]
++ continue
++
++ if bbox['maxy'] < pt[1]:
++ bbox['maxy'] = pt[1]
++ elif bbox['miny'] > pt[1]:
++ bbox['miny'] = pt[1]
++
++ if bbox['maxx'] < pt[0]:
++ bbox['maxx'] = pt[0]
++ elif bbox['minx'] > pt[0]:
++ bbox['minx'] = pt[0]
++ return bbox
++
++ def _convertGeom(self, poPoints):
++
++ Points = poPoints.contents
++
++ pts_geom = []
++ for j in range(Points.n_points):
++ pts_geom.append((Points.x[j], Points.y[j]))
++
++ return pts_geom
++
+ def MoveSelectedLines(self, move):
+ """!Move selected features
+
+@@ -536,16 +725,45 @@
+ if not self._checkMap():
+ return -1
+
++ nsel = len(self._display.selected['ids'])
++ if nsel < 1:
++ return -1
++
+ thresh = self._display.GetThreshold()
+ snap = self._getSnapMode()
+
+ poList = self._display.GetSelectedIList()
++
++ if self.emit_signals:
++ old_bboxs = []
++ old_areas_cats = []
++ for sel_id in self._display.selected['ids']:
++ ret = self._getLineAreaBboxCats(sel_id)
++ if ret:
++ old_bboxs += ret[0]
++ old_areas_cats += ret[1]
++
++ Vect_set_updated(self.poMapInfo, 1)
++ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
++
+ nlines = Vedit_move_lines(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+ poList,
+ move[0], move[1], 0,
+ snap, thresh)
++
+ Vect_destroy_list(poList)
+-
++
++ if nlines > 0 and self.emit_signals:
++ new_bboxs = []
++ new_areas_cats = []
++ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
++ for i in range(n_up_lines_old, n_up_lines):
++ new_id = Vect_get_updated_line(self.poMapInfo, i)
++ ret = self._getLineAreaBboxCats(new_id)
++ if ret:
++ new_bboxs += ret[0]
++ new_areas_cats += ret[1]
++
+ if nlines > 0 and self._settings['breakLines']:
+ for i in range(1, nlines):
+ self._breakLineAtIntersection(nlines + i, None, changeset)
+@@ -553,7 +771,13 @@
+ if nlines > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.featuresMoved.emit(new_bboxs = new_bboxs,
++ old_bboxs = old_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
+ return nlines
+
+ def MoveSelectedVertex(self, point, move):
+@@ -571,12 +795,21 @@
+
+ if len(self._display.selected['ids']) != 1:
+ return -1
+-
++
++ # move only first found vertex in bbox
++ poList = self._display.GetSelectedIList()
++
++ if self.emit_signals:
++ cList = poList.contents
++ old_bboxs = [self._getBbox(cList.value[0])]
++ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
++
++ Vect_set_updated(self.poMapInfo, 1)
++ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
++
+ Vect_reset_line(self.poPoints)
+ Vect_append_point(self.poPoints, point[0], point[1], 0.0)
+-
+- # move only first found vertex in bbox
+- poList = self._display.GetSelectedIList()
++
+ moved = Vedit_move_vertex(self.poMapInfo, self.popoBgMapInfo, int(self.poBgMapInfo is not None),
+ poList, self.poPoints,
+ self._display.GetThreshold(type = 'selectThresh'),
+@@ -584,7 +817,17 @@
+ move[0], move[1], 0.0,
+ 1, self._getSnapMode())
+ Vect_destroy_list(poList)
+-
++
++ if moved > 0 and self.emit_signals:
++ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
++
++ new_bboxs = []
++ new_areas_cats = []
++ for i in range(n_up_lines_old, n_up_lines):
++ new_id = Vect_get_updated_line(self.poMapInfo, i)
++ new_bboxs.append(self._getBbox(new_id))
++ new_areas_cats.append(self._getLineAreasCategories(new_id))
++
+ if moved > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+ None)
+@@ -592,7 +835,13 @@
+ if moved > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.vertexMoved.emit(new_bboxs = new_bboxs,
++ new_areas_cats = new_areas_cats,
++ old_areas_cats = old_areas_cats,
++ old_bboxs = old_bboxs)
++
+ return moved
+
+ def AddVertex(self, coords):
+@@ -681,6 +930,10 @@
+ self._error.ReadLine(line)
+ return -1
+
++ if self.emit_signals:
++ old_bboxs = [self._getBbox(line)]
++ old_areas_cats = [self._getLineAreasCategories(line)]
++
+ # build feature geometry
+ Vect_reset_line(self.poPoints)
+ for p in coords:
+@@ -696,6 +949,9 @@
+
+ newline = Vect_rewrite_line(self.poMapInfo, line, ltype,
+ self.poPoints, self.poCats)
++ if newline > 0 and self.emit_signals:
++ new_geom = [self._getBbox(newline)]
++ new_areas_cats = [self._getLineAreasCategories(newline)]
+
+ if newline > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(newline, None)
+@@ -703,7 +959,13 @@
+ if newline > 0:
+ self._addChangeset()
+ self.toolbar.EnableUndo()
+-
++
++ if self.emit_signals:
++ self.lineEdited.emit(old_bboxs = old_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_bboxs = new_bboxs,
++ new_areas_cats = new_areas_cats)
++
+ return newline
+
+ def FlipLine(self):
+@@ -1514,6 +1776,16 @@
+ return 0
+
+ poList = self._display.GetSelectedIList()
++
++ if self.emit_signals:
++ cList = poList.contents
++
++ old_bboxs = [self._getBbox(cList.value[0])]
++ old_areas_cats = [self._getLineAreasCategories(cList.value[0])]
++
++ Vect_set_updated(self.poMapInfo, 1)
++ n_up_lines_old = Vect_get_num_updated_lines(self.poMapInfo)
++
+ Vect_reset_line(self.poPoints)
+ Vect_append_point(self.poPoints, coords[0], coords[1], 0.0)
+
+@@ -1525,15 +1797,35 @@
+ else:
+ ret = Vedit_remove_vertex(self.poMapInfo, poList,
+ self.poPoints, thresh)
++
+ Vect_destroy_list(poList)
++
++ if ret > 0 and self.emit_signals:
++ new_bboxs = []
++ new_areas_cats = []
++
++ n_up_lines = Vect_get_num_updated_lines(self.poMapInfo)
++ for i in range(n_up_lines_old, n_up_lines):
++ new_id = Vect_get_updated_line(self.poMapInfo, i)
++ new_areas_cats.append(self._getLineAreasCategories(new_id))
++ new_bboxs.append(self._getBbox(new_id))
+
+ if not add and ret > 0 and self._settings['breakLines']:
+ self._breakLineAtIntersection(Vect_get_num_lines(self.poMapInfo),
+ None)
+-
++
+ if ret > 0:
+ self._addChangeset()
+-
++
++ if ret > 0 and self.emit_signals:
++ if add:
++ self.vertexAdded.emit(old_bboxs = old_bboxs, new_bboxs = new_bboxs)
++ else:
++ self.vertexRemoved.emit(old_bboxs = old_bboxs,
++ new_bboxs = new_bboxs,
++ old_areas_cats = old_areas_cats,
++ new_areas_cats = new_areas_cats)
++
+ return 1
+
+ def GetLineCats(self, line):
+Index: gui/wxpython/vdigit/toolbars.py
===================================================================
---- gui/wxpython/Makefile (revision 57728)
-+++ gui/wxpython/Makefile (working copy)
-@@ -13,7 +13,7 @@
- $(wildcard animation/* core/*.py dbmgr/* gcp/*.py gmodeler/* \
- gui_core/*.py iclass/* lmgr/*.py location_wizard/*.py mapwin/*.py mapdisp/*.py \
- mapswipe/* modules/*.py nviz/*.py psmap/* rlisetup/* timeline/* vdigit/* \
-- vnet/*.py web_services/*.py wxplot/*.py) \
-+ vnet/*.py web_services/*.py wxplot/*.py scatt_plot/*.py) \
- gis_set.py gis_set_error.py wxgui.py README
+--- gui/wxpython/vdigit/toolbars.py (revision 57734)
++++ gui/wxpython/vdigit/toolbars.py (working copy)
+@@ -17,6 +17,7 @@
+ import wx
- DSTFILES := $(patsubst %,$(ETCDIR)/%,$(SRCFILES)) \
-@@ -21,8 +21,9 @@
+ from grass.script import core as grass
++from grass.pydispatch.signal import Signal
- PYDSTDIRS := $(patsubst %,$(ETCDIR)/%,animation core dbmgr gcp gmodeler \
- gui_core iclass lmgr location_wizard mapwin mapdisp modules nviz psmap \
-- mapswipe vdigit wxplot web_services rlisetup vnet timeline)
-+ mapswipe vdigit wxplot web_services rlisetup vnet timeline scatt_plot)
+ from gui_core.toolbars import BaseToolbar, BaseIcons
+ from gui_core.dialogs import CreateNewVector
+@@ -42,6 +43,8 @@
+ self.digit = None
+ self._giface = giface
+
++ self.editingStarted = Signal("VDigitToolbar.editingStarted")
++
+ # currently selected map layer for editing (reference to MapLayer instance)
+ self.mapLayer = None
+ # list of vector layers from Layer Manager (only in the current mapset)
+@@ -860,6 +863,7 @@
+ alpha = int(opacity * 255)
+ self.digit.GetDisplay().UpdateSettings(alpha = alpha)
+
++ self.editingStarted.emit(vectMap = mapLayer.GetName(), digit = self.digit)
+ return True
+ def StopEditing(self):
+Index: gui/icons/grass/polygon.png
+===================================================================
+Cannot display: file marked as a binary type.
+svn:mime-type = application/octet-stream
+Index: gui/icons/grass/polygon.png
+===================================================================
+--- gui/icons/grass/polygon.png (revision 57734)
++++ gui/icons/grass/polygon.png (working copy)
+
+Property changes on: gui/icons/grass/polygon.png
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++application/octet-stream
+\ No newline at end of property
+Index: lib/imagery/scatt_sccats.c
+===================================================================
+--- lib/imagery/scatt_sccats.c (revision 0)
++++ lib/imagery/scatt_sccats.c (working copy)
+@@ -0,0 +1,405 @@
++/*!
++ \file lib/imagery/scatt_cat_rast.c
+
- DSTDIRS := $(patsubst %,$(ETCDIR)/%,icons scripts xml)
-
- default: $(DSTFILES)
++ \brief Imagery library - functions for manipulation with scatter plot structs.
++
++ Copyright (C) 2013 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 Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
++ */
++
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++/*!
++ \brief Compute band ids from scatter plot id.
++
++ Scatter plot id describes which bands defines the scatter plot.
++
++ Let say we have 3 bands, their ids are 0, 1 and 2.
++ Scatter plot with id 0 consists of band 1 (b_1_id) 0 and band 2 (b_2_id) 1.
++ All scatter plots:
++ scatt_id b_1_id b_2_id
++ 0 0 1
++ 1 0 2
++ 2 1 2
++
++ \param scatt_id scatter plot id
++ \param n_bands number of bands
++ \param [out] b_1_id id of band1
++ \param[out] b_2_id id of band2
++
++ \return 0
++ */
++int I_id_scatt_to_bands(const int scatt_id, const int n_bands, int * b_1_id, int * b_2_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * b_1_id = (int) ((2 * n_b1 + 1 - sqrt((double)((2 * n_b1 + 1) * (2 * n_b1 + 1) - 8 * scatt_id))) / 2);
++
++ * b_2_id = scatt_id - ((* b_1_id) * (2 * n_b1 + 1) - (* b_1_id) * (* b_1_id)) / 2 + (* b_1_id) + 1;
++
++ return 0;
++}
++
++
++/*!
++ \brief Compute scatter plot id from band ids.
++
++ See also I_id_scatt_to_bands().
++
++ \param n_bands number of bands
++ \param b_1_id id of band1
++ \param b_1_id id of band2
++ \param [out] scatt_id scatter plot id
++
++ \return 0
++ */
++int I_bands_to_id_scatt(const int b_1_id, const int b_2_id, const int n_bands, int * scatt_id)
++{
++ int n_b1 = n_bands - 1;
++
++ * scatt_id = (b_1_id * (2 * n_b1 + 1) - b_1_id * b_1_id) / 2 + b_2_id - b_1_id - 1;
++
++ return 0;
++}
++
++/*!
++ \brief Initialize structure for storing scatter plots data.
++
++ \param cats pointer to scCats struct
++ \param n_bands number of bands
++ \param type SC_SCATT_DATA - stores scatter plots
++ \param type SC_SCATT_CONDITIONS - stores selected areas in scatter plots
++ */
++void I_sc_init_cats(struct scCats * cats, int n_bands, int type)
++{
++ int i_cat;
++
++ cats->type = type;
++
++ cats->n_cats = 100;
++ cats->n_a_cats = 0;
++
++ cats->n_bands = n_bands;
++ cats->n_scatts = (n_bands - 1) * n_bands / 2;
++
++ cats->cats_arr = (struct scScatts **) G_malloc(cats->n_cats * sizeof(struct scScatts *));
++ memset(cats->cats_arr, 0, cats-> n_cats * sizeof(struct scScatts *));
++
++ cats->cats_ids = (int *) G_malloc(cats->n_cats * sizeof(int));
++ cats->cats_idxs =(int *) G_malloc(cats->n_cats * sizeof(int));
++
++ for(i_cat = 0; i_cat < cats->n_cats; i_cat++)
++ cats->cats_idxs[i_cat] = -1;
++
++ return;
++}
++
++/*!
++ \brief Free data of struct scCats, the structure itself remains alocated.
++
++ \param cats pointer to existing scCats struct
++ */
++void I_sc_free_cats(struct scCats * cats)
++{
++ int i_cat;
++
++ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++ {
++ if(cats->cats_arr[i_cat])
++ {
++ G_free(cats->cats_arr[i_cat]->scatt_idxs);
++ G_free(cats->cats_arr[i_cat]->scatts_bands);
++ G_free(cats->cats_arr[i_cat]->scatts_arr);
++ G_free(cats->cats_arr[i_cat]);
++ }
++ }
++
++ G_free(cats->cats_ids);
++ G_free(cats->cats_idxs);
++ G_free(cats->cats_arr);
++
++ cats->n_cats = 0;
++ cats->n_a_cats = 0;
++ cats->n_bands = 0;
++ cats->n_scatts = 0;
++ cats->type = -1;
++
++ return;
++}
++
++#if 0
++void I_sc_get_active_categories(int * a_cats_ids, int * n_a_cats, struct scCats * cats)
++{
++ a_cats_ids = cats->cats_ids;
++ * n_a_cats = cats->n_a_cats;
++}
++#endif
++
++/*!
++ \brief Add category.
++
++ Category represents group of scatter plots.
++
++ \param cats pointer to scCats struct
++
++ \return assigned category id (starts with 0)
++ \return -1 if maximum nuber of categories was reached
++ */
++int I_sc_add_cat(struct scCats * cats)
++{
++ int i_scatt, i_cat_id, cat_id;
++ int n_a_cats = cats->n_a_cats;
++
++ if(cats->n_a_cats >= cats->n_cats)
++ return -1;
++
++ for(i_cat_id = 0; i_cat_id < cats->n_cats; i_cat_id++)
++ if(cats->cats_idxs[i_cat_id] < 0) {
++ cat_id = i_cat_id;
++ break;
++ }
++
++ cats->cats_ids[n_a_cats] = cat_id;
++ cats->cats_idxs[cat_id] = n_a_cats;
++
++ cats->cats_arr[n_a_cats] = (struct scScatts *) G_malloc(sizeof(struct scScatts));
++
++ cats->cats_arr[n_a_cats]->scatts_arr = (struct scdScattData **) G_malloc(cats->n_scatts * sizeof(struct scdScattData *));
++ memset((cats->cats_arr[n_a_cats]->scatts_arr), 0, cats->n_scatts * sizeof(struct scdScattData *));
++
++ cats->cats_arr[n_a_cats]->n_a_scatts = 0;
++
++ cats->cats_arr[n_a_cats]->scatts_bands = (int *) G_malloc(cats->n_scatts * 2 * sizeof(int));
++
++ cats->cats_arr[n_a_cats]->scatt_idxs = (int *) G_malloc(cats->n_scatts * sizeof(int));
++ for(i_scatt = 0; i_scatt < cats->n_scatts; i_scatt++)
++ cats->cats_arr[n_a_cats]->scatt_idxs[i_scatt] = -1;
++
++ ++cats->n_a_cats;
++
++ return cat_id;
++}
++
++#if 0
++int I_sc_delete_cat(struct scCats * cats, int cat_id)
++{
++ int cat_idx, i_cat;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ G_free(cats->cats_arr[cat_idx]->scatt_idxs);
++ G_free(cats->cats_arr[cat_idx]->scatts_bands);
++ G_free(cats->cats_arr[cat_idx]->scatts_arr);
++ G_free(cats->cats_arr[cat_idx]);
++
++ for(i_cat = cat_idx; i_cat < cats->n_a_cats - 1; i_cat++)
++ {
++ cats->cats_arr[i_cat] = cats->cats_arr[i_cat + 1];
++ cats->cats_ids[i_cat] = cats->cats_ids[i_cat + 1];
++ }
++ cats->cats_idxs[cat_id] = -1;
++
++ --cats->n_a_cats;
++
++ return 0;
++}
++#endif
++
++/*!
++ \brief Insert scatter plot data .
++ Inserted scatt_data struct must have same type as cats struct (SC_SCATT_DATA or SC_SCATT_CONDITIONS).
++
++ \param cats pointer to scCats struct
++ \param cat_id id number of category.
++ \param scatt_id id number of scatter plot.
++
++ \return 0 on success
++ \return -1 on failure
++ */
++int I_sc_insert_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++ int band_1, band_2, cat_idx, n_a_scatts;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 || cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 && scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] >= 0)
++ return -1;
++
++ if(!scatt_data->b_conds_arr && cats->type == SC_SCATT_CONDITIONS)
++ return -1;
++
++ if(!scatt_data->scatt_vals_arr && cats->type == SC_SCATT_DATA)
++ return -1;
++
++ n_a_scatts = scatts->n_a_scatts;
++
++ scatts->scatt_idxs[scatt_id] = n_a_scatts;
++
++ I_id_scatt_to_bands(scatt_id, cats->n_bands, &band_1, &band_2);
++
++ scatts->scatts_bands[n_a_scatts * 2] = band_1;
++ scatts->scatts_bands[n_a_scatts * 2 + 1] = band_2;
++
++ scatts->scatts_arr[n_a_scatts] = scatt_data;
++ ++scatts->n_a_scatts;
++
++ return 0;
++}
++
++#if 0
++int I_sc_remove_scatt_data(struct scCats * cats, struct scdScattData * scatt_data, int cat_id, int scatt_id)
++{
++ int cat_idx, scatt_idx, n_init_scatts, i_scatt;
++ struct scScatts * scatts;
++
++ if(cat_id < 0 && cat_id >= cats->n_cats)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(scatt_id < 0 || scatt_id >= cats->n_scatts)
++ return -1;
++
++ scatts = cats->cats_arr[cat_idx];
++ if(scatts->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ scatt_data = scatts->scatts_arr[scatt_idx];
++
++ for(i_scatt = scatt_idx; i_scatt < scatts->n_a_scatts - 1; i_scatt++)
++ {
++ scatts->scatts_arr[i_scatt] = scatts->scatts_arr[i_scatt + 1];
++ scatts->scatts_bands[i_scatt * 2] = scatts->scatts_bands[(i_scatt + 1)* 2];
++ scatts->scatts_bands[i_scatt * 2 + 1] = scatts->scatts_bands[(i_scatt + 1) * 2 + 1];
++ }
++ scatts->scatts_arr[scatts->n_a_scatts] = NULL;
++
++ scatts->scatt_idxs[scatt_id] = -1;
++
++ scatt_data = scatts->scatts_arr[scatt_id];
++ scatts->n_a_scatts--;
++
++ return 0;
++}
++
++int I_sc_set_value(struct scCats * cats, int cat_id, int scatt_id, int value_idx, int value)
++{
++ int n_a_scatts = cats->cats_arr[cat_id]->n_a_scatts;
++ int cat_idx, scatt_idx, ret;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ if(cat_idx < 0)
++ return -1;
++
++ if(cats->cats_arr[cat_idx]->scatt_idxs[scatt_id] < 0)
++ return -1;
++
++ cat_idx = cats->cats_idxs[cat_id];
++ scatt_idx = cats->cats_arr[cat_idx]->scatt_idxs[scatt_id];
++
++ I_scd_set_value(cats->cats_arr[cat_idx]->scatts_arr[scatt_idx], value_idx, value);
++
++ return 0;
++}
++#endif
++
++/*!
++ \brief Insert scatter plot data.
++
++ \param scatt_data pointer to existing struct scdScattData
++ \param type SC_SCATT_DATA for scatter plots or SC_SCATT_CONDITIONS for selected areas in scatter plot
++ \param n_vals number of data values
++ \param data array of values (unsigned char for SC_SCATT_CONDITIONS, unsigned int for SC_SCATT_DATA)
++ */
++void I_scd_init_scatt_data(struct scdScattData * scatt_data, int type, int n_vals, void * data)
++{
++ scatt_data->n_vals = n_vals;
++
++ if(type == SC_SCATT_DATA)
++ {
++ if(data)
++ scatt_data->scatt_vals_arr = (unsigned int *) data;
++ else {
++ scatt_data->scatt_vals_arr = (unsigned int *) G_malloc(n_vals * sizeof(unsigned int));
++ memset(scatt_data->scatt_vals_arr, 0, n_vals * sizeof(unsigned int));
++ }
++ scatt_data->b_conds_arr = NULL;
++ }
++ else if(type == SC_SCATT_CONDITIONS)
++ {
++ if(data)
++ scatt_data->b_conds_arr = (unsigned char *) data;
++ else {
++ scatt_data->b_conds_arr = (unsigned char *) G_malloc(n_vals * sizeof(unsigned char));
++ memset(scatt_data->b_conds_arr, 0, n_vals * sizeof(unsigned char));
++ }
++ scatt_data->scatt_vals_arr = NULL;
++ }
++
++ return;
++}
++
++
++#if 0
++void I_scd_get_range_min_max(struct scdScattData * scatt_data, CELL * band_1_min, CELL * band_1_max, CELL * band_2_min, CELL * band_2_max)
++{
++
++ Rast_get_range_min_max(&(scatt_data->band_1_range), band_1_min, band_2_min);
++ Rast_get_range_min_max(&(scatt_data->band_2_range), band_2_min, band_2_max);
++
++ return;
++}
++s
++void * I_scd_get_data_ptr(struct scdScattData * scatt_data)
++{
++ if(!scatt_data->b_conds_arr)
++ return scatt_data->b_conds_arr;
++ else if(!scatt_data->scatt_vals_arr)
++ return scatt_data->scatt_vals_arr;
++
++ return NULL;
++}
++
++int I_scd_set_value(struct scdScattData * scatt_data, unsigned int val_idx, unsigned int val)
++{
++ if(val_idx < 0 && val_idx > scatt_data->n_vals)
++ return -1;
++
++ if(scatt_data->b_conds_arr)
++ scatt_data->b_conds_arr[val_idx] = val;
++ else if(scatt_data->scatt_vals_arr)
++ scatt_data->scatt_vals_arr[val_idx] = val;
++ else
++ return -1;
++
++ return 0;
++}
++#endif
+Index: lib/imagery/scatt.c
+===================================================================
+--- lib/imagery/scatt.c (revision 0)
++++ lib/imagery/scatt.c (working copy)
+@@ -0,0 +1,871 @@
++/*!
++ \file lib/imagery/scatt.c
++
++ \brief Imagery library - functions for wx Scatter Plot Tool.
++
++ Low level functions used by wx Scatter Plot Tool.
++
++ Copyright (C) 2013 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 Stepan Turek <stepan.turek at seznam.cz> (Mentor: Martin Landa)
++ */
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.h>
++
++#include <grass/gis.h>
++#include <grass/vector.h>
++#include <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/glocale.h>
++
++#include "iclass_local_proto.h"
++
++struct rast_row
++{
++ CELL * row;
++ char * null_row;
++ struct Range rast_range; /*Range of whole raster.*/
++};
++
++/*!
++ \brief Create pgm header.
++
++ Scatter plot internally generates pgm files. These pgms have header in format created by this function.
++
++ \param region region to be pgm header generated for
++ \param [out] header header of pgm file
++ */
++static int get_cat_rast_header(struct Cell_head * region, char * header){
++ return sprintf(header, "P5\n%d\n%d\n1\n", region->cols, region->rows);
++}
++
++/*!
++ \brief Create category raster conditions file.
++
++ \param cat_rast_region region to be file generated for
++ \param cat_rast path of generated category raster file
++ */
++int I_create_cat_rast(struct Cell_head * cat_rast_region, const char * cat_rast)
++{
++ FILE * f_cat_rast;
++ char cat_rast_header[1024];//TODO magic number
++ int i_row, i_col;
++ int head_nchars;
++
++ unsigned char * row_data;
++
++ f_cat_rast = fopen(cat_rast, "wb");
++ if(!f_cat_rast) {
++ G_warning("Unable to create category raster condition file <%s>.", cat_rast);
++ return -1;
++ }
++
++ head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
++
++ fwrite(cat_rast_header, sizeof(char), head_nchars/sizeof(char), f_cat_rast);
++ if (ferror(f_cat_rast)){
++ fclose(f_cat_rast);
++ G_warning(_("Unable to write header into category raster condition file <%s>."), cat_rast);
++ return -1;
++ }
++
++ row_data = (unsigned char *) G_malloc(cat_rast_region->cols * sizeof(unsigned char));
++ for(i_col = 0; i_col < cat_rast_region->cols; i_col++)
++ row_data[i_col] = 0 & 255;
++
++ for(i_row = 0; i_row < cat_rast_region->rows; i_row++) {
++ fwrite(row_data, sizeof(unsigned char), (cat_rast_region->cols)/sizeof(unsigned char), f_cat_rast);
++ if (ferror(f_cat_rast))
++ {
++ fclose(f_cat_rast);
++ G_warning(_("Unable to write into category raster condition file <%s>."), cat_rast);
++ return -1;
++ }
++ }
++
++ fclose(f_cat_rast);
++ return 0;
++}
++
++static int print_reg(struct Cell_head * intersec, const char * pref, int dbg_level)
++{
++ G_debug(dbg_level, "%s:\n n:%f\ns:%f\ne:%f\nw:%f\nns_res:%f\new_res:%f", pref, intersec->north, intersec->south,
++ intersec->east, intersec->west, intersec->ns_res, intersec->ew_res);
++}
++
++/*!
++ \brief Find intersection region of two regions.
++
++ \param A pointer to intersected region
++ \param B pointer to intersected region
++ \param [out] intersec pointer to intersection region of regions A B (relevant params of the region are: south, north, east, west)
++
++ \return 0 if interection exists
++ \return -1 if regions does not intersect
++ */
++static int regions_intersecion(struct Cell_head * A, struct Cell_head * B, struct Cell_head * intersec)
++{
++
++ if(B->north < A->south) return -1;
++ else if(B->north > A->north) intersec->north = A->north;
++ else intersec->north = B->north;
++
++ if(B->south > A->north) return -1;
++ else if(B->south < A->south) intersec->south = A->south;
++ else intersec->south = B->south;
++
++ if(B->east < A->west) return -1;
++ else if(B->east > A->east) intersec->east = A->east;
++ else intersec->east = B->east;
++
++ if(B->west > A->east) return -1;
++ else if(B->west < A->west) intersec->west = A->west;
++ else intersec->west = B->west;
++
++ if(intersec->north == intersec->south) return -1;
++
++ if(intersec->east == intersec->west) return -1;
++
++ return 0;
++
++}
++
++/*!
++ \brief Get rows and cols numbers, which defines intersection of the regions.
++
++ \param A pointer to intersected region
++ \param B pointer to intersected region (A and B must have same resolution)
++ \param [out] A_bounds rows and cols numbers of A stored in south, north, east, west, which defines intersection of A and B
++ \param [out] B_bounds rows and cols numbers of B stored in south, north, east, west, which defines intersection of A and B
++
++ \return 0 if interection exists
++ \return -1 if regions do not intersect
++ \return -2 resolution of regions is not same
++*/
++static int get_rows_and_cols_bounds(struct Cell_head * A, struct Cell_head * B, struct Cell_head * A_bounds, struct Cell_head * B_bounds)
++{
++ float ns_res, ew_res;
++
++ struct Cell_head intersec;
++
++ /* TODO is it right check? */
++ if(abs(A->ns_res - B->ns_res) > GRASS_EPSILON) {
++ G_debug(0, "'get_rows_and_cols_bounds' ns_res does not fit, A->ns_res: %f B->ns_res: %f", A->ns_res, B->ns_res);
++ return -2;
++ }
++
++ if(abs(A->ew_res - B->ew_res) > GRASS_EPSILON) {
++ G_debug(0, "'get_rows_and_cols_bounds' ew_res does not fit, A->ew_res: %f B->ew_res: %f", A->ew_res, B->ew_res);
++ return -2;
++ }
++
++ ns_res = A->ns_res;
++ ew_res = A->ew_res;
++
++ if(regions_intersecion(A, B, &intersec) == -1)
++ return -1;
++
++ A_bounds->north = ceil((A->north - intersec.north - ns_res * 0.5) / ns_res);
++ A_bounds->south = ceil((A->north - intersec.south - ns_res * 0.5) / ns_res);
++
++ A_bounds->east = ceil((intersec.east - A->west - ew_res * 0.5) / ew_res);
++ A_bounds->west = ceil((intersec.west - A->west - ew_res * 0.5) / ew_res);
++
++ B_bounds->north = ceil((B->north - intersec.north - ns_res * 0.5) / ns_res);
++ B_bounds->south = ceil((B->north - intersec.south - ns_res * 0.5) / ns_res);
++
++ B_bounds->east = ceil((intersec.east - B->west - ew_res * 0.5) / ew_res);
++ B_bounds->west = ceil((intersec.west - B->west - ew_res * 0.5) / ew_res);
++
++ return 0;
++}
++
++/*!
++ \brief Insert raster map patch into pgm file.
++ Warning: calls Rast_set_window
++
++ \param patch_rast name of raster map
++ \param cat_rast_region region of category raster file
++ \param cat_rast path to category raster file
++
++ \return 0 on success
++ \return -1 on failure
++*/
++int I_insert_patch_to_cat_rast(const char * patch_rast, struct Cell_head * cat_rast_region, const char * cat_rast)
++{
++
++ FILE * f_cat_rast;
++ struct Cell_head patch_region, patch_bounds, cat_rast_bounds;
++ char cat_rast_header[1024];//TODO magic number
++ int i_row, i_col, ncols, nrows, cat_rast_col, patch_col, val;
++ int head_nchars, ret;
++ int fd_patch_rast, init_shift, step_shift;
++ unsigned char * patch_data;
++
++ char * null_chunk_row;
++
++ const char *mapset;
++
++ struct Cell_head patch_lines, cat_rast_lines;
++
++ unsigned char * row_data;
++
++ f_cat_rast = fopen(cat_rast, "rb+");
++ if(!f_cat_rast) {
++ G_warning(_("Unable to open category raster condtions file <%s>."), cat_rast);
++ return -1;
++ }
++
++ head_nchars = get_cat_rast_header(cat_rast_region, cat_rast_header);
++ if ((mapset = G_find_raster(patch_rast,"")) == NULL) {
++ fclose(f_cat_rast);
++ G_warning(_("Unable to find patch raster <%s>."), patch_rast);
++ return -1;
++ }
++
++ Rast_get_cellhd(patch_rast, mapset, &patch_region);
++ Rast_set_window(&patch_region);
++
++ if ((fd_patch_rast = Rast_open_old(patch_rast, mapset)) < 0) {
++ fclose(f_cat_rast);
++ return -1;
++ }
++
++ ret = get_rows_and_cols_bounds(cat_rast_region, &patch_region, &cat_rast_bounds, &patch_bounds);
++ if(ret == -2) {
++ G_warning(_("Resolutions of patch <%s> and patched file <%s> are not same."), patch_rast, cat_rast);
++
++ Rast_close(fd_patch_rast);
++ fclose(f_cat_rast);
++
++ return -1;
++ }
++ else if (ret == -1){
++
++ Rast_close(fd_patch_rast);
++ fclose(f_cat_rast);
++
++ return 0;
++ }
++
++ ncols = cat_rast_bounds.east - cat_rast_bounds.west;
++ nrows = cat_rast_bounds.south - cat_rast_bounds.north;
++
++ patch_data = (unsigned char *) G_malloc(ncols * sizeof(unsigned char));
++
++ init_shift = head_nchars + cat_rast_region->cols * cat_rast_bounds.north + cat_rast_bounds.west;
++
++ if(fseek(f_cat_rast, init_shift, SEEK_SET) != 0) {
++ G_warning(_("Corrupted category raster conditions file <%s> (fseek failed)"), cat_rast);
++
++ Rast_close(fd_patch_rast);
++ G_free(null_chunk_row);
++ fclose(f_cat_rast);
++
++ return -1;
++ }
++
++ step_shift = cat_rast_region->cols - ncols;
++
++ null_chunk_row = Rast_allocate_null_buf();
++
++ for(i_row = 0; i_row < nrows; i_row++) {
++ Rast_get_null_value_row (fd_patch_rast, null_chunk_row, i_row + patch_bounds.north);
++
++ for(i_col = 0; i_col < ncols; i_col++) {
++ patch_col = patch_bounds.west + i_col;
++
++ if(null_chunk_row[patch_col] != 1)
++ patch_data[i_col] = 1 & 255;
++ else {
++ patch_data[i_col] = 0 & 255;
++ }
++ }
++
++ fwrite(patch_data, sizeof(unsigned char), (ncols)/sizeof(unsigned char), f_cat_rast);
++ if (ferror(f_cat_rast))
++ {
++ G_warning(_("Unable to write into category raster conditions file <%s>"), cat_rast);
++
++ Rast_close(fd_patch_rast);
++ G_free(null_chunk_row);
++ fclose(f_cat_rast);
++
++ return -1;
++ }
++ if(fseek(f_cat_rast, step_shift, SEEK_CUR) != 0) {
++ G_warning(_("Corrupted category raster conditions file <%s> (fseek failed)"), cat_rast);
++
++ Rast_close(fd_patch_rast);
++ G_free(null_chunk_row);
++ fclose(f_cat_rast);
++
++ return -1;
++ }
++ }
++
++ Rast_close(fd_patch_rast);
++ G_free(null_chunk_row);
++ fclose(f_cat_rast);
++ return 0;
++}
++
++/*!
++ \brief Updates scatter plots data in category by pixels which meets category conditions.
++
++ \param bands_rows data represents data describig one row from raster band
++ \param belongs_pix array which defines which pixels belongs to category (1 value) and which not (0 value)
++ \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, which are modified according to values in belongs_pix
++*/
++static inline void update_cat_scatt_plts(struct rast_row * bands_rows, unsigned short * belongs_pix, struct scScatts * scatts)
++{
++ int band_axis_1, band_axis_2, i_scatt, array_idx, cat_idx, i_chunk_rows_pix, max_arr_idx;
++
++ CELL * b_1_row;
++ CELL * b_2_row;
++ char * b_1_null_row,* b_2_null_row;
++ struct rast_row b_1_rast_row, b_2_rast_row;
++
++ struct Range b_1_range, b_2_range;
++ int b_1_range_size;
++
++ int row_size = Rast_window_cols();
++
++ int * scatts_bands = scatts->scatts_bands;
++
++ for(i_scatt = 0; i_scatt < scatts->n_a_scatts; i_scatt++)
++ {
++ b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
++ b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
++
++ b_1_row = b_1_rast_row.row;
++ b_2_row = b_2_rast_row.row;
++
++ b_1_null_row = b_1_rast_row.null_row;
++ b_2_null_row = b_2_rast_row.null_row;
++
++ b_1_range = b_1_rast_row.rast_range;
++ b_2_range = b_2_rast_row.rast_range;
++
++ b_1_range_size = b_1_range.max - b_1_range.min + 1;
++ max_arr_idx = (b_1_range.max - b_1_range.min + 1) * (b_2_range.max - b_2_range.min + 1);
++
++ for(i_chunk_rows_pix = 0; i_chunk_rows_pix < row_size; i_chunk_rows_pix++)
++ {
++ /* pixel does not belongs to scatter plot or has null value in one of the bands */
++ if(!belongs_pix[i_chunk_rows_pix] ||
++ b_1_null_row[i_chunk_rows_pix] == 1 ||
++ b_2_null_row[i_chunk_rows_pix] == 1)
++ continue;
++
++ /* index in scatter plot array */
++ array_idx = b_1_row[i_chunk_rows_pix] - b_1_range.min + (b_2_row[i_chunk_rows_pix] - b_2_range.min) * b_1_range_size;
++
++ if(array_idx < 0 || array_idx >= max_arr_idx) {
++ G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
++ continue;
++ }
++
++ /* increment scatter plot value */
++ ++scatts->scatts_arr[i_scatt]->scatt_vals_arr[array_idx];
++ }
++ }
++}
++
++/*!
++ \brief Computes scatter plots data from chunk_rows.
++
++ \param scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
++ \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are selected areas (condtitions)stored
++
++ \param chunk_rows data arrays of chunk_rows from analyzed raster bands (all data in chunk_rows, null_chunk_rows and belongs_pix arrays represents same region)
++ \param null_chunk_rows null data arrays of chunk_rows from analyzed raster bands
++ \param row_size size of data in chunk_rows, null_chunk_rows and belongs_pix arrays
++ \param f_cats_rasts_conds file which stores selected areas (conditions) from mapwindow see I_create_cat_rast() and I_pa
++ \param fd_cats_rasts array of openedraster maps which represents all selected pixels for category
++ \param region analysis region
++
++ \return 0 on success
++ \return -1 on failure
++*/
++static inline int compute_scatts_from_chunk_row(struct Cell_head *region, struct scCats * scatt_conds,
++ FILE ** f_cats_rasts_conds, struct rast_row * bands_rows,
++ struct scCats * scatts, int * fd_cats_rasts)
++{
++
++ int i_rows_pix, i_cat, i_scatt, n_a_scatts, n_pixs;
++ int cat_id, scatt_plts_cat_idx, array_idx, max_arr_idx;
++ char * b_1_null_row,* b_2_null_row;
++ struct rast_row b_1_rast_row, b_2_rast_row;
++ CELL * cat_rast_row;
++
++ struct scScatts * scatts_conds;
++ struct scScatts * scatts_scatt_plts;
++ struct scdScattData * conds;
++
++ struct Range b_1_range, b_2_range;
++ int b_1_range_size;
++
++ int * scatts_bands;
++ struct scdScattData ** scatts_arr;
++
++ CELL * b_1_row;
++ CELL * b_2_row;
++ unsigned char * i_scatt_conds;
++
++ int row_size = Rast_window_cols();
++
++ unsigned short * belongs_pix = (unsigned short *) G_malloc(row_size * sizeof(unsigned short));
++ unsigned char * rast_pixs = (unsigned char *) G_malloc(row_size * sizeof(unsigned char));
++ cat_rast_row = Rast_allocate_c_buf();
++
++
++ for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
++ {
++ scatts_conds = scatt_conds->cats_arr[i_cat];
++
++ cat_id = scatt_conds->cats_ids[i_cat];
++
++ scatt_plts_cat_idx = scatts->cats_idxs[cat_id];
++ if(scatt_plts_cat_idx < 0)
++ continue;
++ scatts_scatt_plts = scatts->cats_arr[scatt_plts_cat_idx];
++
++ G_zero(belongs_pix, row_size * sizeof(unsigned short));
++
++ /* if category has no conditions defined, scatter plots without any constraint are computed (default scatter plots) */
++ if(!scatts_conds->n_a_scatts && !f_cats_rasts_conds[i_cat]) {
++ for(i_scatt = 0; i_scatt < scatts_scatt_plts->n_a_scatts; i_scatt++)
++ {
++ /* all pixels belongs */
++ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
++ belongs_pix[i_rows_pix] = 1;
++ }
++ }
++ /* compute belonging pixels for defined conditions */
++ else
++ {
++ scatts_bands = scatts_conds->scatts_bands;
++
++ /* check conditions from category raster condtitions file */
++ if(f_cats_rasts_conds[i_cat]) {
++ n_pixs = fread(rast_pixs, sizeof(unsigned char), (row_size)/sizeof(unsigned char), f_cats_rasts_conds[i_cat]);
++
++ if (ferror(f_cats_rasts_conds[i_cat]))
++ {
++ G_free(rast_pixs);
++ G_free(belongs_pix);
++ G_warning(_("Unable to read from category raster condtition file."));
++ return -1;
++ }
++ if (n_pixs != n_pixs) {
++ G_free(rast_pixs);
++ G_free(belongs_pix);
++ G_warning(_("Invalid size of category raster conditions file."));
++ return -1;
++
++ }
++
++ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
++ {
++ if(rast_pixs[i_rows_pix] != 0 & 255)
++ belongs_pix[i_rows_pix] = 1;
++ }
++ }
++
++ /* check condtions defined in scatter plots*/
++ for(i_scatt = 0; i_scatt < scatts_conds->n_a_scatts; i_scatt++)
++ {
++ b_1_rast_row = bands_rows[scatts_bands[i_scatt * 2]];
++ b_2_rast_row = bands_rows[scatts_bands[i_scatt * 2 + 1]];
++
++ b_1_row = b_1_rast_row.row;
++ b_2_row = b_2_rast_row.row;
++
++ b_1_null_row = b_1_rast_row.null_row;
++ b_2_null_row = b_2_rast_row.null_row;
++
++ b_1_range = b_1_rast_row.rast_range;
++ b_2_range = b_2_rast_row.rast_range;
++
++ b_1_range_size = b_1_range.max - b_1_range.min + 1;
++ max_arr_idx = (b_1_range.max - b_1_range.min + 1) * (b_2_range.max - b_2_range.min + 1);
++
++ i_scatt_conds = scatts_conds->scatts_arr[i_scatt]->b_conds_arr;
++
++ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
++ {
++ /* pixels already belongs to category from category raster conditions file or contains null value in one of the bands */
++ if(belongs_pix[i_rows_pix] ||
++ b_1_null_row[i_rows_pix] == 1 ||
++ b_2_null_row[i_rows_pix] == 1)
++ continue;
++
++ array_idx = b_1_row[i_rows_pix] - b_1_range.min + (b_2_row[i_rows_pix] - b_2_range.min) * b_1_range_size;
++ if(array_idx < 0 || array_idx >= max_arr_idx) {
++ G_warning ("Data inconsistent. Value computed for scatter plot is out of initialized range.");
++ continue;
++ }
++ /* pixels meets condtion defined in scatter plot */
++ if(i_scatt_conds[array_idx])
++ belongs_pix[i_rows_pix] = 1;
++ }
++ }
++ }
++
++ /* update category raster with belonging pixels */
++ if(fd_cats_rasts[i_cat] >= 0) {
++ Rast_set_null_value(cat_rast_row, Rast_window_cols(), CELL_TYPE);
++
++ for(i_rows_pix = 0; i_rows_pix < row_size; i_rows_pix++)
++ if(belongs_pix[i_rows_pix])
++ cat_rast_row[i_rows_pix] = belongs_pix[i_rows_pix];
++
++ Rast_put_c_row (fd_cats_rasts[i_cat], cat_rast_row);
++ }
++
++ /* update scatter plots with belonging pixels */
++ update_cat_scatt_plts(bands_rows, belongs_pix, scatts_scatt_plts);
++ }
++
++ G_free(cat_rast_row);
++ G_free(rast_pixs);
++ G_free(belongs_pix);
++
++ return 0;
++}
++
++/*!
++ \brief Get list if bands needed to be opened for analysis from scCats struct.
++*/
++static void get_needed_bands(struct scCats * cats, int * b_needed_bands)
++{
++ // results in b_needed_bands - array of bools - if item has value 1, band (defined by item index) is needed to be opened
++ int i_cat, i_scatt, cat_id;
++
++ for(i_cat = 0; i_cat < cats->n_a_cats; i_cat++)
++ {
++ for(i_scatt = 0; i_scatt < cats->cats_arr[i_cat]->n_a_scatts; i_scatt++)
++ {
++ G_debug(3, "Active scatt %d in catt %d", i_scatt, i_cat);
++
++ b_needed_bands[cats->cats_arr[i_cat]->scatts_bands[i_scatt * 2]] = 1;
++ b_needed_bands[cats->cats_arr[i_cat]->scatts_bands[i_scatt * 2 + 1]] = 1;
++ }
++ }
++ return;
++}
++
++/*!
++ \brief Helper function for clean up.
++*/
++static void free_compute_scatts_data(int * fd_bands, struct rast_row * bands_rows, int n_a_bands, int * bands_ids,
++ int * fd_cats_rasts, FILE ** f_cats_rasts_conds, int n_a_cats)
++{
++ int i, band_id;
++
++ for(i = 0; i < n_a_bands; i++)
++ {
++ band_id = bands_ids[i];
++ if(band_id >= 0) {
++ Rast_close(fd_bands[i]);
++ G_free(bands_rows[band_id].row);
++ G_free(bands_rows[band_id].null_row);
++ }
++ }
++
++ if(f_cats_rasts_conds)
++ for(i = 0; i < n_a_cats; i++)
++ if(f_cats_rasts_conds[i])
++ fclose(f_cats_rasts_conds[i]);
++
++ if(fd_cats_rasts)
++ for(i = 0; i < n_a_cats; i++)
++ if(fd_cats_rasts[i] >= 0)
++ Rast_close(fd_cats_rasts[i]);
++
++}
++
++/*!
++ \brief Compute scatter plots data.
++
++ If category has not defined no category raster condition file and no scatter plot with consdtion,
++ default scatter plot is computed.
++ Warning: calls Rast_set_window
++
++ \param region analysis region, beaware that all input data must be prepared for this region (bands (their ranges), cats_rasts_conds rasters...)
++ \param region function calls Rast_set_window for this region
++ \param scatt_conds pointer to scScatts struct of type SC_SCATT_CONDITIONS, where are stored selected areas (conditions) in scatter plots
++ \param cats_rasts_conds paths to category raster conditions files representing selected areas (conditions) in rasters for every category
++ \param cats_rasts_conds index in array represents corresponding category id
++ \param cats_rasts_conds for manupulation with category raster conditions file see also I_id_scatt_to_bands() and I_insert_patch_to_cat_rast()
++ \param bands names of analyzed bands, order of bands is defined by their id
++ \param n_bands number of bands
++ \param [out] scatts pointer to scScatts struct of type SC_SCATT_DATA, where are computed scatter plots stored
++ \param [out] cats_rasts array of raster maps names where will be stored all selected pixels for every category
++
++ \return 0 on success
++ \return -1 on failure
++*/
++int I_compute_scatts(struct Cell_head *region, struct scCats * scatt_conds, const char ** cats_rasts_conds,
++ const char ** bands, int n_bands, struct scCats * scatts, const char ** cats_rasts)
++{
++ const char *mapset;
++ char header[1024];
++
++ int fd_cats_rasts[scatt_conds->n_a_cats];
++ FILE * f_cats_rasts_conds[scatt_conds->n_a_cats];
++
++ struct rast_row bands_rows[n_bands];
++
++ RASTER_MAP_TYPE data_type;
++
++ int nrows, i_band, n_a_bands, band_id,
++ i, i_row, head_nchars, i_cat, id_cat;
++
++ int fd_bands[n_bands];
++ int bands_ids[n_bands];
++ int b_needed_bands[n_bands];
++
++ Rast_set_window(region);
++
++ for(i_band = 0; i_band < n_bands; i_band++)
++ fd_bands[i_band] = -1;
++
++ for(i_band = 0; i_band < n_bands; i_band++)
++ bands_ids[i_band] = -1;
++
++ if (n_bands != scatts->n_bands ||
++ n_bands != scatt_conds->n_bands)
++ return -1;
++
++ memset(b_needed_bands, 0, (size_t)n_bands * sizeof(int));
++
++ get_needed_bands(scatt_conds, &b_needed_bands[0]);
++ get_needed_bands(scatts, &b_needed_bands[0]);
++
++ n_a_bands = 0;
++
++ /* open band rasters, which are needed for computation */
++ for(band_id = 0; band_id < n_bands; band_id++)
++ {
++ if(b_needed_bands[band_id])
++ {
++ G_debug(3, "Opening raster no. %d with name: %s", band_id, bands[band_id]);
++
++ if ((mapset = G_find_raster2(bands[band_id],"")) == NULL) {
++ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
++ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
++ G_warning(_("Unbale to read find raster <%s>"), bands[band_id]);
++ return -1;
++ }
++
++ if ((fd_bands[n_a_bands] = Rast_open_old(bands[band_id], mapset)) < 0) {
++ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
++ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
++ G_warning(_("Unbale to open raster <%s>"), bands[band_id]);
++ return -1;
++ }
++
++ data_type = Rast_get_map_type(fd_bands[n_a_bands]);
++ if(data_type != CELL_TYPE) {
++ G_warning(_("Raster <%s> type is not <%s>"), bands[band_id], "CELL");
++ return -1;
++ }
++
++ bands_rows[band_id].row = Rast_allocate_c_buf();
++ bands_rows[band_id].null_row = Rast_allocate_null_buf();
++
++ if(Rast_read_range(bands[band_id], mapset, &bands_rows[band_id].rast_range) != 1){
++ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands,
++ bands_ids, NULL, NULL, scatt_conds->n_a_cats);
++ G_warning(_("Unable to read range of raster <%s>"), bands[band_id]);
++ return -1;
++ }
++
++ bands_ids[n_a_bands] = band_id;
++ ++n_a_bands;
++ }
++ }
++
++ /* open category rasters condition files and category rasters */
++ for(i_cat = 0; i_cat < scatts->n_a_cats; i_cat++)
++ {
++ id_cat = scatts->cats_ids[i_cat];
++ if(cats_rasts[id_cat]) {
++ fd_cats_rasts[i_cat] = Rast_open_new(cats_rasts[id_cat], CELL_TYPE);
++ }
++ else
++ fd_cats_rasts[i_cat] = -1;
++
++ if(cats_rasts_conds[id_cat]) {
++ f_cats_rasts_conds[i_cat] = fopen(cats_rasts_conds[id_cat], "r");
++ if(!f_cats_rasts_conds[i_cat]) {
++ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids,
++ f_cats_rasts_conds, f_cats_rasts_conds, scatt_conds->n_a_cats);
++ G_warning(_("Unable to open category raster condtition file <%s>"), bands[band_id]);
++ return -1;
++ }
++ }
++ else
++ f_cats_rasts_conds[i_cat] = NULL;
++ }
++
++ head_nchars = get_cat_rast_header(region, header);
++ for(i_cat = 0; i_cat < scatt_conds->n_a_cats; i_cat++)
++ if(f_cats_rasts_conds[i_cat])
++ if( fseek(f_cats_rasts_conds[i_cat] , head_nchars, SEEK_SET) != 0) {
++ G_warning(_("Corrupted category raster conditions file (fseek failed)"));
++ return -1;
++ }
++
++ nrows = Rast_window_rows();
++
++ /* analyze bands by rows */
++ for (i_row = 0; i_row < nrows; i_row++)
++ {
++ for(i_band = 0; i_band < n_a_bands; i_band++)
++ {
++ band_id = bands_ids[i_band];
++ Rast_get_c_row(fd_bands[i_band], bands_rows[band_id].row, i_row);
++ Rast_get_null_value_row (fd_bands[i_band], bands_rows[band_id].null_row, i_row);
++ }
++ if(compute_scatts_from_chunk_row(region, scatt_conds, f_cats_rasts_conds, bands_rows, scatts, fd_cats_rasts) == -1)
++ {
++ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids, fd_cats_rasts,
++ f_cats_rasts_conds, scatt_conds->n_a_cats);
++ return -1;
++ }
++
++ }
++ free_compute_scatts_data(fd_bands, bands_rows, n_a_bands, bands_ids,
++ fd_cats_rasts, f_cats_rasts_conds, scatt_conds->n_a_cats);
++ return 0;
++}
++
++/*!
++ \brief Merge arrays according to opacity.
++ Every pixel in array must be represented by 4 values (RGBA).
++
++
++ \param merged_arr array which will be overlayd with overlay_arr
++ \param overlay_arr array to be merged_arr overlayed with
++ \param rows number of rows for the both arrays
++ \param cols number of columns for the both arrays
++ \param alpha transparency of the overlay array for merging
++
++ \return 0
++*/
++int I_merge_arrays(unsigned char * merged_arr, unsigned char * overlay_arr, unsigned rows, unsigned cols, double alpha)
++{
++ unsigned int i_row, i_col, i_b;
++ unsigned int row_idx, col_idx, idx;
++ unsigned int c_a_i, c_a;
++
++ for(i_row = 0; i_row < rows; i_row++)
++ {
++ row_idx = i_row * cols;
++ for(i_col = 0; i_col < cols; i_col++)
++ {
++ col_idx = 4 * (row_idx + i_col);
++ idx = col_idx + 3;
++
++ c_a = overlay_arr[idx] * alpha;
++ c_a_i = 255 - c_a;
++
++ merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * 255) / 255;
++
++ for(i_b = 0; i_b < 3; i_b++)
++ {
++ idx = col_idx + i_b;
++ merged_arr[idx] = (c_a_i * (int)merged_arr[idx] + c_a * (int)overlay_arr[idx]) / 255;
++ }
++ }
++ }
++ return 0;
++}
++
++
++int I_apply_colormap(unsigned char * vals, unsigned char * vals_mask, unsigned vals_size, unsigned char * colmap, unsigned char * col_vals){
++ unsigned int i_val;
++ int v, i, i_cm;
++
++ for(i_val = 0; i_val < vals_size; i_val++){
++ i_cm = 4 * i_val;
++
++ v = vals[i_val];
++
++ if(vals_mask && vals_mask[i_val])
++ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[258 * 4 + i];
++ else if(v > 255)
++ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[257 * 4 + i];
++ else if(v < 0)
++ for(i = 0; i < 4; i++) col_vals[i_cm + i] = colmap[256 * 4 + i];
++ else
++ for(i = 0; i < 4; i++){ col_vals[i_cm + i] = colmap[v * 4 + i];
++ }
++ }
++ return 0;
++}
++
++
++int I_rasterize(double * polygon, int pol_n_pts, unsigned char * rast, unsigned char val, struct Cell_head *rast_region){
++ int i;
++ int x0, x1, y;
++ int row, row_idx, i_col;
++
++ IClass_perimeter perimeter;
++
++ struct line_pnts *pol;
++ pol = Vect_new_line_struct();
++
++ for(i = 0; i < pol_n_pts; i++) {
++ Vect_append_point(pol,
++ polygon[i*2],
++ polygon[i*2 + 1],
++ 0.0);
++ }
++
++ Rast_set_window(rast_region);
++
++ make_perimeter(pol, &perimeter, rast_region);
++ for (i = 1; i < perimeter.npoints; i += 2) {
++ y = perimeter.points[i].y;
++ if (y != perimeter.points[i - 1].y) {
++ G_warning(_("prepare_signature: scan line %d has odd number of points."),
++ (i + 1) / 2);
++ return 1;
++ }
++
++ x0 = perimeter.points[i - 1].x;
++ x1 = perimeter.points[i].x;
++
++ if (x0 > x1) {
++ G_warning(_("signature: perimeter points out of order."));
++ return 1;
++ }
++
++ row = (rast_region->rows - y);
++ if(row < 0 || row >= rast_region->rows) {
++ continue;
++ }
++
++ row_idx = rast_region->cols * row;
++
++ for(i_col = x0; i_col <= x1; i_col++) {
++ if(i_col < 0 || i_col >= rast_region->cols) {
++ continue;
++ }
++ rast[row_idx + i_col] = val;
++ }
++ }
++
++ Vect_destroy_line_struct(pol);
++ G_free(perimeter.points);
++ return 0;
++}
More information about the grass-commit
mailing list