[GRASS-SVN] r57644 - sandbox/turek/scatter_plot

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Sep 12 00:27:23 PDT 2013


Author: turek
Date: 2013-09-12 00:27:23 -0700 (Thu, 12 Sep 2013)
New Revision: 57644

Modified:
   sandbox/turek/scatter_plot/testing_patch.diff
Log:
scatter plot: rasterization fixes

Modified: sandbox/turek/scatter_plot/testing_patch.diff
===================================================================
--- sandbox/turek/scatter_plot/testing_patch.diff	2013-09-12 05:14:09 UTC (rev 57643)
+++ sandbox/turek/scatter_plot/testing_patch.diff	2013-09-12 07:27:23 UTC (rev 57644)
@@ -1,3 +1,1232 @@
+Index: lib/vector/Vlib/open.c
+===================================================================
+--- lib/vector/Vlib/open.c	(revision 57633)
++++ lib/vector/Vlib/open.c	(working copy)
+@@ -240,7 +240,9 @@
+         }
+         else {
+             char file_path[GPATH_MAX];
+-            
++            /* reduce to current mapset if search path was set */
++            if(strcmp(Map->mapset, "") == 0)
++                Map->mapset = G_store(G_mapset());
+             /* temporary map: reduce to current mapset if search path
+              * was set */
+             if (strcmp(Map->mapset, "") == 0)
+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,799 @@
++/*!
++   \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 <grass/raster.h>
++#include <grass/imagery.h>
++#include <grass/gis.h>
++#include <grass/glocale.h>
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <math.h>
++#include <string.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.
++  
++   \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.
++
++   \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;    
++}
++
++
++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[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;
++}
 Index: include/imagery.h
 ===================================================================
 --- include/imagery.h	(revision 57633)
@@ -103,52 +1332,170 @@
  /* sig.c */
  int I_init_signatures(struct Signature *, int);
  int I_new_signature(struct Signature *);
-Index: gui/wxpython/mapdisp/frame.py
+Index: gui/icons/grass/polygon.png
 ===================================================================
---- gui/wxpython/mapdisp/frame.py	(revision 57633)
-+++ 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']
+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 57633)
++++ 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/iclass/plots.py
+===================================================================
+--- gui/wxpython/iclass/plots.py	(revision 57633)
++++ gui/wxpython/iclass/plots.py	(working copy)
+@@ -19,6 +19,7 @@
+ import wx.lib.plot as plot
+ import wx.lib.scrolledpanel as scrolled
+ from core.utils import _
++from core.gcmd import GError
  
-+    def OnScatterplot2(self, event):
+ class PlotPanel(scrolled.ScrolledPanel):
+     """!Panel for drawing multiple plots.
+@@ -28,7 +29,7 @@
+     for each band and for one category. Coincidence plots show min max range
+     of classes for each band.
+     """
+-    def __init__(self, parent, stats_data):
++    def __init__(self, parent, giface, stats_data):
+         scrolled.ScrolledPanel.__init__(self, parent)
+         
+         self.SetupScrolling(scroll_x = False, scroll_y = True)
+@@ -38,26 +39,71 @@
+         self.stats_data = stats_data
+         self.currentCat = None
+         
++        self._giface = giface
++
+         self.mainSizer = wx.BoxSizer(wx.VERTICAL)
+-        
++
+         self._createControlPanel()
+-        
++        self._createPlotPanel()
++        self._createScatterPlotPanel()
++
+         self.SetSizer(self.mainSizer)
+         self.mainSizer.Fit(self)
+         self.Layout()
+-        
++
++    def _createPlotPanel(self):
++
++        self.canvasPanel = wx.Panel(parent=self)
++        self.mainSizer.Add(item = self.canvasPanel, proportion = 1, flag = wx.EXPAND, border = 0)
++        self.canvasSizer = wx.BoxSizer(wx.VERTICAL)
++        self.canvasPanel.SetSizer(self.canvasSizer)
++
+     def _createControlPanel(self):
+         self.plotSwitch = wx.Choice(self, id = wx.ID_ANY,
+                                      choices = [_("Histograms"),
+-                                                _("Coincident plots")])
++                                                _("Coincident plots"),
++                                                _("Scatter plots")])
+         self.mainSizer.Add(self.plotSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
+         self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
+-        
++    
++    def _createScatterPlotPanel(self):
 +        """!Init interactive scatterplot tools
 +        """
-+        if self.dialogs['scatt_plot']:
-+            self.dialogs['scatt_plot'].Raise()
-+            return
++        try:
++            from scatt_plot.frame import IClassScatterPlotsPanel
++            self.scatt_plot_panel = IClassScatterPlotsPanel(parent=self, 
++                                                            giface=self._giface, 
++                                                            iclass_mapwin = self.parent.GetFirstWindow())
++            self.mainSizer.Add(self.scatt_plot_panel, proportion = 1, flag = wx.EXPAND, border = 0)
++            self.scatt_plot_panel.Hide()
++        except ImportError as e:#TODO
++            self.scatt_error = _("Scatter plot functionality is disabled. Reason:\n" \
++                                 "Unable to import packages needed for scatter plot.\n%s" % e)
++            GError(self.scatt_error)
++            self.scatt_plot_panel = None
 +
-+        from scatt_plot.dialogs import ScattPlotMainDialog
-+        self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface)
+     def OnPlotTypeSelected(self, event):
+         """!Plot type selected"""
++
++        if self.plotSwitch.GetSelection() in [0, 1]:
++            self.SetupScrolling(scroll_x = False, scroll_y = True)
++            if self.scatt_plot_panel:
++                self.scatt_plot_panel.Hide()
++            self.canvasPanel.Show()
++            self.Layout()
++
++        elif self.plotSwitch.GetSelection() == 2:
++            self.SetupScrolling(scroll_x = False, scroll_y = False)
++            if self.scatt_plot_panel:
++                self.scatt_plot_panel.Show()
++            else:
++                GError(self.scatt_error)
++            self.canvasPanel.Hide()
++            self.Layout()
++
+         if self.currentCat is None:
+             return
+-        
++
+         if self.plotSwitch.GetSelection() == 0:
+             stat = self.stats_data.GetStatistics(self.currentCat)
+             if not stat.IsReady():
+@@ -66,7 +112,10 @@
+             self.DrawHistograms(stat)
+         else:
+             self.DrawCoincidencePlots()
+-            
++
++        self.Layout()
++
++
+     def StddevChanged(self):
+         """!Standard deviation multiplier changed, redraw histograms"""
+         if self.plotSwitch.GetSelection() == 0:
+@@ -89,7 +138,7 @@
+             panel.Destroy()
+             
+         self.canvasList = []
+-            
++
+     def ClearPlots(self):
+         """!Clears plot canvases"""
+         for bandIdx in range(len(self.bandList)):
+@@ -104,15 +153,15 @@
+     def CreatePlotCanvases(self):
+         """!Create plot canvases according to the number of bands"""
+         for band in self.bandList:
+-            canvas = plot.PlotCanvas(self)
++            canvas = plot.PlotCanvas(self.canvasPanel)
+             canvas.SetMinSize((-1, 140))
+             canvas.SetFontSizeTitle(10)
+             canvas.SetFontSizeAxis(8)
+             self.canvasList.append(canvas)
+             
+-            self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
+-
+-        self.SetVirtualSize(self.GetBestVirtualSize()) 
++            self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
 +        
-+        self.dialogs['scatt_plot'].CenterOnScreen()
-+        self.dialogs['scatt_plot'].Show()
-+
-     def OnVNet(self, event):
-         """!Dialog for v.net* modules 
-         """
-Index: gui/wxpython/mapdisp/toolbars.py
-===================================================================
---- gui/wxpython/mapdisp/toolbars.py	(revision 57633)
-+++ gui/wxpython/mapdisp/toolbars.py	(working copy)
-@@ -239,7 +239,8 @@
-                       (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["scatter"],     self.parent.OnScatterplot2)))
++        self.SetVirtualSize(self.GetBestVirtualSize())
+         self.Layout()
          
-     def OnDecoration(self, event):
-         """!Decorations overlay menu
+     def UpdatePlots(self, group, currentCat, stats_data):
+@@ -138,7 +187,7 @@
+         
+     def UpdateCategory(self, cat):
+         self.currentCat = cat
+-        
++    
+     def DrawCoincidencePlots(self):
+         """!Draw coincidence plots"""
+         for bandIdx in range(len(self.bandList)):
 Index: gui/wxpython/iclass/dialogs.py
 ===================================================================
 --- gui/wxpython/iclass/dialogs.py	(revision 57633)
@@ -468,155 +1815,1830 @@
  
  def test():
      import core.render as render
-Index: gui/wxpython/iclass/plots.py
+Index: gui/wxpython/mapdisp/toolbars.py
 ===================================================================
---- gui/wxpython/iclass/plots.py	(revision 57633)
-+++ gui/wxpython/iclass/plots.py	(working copy)
-@@ -19,6 +19,7 @@
- import wx.lib.plot as plot
- import wx.lib.scrolledpanel as scrolled
- from core.utils import _
-+from core.gcmd import GError
+--- gui/wxpython/mapdisp/toolbars.py	(revision 57633)
++++ gui/wxpython/mapdisp/toolbars.py	(working copy)
+@@ -239,7 +239,8 @@
+                       (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["scatter"],     self.parent.OnScatterplot2)))
+         
+     def OnDecoration(self, event):
+         """!Decorations overlay menu
+Index: gui/wxpython/mapdisp/frame.py
+===================================================================
+--- gui/wxpython/mapdisp/frame.py	(revision 57633)
++++ 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']
  
- class PlotPanel(scrolled.ScrolledPanel):
-     """!Panel for drawing multiple plots.
-@@ -28,7 +29,7 @@
-     for each band and for one category. Coincidence plots show min max range
-     of classes for each band.
-     """
--    def __init__(self, parent, stats_data):
-+    def __init__(self, parent, giface, stats_data):
-         scrolled.ScrolledPanel.__init__(self, parent)
-         
-         self.SetupScrolling(scroll_x = False, scroll_y = True)
-@@ -38,26 +39,71 @@
-         self.stats_data = stats_data
-         self.currentCat = None
-         
-+        self._giface = giface
++    def OnScatterplot2(self, event):
++        """!Init interactive scatterplot tools
++        """
++        if self.dialogs['scatt_plot']:
++            self.dialogs['scatt_plot'].Raise()
++            return
 +
-         self.mainSizer = wx.BoxSizer(wx.VERTICAL)
--        
++        from scatt_plot.dialogs import ScattPlotMainDialog
++        self.dialogs['scatt_plot'] = ScattPlotMainDialog(parent=self, giface=self._giface)
++        
++        self.dialogs['scatt_plot'].CenterOnScreen()
++        self.dialogs['scatt_plot'].Show()
 +
-         self._createControlPanel()
--        
-+        self._createPlotPanel()
-+        self._createScatterPlotPanel()
+     def OnVNet(self, event):
+         """!Dialog for v.net* modules 
+         """
+Index: gui/wxpython/scatt_plot/plots.py
+===================================================================
+--- gui/wxpython/scatt_plot/plots.py	(revision 0)
++++ gui/wxpython/scatt_plot/plots.py	(working copy)
+@@ -0,0 +1,950 @@
++"""!
++ at package scatt_plot.dialogs
 +
-         self.SetSizer(self.mainSizer)
-         self.mainSizer.Fit(self)
-         self.Layout()
--        
++ at brief Ploting widgets.
 +
-+    def _createPlotPanel(self):
++Classes:
 +
-+        self.canvasPanel = wx.Panel(parent=self)
-+        self.mainSizer.Add(item = self.canvasPanel, proportion = 1, flag = wx.EXPAND, border = 0)
-+        self.canvasSizer = wx.BoxSizer(wx.VERTICAL)
-+        self.canvasPanel.SetSizer(self.canvasSizer)
++(C) 2013 by the GRASS Development Team
 +
-     def _createControlPanel(self):
-         self.plotSwitch = wx.Choice(self, id = wx.ID_ANY,
-                                      choices = [_("Histograms"),
--                                                _("Coincident plots")])
-+                                                _("Coincident plots"),
-+                                                _("Scatter plots")])
-         self.mainSizer.Add(self.plotSwitch, proportion = 0, flag = wx.EXPAND|wx.ALL, border = 5)
-         self.plotSwitch.Bind(wx.EVT_CHOICE, self.OnPlotTypeSelected)
--        
++This program is free software under the GNU General Public License
++(>=v2). Read the file COPYING that comes with GRASS for details.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import wx
++import numpy as np
++
++#TODO testing
++import time
++from multiprocessing import Process, Queue
++
++#TODO
++#from numpy.lib.stride_tricks import as_strided
++from copy import deepcopy
++from scatt_plot.core_c import MergeArrays, ApplyColormap
++from core.settings import UserSettings
++
++try:
++    import matplotlib
++    matplotlib.use('WXAgg')
++    from matplotlib.figure import Figure
++    from matplotlib.backends.backend_wxagg import \
++    FigureCanvasWxAgg as FigCanvas
++    from matplotlib.lines import Line2D
++    from matplotlib.artist import Artist
++    from matplotlib.mlab import dist_point_to_segment
++    from matplotlib.patches import Polygon, Ellipse, Rectangle
++    import matplotlib.image as mi
++    import matplotlib.colors as mcolors
++    import matplotlib.cbook as cbook
++except ImportError as e:
++    raise ImportError(_("Unable to import matplotlib (try to install it).\n%s") % e)
++
++import grass.script as grass
++from grass.pydispatch.signal import Signal
++
++class ScatterPlotWidget(wx.Panel):
++    def __init__(self, parent, scatt_id, scatt_mgr, transpose,
++                 id = wx.ID_ANY):
++
++        wx.Panel.__init__(self, parent, id)
++
++        self.parent = parent
++        self.full_extend = None
++        self.mode = None
++
++        self._createWidgets()
++        self._doLayout()
++        self.scatt_id = scatt_id
++        self.scatt_mgr = scatt_mgr
++
++        self.cidpress = None
++        self.cidrelease = None
++
++        self.transpose = transpose
++
++        self.inverse = False
++
++        self.SetSize((200, 100))
++        self.Layout()
++
++        self.base_scale = 1.2
++        self.Bind(wx.EVT_CLOSE,lambda event : self.CleanUp())
++
++        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
++        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")
++
++        self.contex_menu = ScatterPlotContextMenu(plot = self)
++
++        self.ciddscroll = None
++
++        self.canvas.mpl_connect('motion_notify_event', self.Motion)
++        self.canvas.mpl_connect('button_press_event', self.OnPress)
++        self.canvas.mpl_connect('button_release_event', self.OnRelease)
++        self.canvas.mpl_connect('draw_event', self.draw_callback)
++
++    def draw_callback(self, event):
++        self.polygon_drawer.draw_callback(event)
++        self.axes.draw_artist(self.zoom_rect)
++
++    def _createWidgets(self):
++
++        # Create the mpl Figure and FigCanvas objects. 
++        # 5x4 inches, 100 dots-per-inch
++        #
++        self.dpi = 100
++        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
++        self.fig.autolayout = True
++
++        self.canvas = FigCanvas(self, -1, self.fig)
++        
++        self.axes = self.fig.add_axes([0,0,1,1])
++
++        pol = Polygon(list(zip([0], [0])), animated=True)
++        self.axes.add_patch(pol)
++        self.polygon_drawer = PolygonDrawer(self.axes, pol = pol, empty_pol = True)
++
++        self.zoom_wheel_coords = None
++        self.zoom_rect_coords = None
++        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor = 'none')
++        self.zoom_rect.set_visible(False)
++        self.axes.add_patch(self.zoom_rect)
++
++    def ZoomToExtend(self):
++        if self.full_extend:
++            self.axes.axis(self.full_extend)
++            self.canvas.draw()
++
++    def SetMode(self, mode):
++        self._deactivateMode()
++        if mode == 'zoom':
++            self.ciddscroll = self.canvas.mpl_connect('scroll_event', self.ZoomWheel)
++            self.mode = 'zoom'
++        elif mode == 'zoom_extend':
++            self.mode = 'zoom_extend'
++        elif mode == 'pan':
++            self.mode = 'pan'
++        elif mode:
++            self.polygon_drawer.SetMode(mode)
++
++    def SetSelectionPolygonMode(self, activate):
++        self.polygon_drawer.SetSelectionPolygonMode(activate)
++
++    def _deactivateMode(self):
++        self.mode  = None
++        self.polygon_drawer.SetMode(None)
++
++        if self.ciddscroll:
++            self.canvas.mpl_disconnect(self.ciddscroll)
++
++        self.zoom_rect.set_visible(False)
++        self._stopCategoryEdit()
++
++    def GetCoords(self):
++
++        coords = self.polygon_drawer.GetCoords()
++        if coords is None:
++            return
++
++        if self.transpose:
++            for c in coords:
++                tmp = c[0]
++                c[0] = c[1]
++                c[1] = tmp
++
++        return coords
++
++    def SetEmpty(self):
++        return self.polygon_drawer.SetEmpty()
++
++    def OnRelease(self, event):
++        if not self.mode == "zoom": return
++        self.zoom_rect.set_visible(False)
++        self.ZoomRectangle(event)
++        self.canvas.draw()
 +    
-+    def _createScatterPlotPanel(self):
-+        """!Init interactive scatterplot tools
++    def OnPress(self, event):
++        'on button press we will see if the mouse is over us and store some data'
++        if not event.inaxes:
++            return
++        if self.mode == "zoom_extend":
++            self.ZoomToExtend()
++
++        if event.xdata and event.ydata:
++            self.zoom_wheel_coords = { 'x' : event.xdata, 'y' : event.ydata}
++            self.zoom_rect_coords = { 'x' : event.xdata, 'y' : event.ydata}
++        else:
++            self.zoom_wheel_coords = None
++            self.zoom_rect_coords = None
++
++    def _stopCategoryEdit(self):
++        'disconnect all the stored connection ids'
++
++        if self.cidpress:
++            self.canvas.mpl_disconnect(self.cidpress)
++        if self.cidrelease:
++            self.canvas.mpl_disconnect(self.cidrelease)
++        #self.canvas.mpl_disconnect(self.cidmotion)
++
++    def _doLayout(self):
++        
++        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
++        self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
++        self.SetSizer(self.main_sizer)
++        self.main_sizer.Fit(self)
++    
++    def Plot(self, cats_order, scatts, ellipses, styles):
++        """ Redraws the figure
 +        """
++
++        callafter_list = []
++
++        if self.full_extend:
++            cx = self.axes.get_xlim()
++            cy = self.axes.get_ylim()
++            c = cx + cy
++        else:
++            c = None
++
++        
++        #q = Queue()
++        #p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.transpose, q))
++        #p.start()
++        #merged_img, self.full_extend = q.get()
++        #p.join()
++        
++        merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
++        self.axes.clear()
++        self.axes.axis('equal')
++
++        if self.transpose:
++            merged_img = np.transpose(merged_img, (1, 0, 2))
++
++        #print self.full_extend 
++
++        img = imshow(self.axes, merged_img,
++                     extent=self.full_extend,
++                     origin='lower',
++                     interpolation='nearest',
++                     aspect="equal")
++
++        callafter_list.append([self.axes.draw_artist, [img]])
++        callafter_list.append([grass.try_remove, [merged_img.filename]])
++
++        for cat_id in cats_order:
++            if cat_id == 0:
++                continue
++            if not ellipses.has_key(cat_id):
++                continue
++                
++            e = ellipses[cat_id]
++            if not e:
++                continue
++
++            colors = styles[cat_id]['color'].split(":")
++            if self.transpose:
++                e['theta'] = 360 - e['theta'] + 90
++                if e['theta'] >= 360:
++                    e['theta'] = abs(360 - e['theta']) 
++                
++                e['pos'] = [e['pos'][1], e['pos'][0]]
++
++            ellip = Ellipse(xy=e['pos'], 
++                            width=e['width'], 
++                            height=e['height'], 
++                            angle=e['theta'], 
++                            edgecolor="w",
++                            linewidth=1.5, 
++                            facecolor='None')
++            self.axes.add_artist(ellip)
++            callafter_list.append([self.axes.draw_artist, [ellip]])
++
++            color = map(lambda v : int(v)/255.0, styles[cat_id]['color'].split(":"))
++
++            ellip = Ellipse(xy=e['pos'], 
++                            width=e['width'], 
++                            height=e['height'], 
++                            angle=e['theta'], 
++                            edgecolor=color,
++                            linewidth=1, 
++                            facecolor='None')
++
++            self.axes.add_artist(ellip)
++            callafter_list.append([self.axes.draw_artist, [ellip]])
++            
++            center = Line2D([e['pos'][0]], [e['pos'][1]], 
++                            marker='x',
++                            markeredgecolor='w',
++                            #markerfacecolor=color,
++                            markersize=2)
++            self.axes.add_artist(center)
++            callafter_list.append([self.axes.draw_artist, [center]])
++
++        callafter_list.append([self.fig.canvas.blit, []])
++
++        if c:
++            self.axes.axis(c)
++        wx.CallAfter(lambda : self.CallAfter(callafter_list))
++    
++    def CallAfter(self, funcs_list):
++        while funcs_list: 
++            fcn, args = funcs_list.pop(0) 
++            fcn(*args) 
++
++        self.canvas.draw()
++
++    def CleanUp(self):
++        self.plotClosed.emit(scatt_id = self.scatt_id)
++        self.Destroy()
++
++    def ZoomWheel(self, event):
++        # get the current x and y limits
++        if not event.inaxes:
++            return
++        # tcaswell
++        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
++        cur_xlim = self.axes.get_xlim()
++        cur_ylim = self.axes.get_ylim()
++        
++        xdata = event.xdata
++        ydata = event.ydata 
++        if event.button == 'up':
++            scale_factor = 1/self.base_scale
++        elif event.button == 'down':
++            scale_factor = self.base_scale
++        else:
++            scale_factor = 1
++
++        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
++                  xdata + (cur_xlim[1] - xdata) * scale_factor, 
++                  ydata - (ydata - cur_ylim[0]) * scale_factor,
++                  ydata + (cur_ylim[1] - ydata) * scale_factor)
++
++        self.axes.axis(extend)
++        
++        self.canvas.draw()
++
++
++    def ZoomRectangle(self, event):
++        # get the current x and y limits
++        if not self.mode == "zoom": return
++        if event.inaxes is None: return
++        if event.button != 1: return
++
++        cur_xlim = self.axes.get_xlim()
++        cur_ylim = self.axes.get_ylim()
++        
++        x1, y1 = event.xdata, event.ydata
++        x2 = deepcopy(self.zoom_rect_coords['x'])
++        y2 = deepcopy(self.zoom_rect_coords['y'])
++
++        if x1 == x2 or y1 == y2:
++            return
++
++        self.axes.set_xlim(x1, x2)#, auto = True)
++        self.axes.set_ylim(y1, y2)#, auto = True)
++        self.canvas.draw()
++
++    def Motion(self, event):
++        self.PanMotion(event)
++        self.ZoomRectMotion(event)
++        
++        if event.inaxes is None: 
++            return
++        
++        self.cursorMove.emit(x=event.xdata, y=event.ydata)
++
++    def PanMotion(self, event):
++        'on mouse movement'
++        if not self.mode == "pan": 
++            return
++        if event.inaxes is None: 
++            return
++        if event.button != 1: 
++            return
++
++        cur_xlim = self.axes.get_xlim()
++        cur_ylim = self.axes.get_ylim()
++
++        x,y = event.xdata, event.ydata
++        
++        mx = (x - self.zoom_wheel_coords['x']) * 0.5
++        my = (y - self.zoom_wheel_coords['y']) * 0.5
++
++        extend = (cur_xlim[0] + mx, cur_xlim[1] + mx, cur_ylim[0] + my, cur_ylim[1] + my)
++
++        self.zoom_wheel_coords['x'] = x
++        self.zoom_wheel_coords['y'] = y
++
++        self.axes.axis(extend)
++
++        #self.canvas.copy_from_bbox(self.axes.bbox)
++        #self.canvas.restore_region(self.background)
++        self.canvas.draw()
++        
++    def ZoomRectMotion(self, event):
++        if not self.mode == "zoom": return
++        if event.inaxes is None: return
++        if event.button != 1: return
++
++        x1, y1 = event.xdata, event.ydata
++        self.zoom_rect.set_visible(True)
++        x2 = self.zoom_rect_coords['x']
++        y2 = self.zoom_rect_coords['y']
++
++        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))
++
++        #self.axes.draw_artist(self.zoom_rect)
++        self.canvas.draw()
++
++class ScatterPlotContextMenu:
++    def __init__(self, plot):
++
++        self.plot = plot
++        self.canvas = plot.canvas
++        self.cidpress = self.canvas.mpl_connect(
++            'button_press_event', self.ContexMenu)
++   
++    def ContexMenu(self, event):
++        if not event.inaxes:
++            return
++
++        if event.button == 3:
++            menu = wx.Menu()       
++            menu_items = [["zoom_to_extend", _("Zoom to scatter plot extend"), lambda event : self.plot.ZoomToExtend()]]
++
++            for item in menu_items:
++                item_id = wx.ID_ANY
++                menu.Append(item_id, text = item[1])
++                menu.Bind(wx.EVT_MENU, item[2], id = item_id)
++
++            wx.CallAfter(self.ShowMenu, menu) 
++   
++    def ShowMenu(self, menu):
++        self.plot.PopupMenu(menu)
++        menu.Destroy()
++        self.plot.ReleaseMouse() 
++
++class PolygonDrawer:
++    """
++    An polygon editor.
++    """
++    def __init__(self, ax, pol, empty_pol):
++        if pol.figure is None:
++            raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
++        self.ax = ax
++        self.canvas = pol.figure.canvas
++
++        self.showverts = True
++
++        self.pol = pol
++        self.empty_pol = empty_pol
++
++        x, y = zip(*self.pol.xy)
++
++        style = self._getPolygonStyle()
++
++        self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
++        self.ax.add_line(self.line)
++        #self._update_line(pol)
++
++        cid = self.pol.add_callback(self.poly_changed)
++        self.moving_ver_idx = None # the active vert
++
++        self.mode = None
++
++        if self.empty_pol:
++            self._show(False)
++
++        #self.canvas.mpl_connect('draw_event', self.draw_callback)
++        self.canvas.mpl_connect('button_press_event', self.OnButtonPressed)
++        self.canvas.mpl_connect('button_release_event', self.button_release_callback)
++        self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
++    
++        self.it = 0
++
++    def _getPolygonStyle(self):
++        style = {}
++        style['sel_pol'] = UserSettings.Get(group='scatt', 
++                                            key='selection', 
++                                            subkey='sel_pol')
++        style['sel_pol_vertex'] = UserSettings.Get(group='scatt', 
++                                                   key='selection', 
++                                                   subkey='sel_pol_vertex')
++
++        style['sel_pol'] = [i / 255.0 for i in style['sel_pol']] 
++        style['sel_pol_vertex'] = [i / 255.0 for i in style['sel_pol_vertex']] 
++
++        return style
++
++    def _getSnapTresh(self):
++        return UserSettings.Get(group='scatt', 
++                                key='selection', 
++                                subkey='snap_tresh')
++
++    def SetMode(self, mode):
++        self.mode = mode
++
++    def SetSelectionPolygonMode(self, activate):
++        
++        self.Show(activate)
++        if not activate and self.mode:
++            self.SetMode(None) 
++
++    def Show(self, show):
++        if show:
++            if not self.empty_pol:
++                self._show(True)
++        else:
++            self._show(False)
++
++    def GetCoords(self):
++        if self.empty_pol:
++            return None
++
++        coords = deepcopy(self.pol.xy)
++        return coords
++
++    def SetEmpty(self):
++        self._setEmptyPol(True)
++
++    def _setEmptyPol(self, empty_pol):
++        self.empty_pol = empty_pol
++        self._show(not empty_pol)
++
++    def _show(self, show):
++
++        self.show = show
++
++        self.line.set_visible(self.show)
++        self.pol.set_visible(self.show)
++
++        self.Redraw()
++
++    def Redraw(self):
++        if self.show:
++            self.ax.draw_artist(self.pol)
++            self.ax.draw_artist(self.line)
++        self.canvas.blit(self.ax.bbox)
++        self.canvas.draw()
++
++    def draw_callback(self, event):
++
++        style=self._getPolygonStyle()
++        self.pol.set_facecolor(style['sel_pol'])
++        self.line.set_markerfacecolor(style['sel_pol_vertex'])
++
++        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
++        self.ax.draw_artist(self.pol)
++        self.ax.draw_artist(self.line)
++    
++    def poly_changed(self, pol):
++        'this method is called whenever the polygon object is called'
++        # only copy the artist props to the line (except visibility)
++        vis = self.line.get_visible()
++        Artist.update_from(self.line, pol)
++        self.line.set_visible(vis)  # don't use the pol visibility state
++
++    def get_ind_under_point(self, event):
++        'get the index of the vertex under point if within treshold'
++
++        # display coords
++        xy = np.asarray(self.pol.xy)
++        xyt = self.pol.get_transform().transform(xy)
++        xt, yt = xyt[:, 0], xyt[:, 1]
++        d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
++        indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
++        ind = indseq[0]
++
++        if d[ind]>=self._getSnapTresh():
++            ind = None
++
++        return ind
++
++    def OnButtonPressed(self, event):
++        if not event.inaxes:
++            return
++
++        if event.button in [2, 3]: 
++            return
++
++        if self.mode == "delete_vertex":
++            self._deleteVertex(event)
++        elif self.mode == "add_boundary_vertex":
++            self._addVertexOnBoundary(event)
++        elif self.mode == "add_vertex":
++            self._addVertex(event)
++        elif self.mode == "remove_polygon":
++            self.SetEmpty()
++        self.moving_ver_idx = self.get_ind_under_point(event)
++
++    def button_release_callback(self, event):
++        'whenever a mouse button is released'
++        if not self.showverts: return
++        if event.button != 1: return
++        self.moving_ver_idx = None
++
++    def ShowVertices(self, show):
++        self.showverts = show
++        self.line.set_visible(self.showverts)
++        if not self.showverts: self.moving_ver_idx = None
++
++    def _deleteVertex(self, event):
++        ind = self.get_ind_under_point(event)
++
++        if ind  is None or self.empty_pol:
++            return
++
++        if len(self.pol.xy) <= 2:
++            self.empty_pol = True
++            self._show(False)
++            return
++
++        coords = []
++        for i,tup in enumerate(self.pol.xy): 
++            if i == ind:
++                continue
++            elif i == 0 and ind == len(self.pol.xy) - 1:
++                continue
++            elif i == len(self.pol.xy) - 1 and ind == 0: 
++                continue
++
++            coords.append(tup)
++
++        self.pol.xy = coords
++        self.line.set_data(zip(*self.pol.xy))
++
++        self.Redraw()
++
++    def _addVertexOnBoundary(self, event):
++        if self.empty_pol:
++            return
++
++        xys = self.pol.get_transform().transform(self.pol.xy)
++        p = event.x, event.y # display coords
++        for i in range(len(xys)-1):
++            s0 = xys[i]
++            s1 = xys[i+1]
++            d = dist_point_to_segment(p, s0, s1)
++
++            if d<=self._getSnapTresh():
++                self.pol.xy = np.array(
++                    list(self.pol.xy[:i + 1]) +
++                    [(event.xdata, event.ydata)] +
++                    list(self.pol.xy[i + 1:]))
++                self.line.set_data(zip(*self.pol.xy))
++                break
++
++        self.Redraw()
++
++    def _addVertex(self, event):
++
++        if self.empty_pol:
++            pt = (event.xdata, event.ydata)
++            self.pol.xy = np.array([pt, pt])
++            self._show(True)
++            self.empty_pol = False
++        else:
++            self.pol.xy = np.array(
++                        [(event.xdata, event.ydata)] +
++                        list(self.pol.xy[1:]) +
++                        [(event.xdata, event.ydata)])
++
++        self.line.set_data(zip(*self.pol.xy))
++        
++        self.Redraw()
++
++    def motion_notify_callback(self, event):
++        'on mouse movement'
++        if not self.mode == "move_vertex": return
++        if not self.showverts: return
++        if self.empty_pol: return
++        if self.moving_ver_idx is None: return
++        if event.inaxes is None: return
++        if event.button != 1: return
++
++        self.it += 1
++
++        x,y = event.xdata, event.ydata
++
++        self.pol.xy[self.moving_ver_idx] = x,y
++        if self.moving_ver_idx == 0:
++            self.pol.xy[len(self.pol.xy) - 1] = x,y
++        elif self.moving_ver_idx == len(self.pol.xy) - 1:
++            self.pol.xy[0] = x,y
++
++        self.line.set_data(zip(*self.pol.xy))
++
++        self.canvas.restore_region(self.background)
++
++        self.Redraw()
++
++class ModestImage(mi.AxesImage):
++    """
++    Computationally modest image class.
++
++    ModestImage is an extension of the Matplotlib AxesImage class
++    better suited for the interactive display of larger images. Before
++    drawing, ModestImage resamples the data array based on the screen
++    resolution and view window. This has very little affect on the
++    appearance of the image, but can substantially cut down on
++    computation since calculations of unresolved or clipped pixels
++    are skipped.
++
++    The interface of ModestImage is the same as AxesImage. However, it
++    does not currently support setting the 'extent' property. There
++    may also be weird coordinate warping operations for images that
++    I'm not aware of. Don't expect those to work either.
++
++    Author: Chris Beaumont <beaumont at hawaii.edu>
++    """
++    def __init__(self, minx=0.0, miny=0.0, *args, **kwargs):
++        if 'extent' in kwargs and kwargs['extent'] is not None:
++            raise NotImplementedError("ModestImage does not support extents")
++
++        self._full_res = None
++        self._sx, self._sy = None, None
++        self._bounds = (None, None, None, None)
++        self.minx = minx
++        self.miny = miny
++
++        super(ModestImage, self).__init__(*args, **kwargs)
++
++    def set_data(self, A):
++        """
++        Set the image array
++
++        ACCEPTS: numpy/PIL Image A
++        """
++        self._full_res = A
++        self._A = A
++
++        if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
++                                                         np.float):
++            raise TypeError("Image data can not convert to float")
++
++        if (self._A.ndim not in (2, 3) or
++            (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
++            raise TypeError("Invalid dimensions for image data")
++
++        self._imcache =None
++        self._rgbacache = None
++        self._oldxslice = None
++        self._oldyslice = None
++        self._sx, self._sy = None, None
++
++    def get_array(self):
++        """Override to return the full-resolution array"""
++        return self._full_res
++
++    def _scale_to_res(self):
++        """ Change self._A and _extent to render an image whose
++        resolution is matched to the eventual rendering."""
++
++        ax = self.axes
++        ext = ax.transAxes.transform([1, 1]) - ax.transAxes.transform([0, 0])
++        xlim, ylim = ax.get_xlim(), ax.get_ylim()
++        dx, dy = xlim[1] - xlim[0], ylim[1] - ylim[0]
++
++        y0 = max(self.miny, ylim[0] - 5)
++        y1 = min(self._full_res.shape[0] + self.miny, ylim[1] + 5)
++        x0 = max(self.minx, xlim[0] - 5)
++        x1 = min(self._full_res.shape[1] + self.minx, xlim[1] + 5)
++        y0, y1, x0, x1 = map(int, [y0, y1, x0, x1])
++
++        sy = int(max(1, min((y1 - y0) / 5., np.ceil(dy / ext[1]))))
++        sx = int(max(1, min((x1 - x0) / 5., np.ceil(dx / ext[0]))))
++
++        # have we already calculated what we need?
++        if sx == self._sx and sy == self._sy and \
++            x0 == self._bounds[0] and x1 == self._bounds[1] and \
++            y0 == self._bounds[2] and y1 == self._bounds[3]:
++            return
++
++        self._A = self._full_res[y0 - self.miny:y1 - self.miny:sy, 
++                                 x0 - self.minx:x1 - self.minx:sx]
++        self._A = cbook.safe_masked_invalid(self._A)
++        x1 = x0 + self._A.shape[1] * sx
++        y1 = y0 + self._A.shape[0] * sy
++
++        self.set_extent([x0 - .5, x1 - .5, y0 - .5, y1 - .5])
++        self._sx = sx
++        self._sy = sy
++        self._bounds = (x0, x1, y0, y1)
++        self.changed()
++
++    def draw(self, renderer, *args, **kwargs):
++        self._scale_to_res()
++        super(ModestImage, self).draw(renderer, *args, **kwargs)
++
++def imshow(axes, X, cmap=None, norm=None, aspect=None,
++           interpolation=None, alpha=None, vmin=None, vmax=None,
++           origin=None, extent=None, shape=None, filternorm=1,
++           filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
++    """Similar to matplotlib's imshow command, but produces a ModestImage
++
++    Unlike matplotlib version, must explicitly specify axes
++    Author: Chris Beaumont <beaumont at hawaii.edu>
++    """
++
++    if not axes._hold:
++        axes.cla()
++    if norm is not None:
++        assert(isinstance(norm, mcolors.Normalize))
++    if aspect is None:
++        aspect = rcParams['image.aspect']
++    axes.set_aspect(aspect)
++
++    if extent:
++        minx=extent[0]
++        miny=extent[2]
++    else:
++        minx=0.0
++        miny=0.0
++
++    im = ModestImage(minx, miny, axes, cmap, norm, interpolation, origin, extent,
++                     filternorm=filternorm,
++                     filterrad=filterrad, resample=resample, **kwargs)
++
++    im.set_data(X)
++    im.set_alpha(alpha)
++    axes._set_artist_props(im)
++
++    if im.get_clip_path() is None:
++        # image does not already have clipping set, clip to axes patch
++        im.set_clip_path(axes.patch)
++
++    #if norm is None and shape is None:
++    #    im.set_clim(vmin, vmax)
++    if vmin is not None or vmax is not None:
++        im.set_clim(vmin, vmax)
++    else:
++        im.autoscale_None()
++    im.set_url(url)
++
++    # update ax.dataLim, and, if autoscaling, set viewLim
++    # to tightly fit the image, regardless of dataLim.
++    im.set_extent(im.get_extent())
++
++    axes.images.append(im)
++    im._remove_method = lambda h: axes.images.remove(h)
++
++    return im
++
++def MergeImg(cats_order, scatts, styles, output_queue):
++
++        start_time = time.clock()
++        cmap_time = 0
++        merge_time = 0
++        mask_time = 0
++        norm_time = 0
++        max_time = 0
++
++        init = True
++        merge_tmp = grass.tempfile()
++        for cat_id in cats_order:
++            if not scatts.has_key(cat_id):
++                continue
++            scatt = scatts[cat_id]
++            #print "color map %d" % cat_id
++            #TODO make more general
++            if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
++               not styles[cat_id]['show']):
++                continue
++            if init:
++
++                b2_i = scatt['bands_info']['b1']
++                b1_i = scatt['bands_info']['b2']
++
++                full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5) 
++            
++            if cat_id == 0:
++                cmap = matplotlib.cm.jet
++                cmap.set_bad('w',1.)
++                cmap._init()
++                cmap._lut[len(cmap._lut) - 1, -1] = 0
++            else:
++                colors = styles[cat_id]['color'].split(":")
++
++                cmap = matplotlib.cm.jet
++                cmap.set_bad('w',1.)
++                cmap._init()
++                cmap._lut[len(cmap._lut) - 1, -1] = 0
++                cmap._lut[:, 0] = int(colors[0])/255.0
++                cmap._lut[:, 1] = int(colors[1])/255.0
++                cmap._lut[:, 2] = int(colors[2])/255.0
++
++
++            #if init:
++            tmp = time.clock()
++
++            masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
++            mask_time += time.clock() - tmp
++
++            tmp = time.clock()
++            vmax = np.amax(masked_cat)
++            max_time += time.clock() - tmp
++
++            tmp = time.clock()
++            masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
++            norm_time += time.clock() - tmp
++
++            #print "ahoj"
++            #print masked_cat
++            #print masked_cat.shape
++            #print masked_cat.mask.dtype
++            #
++            #print "colored_cat.shape"
++            #print colored_cat.shape
++            #merged_img = np.memmap(merge_tmp, dtype='uint8',, shape=colored_cat.shape)
++            tmp = time.clock()
++            cmap = np.uint8(cmap._lut * 255)
++            sh =masked_cat.shape
++
++            colored_cat = np.zeros(dtype='uint8', shape=(sh[0], sh[1], 4))
++            ApplyColormap(masked_cat, masked_cat.mask, cmap, colored_cat)
++
++            #colored_cat = np.uint8(cmap(masked_cat) * 255)
++            cmap_time += time.clock() - tmp
++
++            del masked_cat
++            del cmap
++            
++            #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
++
++            tmp = time.clock()
++            if init:
++                merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', shape=colored_cat.shape)
++                merged_img[:] = colored_cat[:]
++                init = False
++            else:
++                MergeArrays(merged_img, colored_cat, styles[cat_id]['opacity'])
++
++            """
++                #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape) 
++                c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
++
++                #TODO apply strides and there will be no need for loop
++                #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
++                
++                for i in range(3):
++                    merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
++                merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
++                
++                del c_img_a
++            """
++            merge_time += time.clock() - tmp
++
++            del colored_cat
++
++        end_time =  time.clock() - start_time
++        #print "all time:%f" % (end_time)
++        #print "cmap_time:%f" % (cmap_time / end_time * 100.0 )
++        #print "merge_time:%f" % (merge_time / end_time * 100.0 )
++        #print "mask_time:%f" % (mask_time / end_time * 100.0 )
++        #print "nax_time:%f" % (max_time / end_time * 100.0 )
++        #print "norm_time:%f" % (norm_time / end_time * 100.0 )
++        
++        #output_queue.put((merged_img, full_extend))
++        return merged_img, full_extend 
+\ No newline at end of file
+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,816 @@
++"""!
++ at package scatt_plot.controllers
++
++ at brief Controller layer for scatter plot tool.
++
++Classes:
++
++(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.
++
++ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
++"""
++import os
++import sys
++
++#TODO just for testing
++import time
++
++#TODO
++import wx
++
++from core.gcmd import GException, GError, GMessage, RunCommand
++
++from scatt_plot.scatt_core import Core, idBandsToidScatt
++
++from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
++from scatt_plot.gthreading import gThread
++from core.gconsole import EVT_CMD_DONE
++from core.settings import UserSettings
++from grass.pydispatch.signal import Signal
++import grass.script as grass
++
++from grass.pydispatch.signal import Signal
++
++class ScattsManager(wx.EvtHandler):
++    def __init__(self, guiparent, giface, iclass_mapwin):
++        #TODO remove iclass parameter
++
++        wx.EvtHandler.__init__(self)
++        self.giface = giface
++        self.mapDisp  = giface.GetMapDisplay()
++
++        if iclass_mapwin:
++            self.mapWin = iclass_mapwin
++        else:
++            self.mapWin = giface.GetMapWindow()
++
++        self.guiparent = guiparent
++
++        self.show_add_scatt_plot = False
++
++        self.core = Core()
++        self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
++
++        self.cats_mgr = CategoriesManager(self, self.core)
++
++        self.thread = gThread(self);
++        
++        self.plots = {}
++        self.added_cats_rasts = []
++
++        self.cats_to_update = []
++
++        self.plot_mode =  None
++        self.pol_sel_mode = [False, None]
++
++        self.data_set = False
++
++        self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")
++
++        self.renderingStarted = Signal("ScattsManager.renderingStarted")
++        self.renderingFinished = Signal("ScattsManager.renderingFinished")
++
++        self.computingStarted = Signal("ScattsManager.computingStarted")
++
++        if iclass_mapwin: 
++            self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
++            self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
++        else:
++            self.mapWin_conn = None
++            self.iclass_conn = None
++
++        self.tasks_pids = {
++                            'add_scatt' : [],
++                            'set_data' : -1,
++                            'set_data_add' : -1,
++                            'set_edit_cat_data' : -1,
++                            'mapwin_conn' : [],
++                            'render_plots' : -1,
++                            'render' : [],
++                            'export_cat_rast' : []
++                           }
++
++        self._initSettings()
++
++        self.Bind(EVT_CMD_DONE, self.OnThreadDone)
++
++        self.modeSet = Signal("ScattsManager.mondeSet")
++
++    def CleanUp(self):
++        self.core.CleanUp()
++        self.added_cats_rasts = []
++
++        self.cats_mgr.CleanUp()
++        for scatt_id, scatt in self.plots.items():
++            scatt.CleanUp()
++
++    def _initSettings(self):
++        """!Initialization of settings (if not already defined)"""
++        # initializes default settings
++        initSettings = [
++                        ['selection', 'sel_pol', (255,255,0)],
++                        ['selection', 'sel_pol_vertex', (255,0,0)],
++                        ['selection', 'sel_area', (0,255,19)],
++                        ['selection', "snap_tresh", 10],
++                        ['selection', 'sel_area_opacty', 50],
++                        ['ellipses', 'show_ellips', True],
++                       ]
++
++        for init in initSettings: 
++            UserSettings.ReadSettingsFile()
++            UserSettings.Append(dict = UserSettings.userSettings, 
++                                    group ='scatt',
++                                    key = init[0],
++                                    subkey =init[1],
++                                    value = init[2],
++                                    overwrite = False)
++
++    def OnThreadDone(self, event):
++        
++        if event.exception:
++            GError(str(event.exception))
++            return
++
++        if event.pid in self.tasks_pids['mapwin_conn']:
++            self.tasks_pids['mapwin_conn'].remove(event.pid)
++            updated_cats = event.ret
++
++            for cat in updated_cats:
++                if cat not in  self.cats_to_update:
++                    self.cats_to_update.append(cat)
++
++            if not self.tasks_pids['mapwin_conn']:
++                self.tasks_pids['render_plots'] = self.thread.GetId()
++                self.thread.Run(callable = self.core.ComputeCatsScatts, 
++                                cats_ids = self.cats_to_update[:])
++                del self.cats_to_update[:]
++ 
++            return
++
++        if self.tasks_pids['render_plots'] == event.pid:
++            self.RenderScattPlts()
++            return
++
++        if event.pid in self.tasks_pids['render']:
++            self.tasks_pids['render'].remove(event.pid)
++            if not self.tasks_pids['render']:
++                self.renderingFinished.emit()
++            return
++            
++        if event.pid in self.tasks_pids['add_scatt']:
++            self.tasks_pids['add_scatt'].remove(event.pid)
++            self.AddScattPlotDone(event)
++            return
++
++        if self.tasks_pids['set_data'] == event.pid:
++            self.SetDataDone(event)
++            return
++        
++        if self.tasks_pids['set_data_add'] == event.pid:
++            self.SetDataDone(event)
++            self.AddScattPlot()
++            return
++        
++        if self.tasks_pids['set_edit_cat_data'] == event.pid:
++            self.SetEditCatDataDone(event)
++            return
++
++        if event.pid in self.tasks_pids['export_cat_rast']:
++            self.cats_mgr.OnExportCatRastDone(event)
++            return
++
++    def SetData(self, bands):
++        "setting datas"
++        self.CleanUp()
++        self.data_set = False
++        
++        if self.show_add_scatt_plot:
++            self.tasks_pids['set_data_add'] = self.thread.GetId()
++        else:
++            self.tasks_pids['set_data'] = self.thread.GetId()
++
++        self.thread.Run(callable=self.core.SetData, bands=bands)
++
++    def SetDataDone(self, event):
++        self.data_set = True
++        self.iclass_conn.SyncCats()
++
++    def SettingsUpdated(self, event):
++        #TODO optimization
++        self.RenderScattPlts()
++
++    def OnOutput(self, event):
++        """!Print thread output according to debug level.
++        """
++        print event.text
++
++    def GetBands(self):
++        return self.core.GetBands()
++
++    def AddScattPlot(self):
++        if not self.data_set and self.iclass_conn:
++            self.show_add_scatt_plot = True
++            self.iclass_conn.SetData()
++            self.show_add_scatt_plot = False
++            return
++        if not self.data_set:
++            GError(_('No data set.'))
++            return
++
++        self.computingStarted.emit()
++
++        bands = self.core.GetBands()
++        dlg = AddScattPlotDialog(parent = self.guiparent, bands = bands)
++        if dlg.ShowModal() == wx.ID_OK:
++
++            b_1, b_2 = dlg.GetBands()
++           
++            #TODO axes selection
++            transpose = False
++            if b_1 > b_2:
++                transpose = True
++                tmp_band = b_2
++                b_2 = b_1
++                b_1 = tmp_band
++
++            self.scatt_id = idBandsToidScatt(b_1, b_2, len(bands))
++            self._addScattPlot(self.scatt_id, transpose)
++        
++        dlg.Destroy()
++
++        self.mapWin_conn.Update()
++         
++    def _addScattPlot(self, scatt_id, transpose):
++        if self.plots.has_key(scatt_id):
++            GMessage(_("Scatter plot has been already added."))
++            return
++
++        self.tasks_pids['add_scatt'].append(self.thread.GetId())
++        
++        self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id, userdata = {'transpose' : transpose})
++
++    def RenderScattPlts(self, scatt_ids = None):
++        if len(self.tasks_pids['render']) > 1:
++            return 
++        
++        self.renderingStarted.emit()
++        self.tasks_pids['render'].append(self.thread.GetId())
++        self.thread.Run(callable = self._renderscattplts, scatt_ids = scatt_ids)
++
++    def _renderscattplts(self, scatt_ids):
++        cats_attrs = self.cats_mgr.GetCategoriesAttrs()
++        cats = self.cats_mgr.GetCategories()[:]
++        cats.reverse()
++        cats.insert(0, 0)
++        for i_scatt_id, scatt in self.plots.items():
++            if scatt_ids is not None and i_scatt_id not in scatt_ids:
++                continue
++
++            scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
++            if self._showConfEllipses():
++                ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id, cats_attrs)
++            else:
++                ellipses_dt = {}
++
++            if self.pol_sel_mode[0]:
++                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
++
++            scatt.Plot(cats_order = cats, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
++
++    def _showConfEllipses(self):
++        return UserSettings.Get(group='scatt', 
++                                key="ellipses", 
++                                subkey="show_ellips")
++
++    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
++
++        cat_id = self.cats_mgr.GetSelectedCat()
++        if not cat_id:
++            return
++
++        sel_a_cat_id = max(cats_attrs.keys()) + 1        
++
++        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
++        if not s:
++            return
++
++        cats_order.append(sel_a_cat_id)
++
++        col = UserSettings.Get(group='scatt', 
++                                 key='selection', 
++                                 subkey='sel_area')
++
++        col = ":".join(map(str, col))
++        opac = UserSettings.Get(group='scatt', 
++                                key='selection', 
++                                subkey='sel_area_opacty') / 100.0
++
++        cats_attrs[sel_a_cat_id] = {'color' : col,
++                                    'opacity' : opac,
++                                    'show' : True}
++
++        scatt_dt[sel_a_cat_id] = s[cat_id]
++
++    def AddScattPlotDone(self, event):
++        
++        transpose = event.userdata['transpose']
++        scatt_id = event.kwds['scatt_id']
++
++        #TODO guiparent - not very good
++        self.plots[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id, transpose = transpose)
++
++        self.plots[scatt_id].plotClosed.connect(self.PlotClosed)
++        self.plots[scatt_id].cursorMove.connect(lambda x, y: self.cursorPlotMove.emit(x = x, y = y))
++
++        if self.plot_mode:
++            self.plots[scatt_id].SetMode(self.plot_mode)
++            self.plots[scatt_id].ZoomToExtend()
++
++        self.RenderScattPlts(scatt_ids = [scatt_id])
++
++    def PlotClosed(self, scatt_id):
++        del self.plots[scatt_id]
++
++    def SetPlotsMode(self, mode):
++
++        self.plot_mode = mode
++        for scatt in self.plots.itervalues():
++            scatt.SetMode(mode)
++
++        self.modeSet.emit(mode = mode)
++
++    def ActivateSelectionPolygonMode(self, activate):
++        self.pol_sel_mode[0] = activate
++        for scatt in self.plots.itervalues():
++            scatt.SetSelectionPolygonMode(activate)
++            if not activate and self.plot_mode not in ['zoom', 'pan', 'zoom_extend']:
++                self.SetPlotsMode(None)
++
++        self.RenderScattPlts()
++        return activate
++
++    def ProcessSelectionPolygons(self, process_mode):        
++        scatts_polygons = {}        
++        for scatt_id, scatt in self.plots.iteritems():
++            coords = scatt.GetCoords()
++            if coords is not None:
++                scatts_polygons[scatt_id] = coords
++
++        if not scatts_polygons:
++            return
++
++        value = 1
++        if process_mode == 'remove':
++            value = 0
++
++        sel_cat_id = self.cats_mgr.GetSelectedCat()
++        if not sel_cat_id:
++            dlg = wx.MessageDialog(parent = self.guiparent,
++                      message = _("In order to select arrea in scatter plot, "
++                                  "you have to select class first.\n\n"
++                                  "There is no class yet, "
++                                  "do you want to create one?"),
++                      caption = _("No class selected"),
++                      style = wx.YES_NO)
++            if dlg.ShowModal() == wx.ID_YES:
++                self.iclass_conn.EmptyCategories()
++
++        sel_cat_id = self.cats_mgr.GetSelectedCat()
++        if not sel_cat_id:
++            return
++
++        for scatt in self.plots.itervalues():
++            scatt.SetEmpty()
++
++        self.computingStarted.emit()
++
++        self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
++        self.thread.Run(callable = self.core.UpdateCategoryWithPolygons, 
++                        cat_id = sel_cat_id,
++                        scatts_pols = scatts_polygons,
++                        value = value)
++
++    def SetEditCatDataDone(self, event):
++        if event.exception:
++            GError(_("Error occured during computation of scatter plot category:\n%s"), 
++                      parent = self.guiparent, showTraceback = False)
++
++        cat_id = event.ret
++
++        self.RenderScattPlts()
++
++        cat_id = event.kwds["cat_id"]
++        train_mgr, preview_mgr = self.iclass_conn.iclass_frame.GetMapManagers()
++        
++        if not cat_id in self.added_cats_rasts:
++            cat_rast = self.core.GetCatRast(cat_id)
++
++            cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
++            self.UpdateCategoryRaster(cat_id, ['color'], render = False)
++            train_mgr.AddLayer(cat_rast, alias = cat_name)
++
++            self.added_cats_rasts.append(cat_id)
++        else: #TODO settings
++            train_mgr.Render()
++            
++    def UpdateCategoryRaster(self, cat_id, attrs, render = True):
++
++        cat_rast = self.core.GetCatRast(cat_id)
++        if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
++            return
++        cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
++        train_mgr, preview_mgr = self.iclass_conn.iclass_frame.GetMapManagers()
++
++        if "color" in attrs:
++            ret, err_msg = RunCommand('r.colors',
++                                      map=cat_rast,
++                                      rules="-",
++                                      stdin="1 %s" % cats_attrs["color"],
++                                      getErrorMsg=True)
++
++            if ret != 0:
++                GError("r.colors failed\n%s" % err_msg)
++            if render:
++                train_mgr.Render()
++
++        if "name" in attrs:
++            cat_rast = self.core.GetCatRast(cat_id)
++
++            train_mgr.SetAlias(original=cat_rast, alias=cats_attrs['name'])
++            cats_attrs["name"]
++
++
++    def DigitDataChanged(self, vectMap, digit):
++        
++        if self.mapWin_conn:
++            self.mapWin_conn.DigitDataChanged(vectMap, digit)
++            return 1
++        else:
++            return 0
++
++    def GetCategoriesManager(self):
++        return self.cats_mgr
++
++class CategoriesManager:
++
++    def __init__(self, scatt_mgr, core):
++
++        self.core = core
++        self.scatt_mgr = scatt_mgr
++
++        self.cats = {}
++        self.cats_ids = []
++
++        self.sel_cat_id = None
++
++        self.exportRaster = None
++
++        self.initialized = Signal('CategoriesManager.initialized')
++        self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
++        self.deletedCategory = Signal('CategoriesManager.deletedCategory')
++        self.addedCategory = Signal('CategoriesManager.addedCategory')
++
++    def CleanUp(self):
++        
++        self.cats.clear()
++        del self.cats_ids[:]
++
++        self.sel_cat_id = None  
++    """
++    def InitCoreCats(self):
++        if self.scatt_mgr.data_set:
++            for cat_id in self.cats_ids:
++                self._addCategory(cat_id)
++
++        self.scatt_mgr.mapWin_conn.Update()
++    """
++    def ChangePosition(self, cat_id, new_pos):
++        if new_pos >= len(self.cats_ids):
++            return False
++
 +        try:
-+            from scatt_plot.frame import IClassScatterPlotsPanel
-+            self.scatt_plot_panel = IClassScatterPlotsPanel(parent=self, 
-+                                                            giface=self._giface, 
-+                                                            iclass_mapwin = self.parent.GetFirstWindow())
-+            self.mainSizer.Add(self.scatt_plot_panel, proportion = 1, flag = wx.EXPAND, border = 0)
-+            self.scatt_plot_panel.Hide()
-+        except ImportError as e:#TODO
-+            self.scatt_error = _("Scatter plot functionality is disabled. Reason:\n" \
-+                                 "Unable to import packages needed for scatter plot.\n%s" % e)
-+            GError(self.scatt_error)
-+            self.scatt_plot_panel = None
++            pos = self.cats_ids.index(cat_id)
++        except:
++            return False
 +
-     def OnPlotTypeSelected(self, event):
-         """!Plot type selected"""
++        if pos > new_pos:
++            pos -= 1
 +
-+        if self.plotSwitch.GetSelection() in [0, 1]:
-+            self.SetupScrolling(scroll_x = False, scroll_y = True)
-+            if self.scatt_plot_panel:
-+                self.scatt_plot_panel.Hide()
-+            self.canvasPanel.Show()
-+            self.Layout()
++        self.cats_ids.remove(cat_id)
 +
-+        elif self.plotSwitch.GetSelection() == 2:
-+            self.SetupScrolling(scroll_x = False, scroll_y = False)
-+            if self.scatt_plot_panel:
-+                self.scatt_plot_panel.Show()
++        self.cats_ids.insert(new_pos, cat_id)
++
++        self.scatt_mgr.RenderScattPlts()
++        return True
++
++    def _addCategory(self, cat_id):
++        self.scatt_mgr.thread.Run(callable = self.core.AddCategory, 
++                                  cat_id = cat_id)
++
++    def AddCategory(self, cat_id = None, name = None, color = None, nstd = None):
++
++        if cat_id is None:
++            if self.cats_ids:
++                cat_id = max(self.cats_ids) + 1
 +            else:
-+                GError(self.scatt_error)
-+            self.canvasPanel.Hide()
-+            self.Layout()
++                cat_id = 1
 +
-         if self.currentCat is None:
-             return
--        
++        if self.scatt_mgr.data_set:
++            self.scatt_mgr.thread.Run(callable = self.core.AddCategory, 
++                                      cat_id = cat_id)
++            #TODO check number of cats
++            #if ret < 0: #TODO
++            #    return -1;
 +
-         if self.plotSwitch.GetSelection() == 0:
-             stat = self.stats_data.GetStatistics(self.currentCat)
-             if not stat.IsReady():
-@@ -66,7 +112,10 @@
-             self.DrawHistograms(stat)
-         else:
-             self.DrawCoincidencePlots()
--            
++        self.cats[cat_id] = {
++                                'name' : _('Category %s' % cat_id ),
++                                'color' : "0:0:0",
++                                'opacity' : 1.0,
++                                'show' : True,
++                                'nstd' : 1.0,
++                            }
 +
-+        self.Layout()
++        self.cats_ids.insert(0, cat_id)
 +
++        if name is not None:
++            self.cats[cat_id]["name"] = name
++   
++        if color is not None:
++            self.cats[cat_id]["color"] = color
 +
-     def StddevChanged(self):
-         """!Standard deviation multiplier changed, redraw histograms"""
-         if self.plotSwitch.GetSelection() == 0:
-@@ -89,7 +138,7 @@
-             panel.Destroy()
-             
-         self.canvasList = []
--            
++        if nstd is not None:
++            self.cats[cat_id]["nstd"] = nstd
 +
-     def ClearPlots(self):
-         """!Clears plot canvases"""
-         for bandIdx in range(len(self.bandList)):
-@@ -104,15 +153,15 @@
-     def CreatePlotCanvases(self):
-         """!Create plot canvases according to the number of bands"""
-         for band in self.bandList:
--            canvas = plot.PlotCanvas(self)
-+            canvas = plot.PlotCanvas(self.canvasPanel)
-             canvas.SetMinSize((-1, 140))
-             canvas.SetFontSizeTitle(10)
-             canvas.SetFontSizeAxis(8)
-             self.canvasList.append(canvas)
-             
--            self.mainSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
--
--        self.SetVirtualSize(self.GetBestVirtualSize()) 
-+            self.canvasSizer.Add(item = canvas, proportion = 1, flag = wx.EXPAND, border = 0)
++        self.addedCategory.emit(cat_id = cat_id,
++                                name = self.cats[cat_id]["name"], 
++                                color = self.cats[cat_id]["color"] )
++        return cat_id
++
++    def SetCategoryAttrs(self, cat_id, attrs_dict):
++        render = False
++        update_cat_rast = []
++
++        for k, v in attrs_dict.iteritems():
++            if not render and k in ['color', 'opacity', 'show', 'nstd']:
++                render = True
++            if k in ['color', 'name']:
++                update_cat_rast.append(k)
++
++            self.cats[cat_id][k] = v
++
++        if render:
++            self.scatt_mgr.RenderScattPlts()
 +        
-+        self.SetVirtualSize(self.GetBestVirtualSize())
-         self.Layout()
-         
-     def UpdatePlots(self, group, currentCat, stats_data):
-@@ -138,7 +187,7 @@
-         
-     def UpdateCategory(self, cat):
-         self.currentCat = cat
--        
-+    
-     def DrawCoincidencePlots(self):
-         """!Draw coincidence plots"""
-         for bandIdx in range(len(self.bandList)):
++        if update_cat_rast:
++            self.scatt_mgr.UpdateCategoryRaster(cat_id, update_cat_rast)
++
++        self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
++
++    def DeleteCategory(self, cat_id):
++
++        if self.scatt_mgr.data_set:
++            self.scatt_mgr.thread.Run(callable = self.core.DeleteCategory, 
++                                      cat_id = cat_id)
++        del self.cats[cat_id]
++        self.cats_ids.remove(cat_id)
++
++        self.deletedCategory.emit(cat_id = cat_id)
++
++    #TODO emit event?
++    def SetSelectedCat(self, cat_id):
++        self.sel_cat_id = cat_id
++        if self.scatt_mgr.pol_sel_mode[0]:
++            self.scatt_mgr.RenderScattPlts()
++
++    def GetSelectedCat(self):
++        return self.sel_cat_id
++
++    def GetCategoryAttrs(self, cat_id):
++        #TODO is mutable
++        return self.cats[cat_id]
++
++    def GetCategoriesAttrs(self):
++        #TODO is mutable
++        return self.cats
++     
++    def GetCategories(self):
++        return self.cats_ids[:]
++
++    def SetCategoryPosition(self):
++        if newindex > oldindex:
++            newindex -= 1
++        
++        self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
++
++    def ExportCatRast(self, cat_id):
++
++        cat_attrs = self.GetCategoryAttrs(cat_id)
++
++        dlg = ExportCategoryRaster(parent=self.scatt_mgr.guiparent, 
++                                   rasterName=self.exportRaster, 
++                                   title=_("Export scatter plot raster of class <%s>") % cat_attrs['name'])
++        
++        if dlg.ShowModal() == wx.ID_OK:
++            self.exportCatRast = dlg.GetRasterName()
++            dlg.Destroy()
++
++            self.scatt_mgr.tasks_pids['export_cat_rast'].append(self.scatt_mgr.thread.GetId())
++            self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
++                                      userdata={'name' : cat_attrs['name']},
++                                      cat_id=cat_id,
++                                      rast_name=self.exportCatRast)
++
++    def OnExportCatRastDone(self, event):
++        ret, err = event.ret
++        if ret == 0:
++            cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
++            GMessage(_("Scatter plot raster of class <%s> exported to raster map <%s>.") % 
++                      (event.userdata['name'], event.kwds['rast_name']))
++        else:
++            GMessage(_("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") % 
++                      (event.userdata['name'], event.kwds['rast_name'], err))
++
++
++class MapWinConnection:
++    def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
++        self.mapWin = mapWin
++        self.vectMap = None
++        self.scatt_rast_updater = scatt_rast_updater
++        self.scatt_mgr = scatt_mgr
++        self.cats_mgr = scatt_mgr.cats_mgr
++
++        self.thread = self.scatt_mgr.thread
++
++        #TODO
++        self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
++
++        #def ChangeMap(self, vectMap, layers_cats):
++        #     self.vectMap = vectMap
++        #     self.layers_cats = layers_cats
++
++        #ret, region, msg = RunCommand("v.to.rast",
++        #                              flags = "gp",
++        #                              getErrorMsg = True,
++        #                              read = True)
++
++    def Update(self):
++        self.thread.Run(callable=self.scatt_rast_updater.SynchWithMap)
++
++    def _connectSignals(self):
++        self.digit.featureAdded.connect(self.AddFeature)
++        self.digit.areasDeleted.connect(self.DeleteAreas)
++        self.digit.featuresDeleted.connect(self.DeleteAreas)
++        self.digit.vertexMoved.connect(self.EditedFeature)
++        self.digit.vertexRemoved.connect(self.EditedFeature)
++        self.digit.lineEdited.connect(self.EditedFeature)
++        self.digit.featuresMoved.connect(self.EditedFeature)
++
++    def AddFeature(self, new_bboxs, new_areas_cats):
++        if not self.scatt_mgr.data_set:
++            return
++        self.scatt_mgr.computingStarted.emit()
++
++        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
++                        new_bboxs = new_bboxs, 
++                        old_bboxs = [], 
++                        old_areas_cats = [],
++                        new_areas_cats = new_areas_cats)
++
++    def DeleteAreas(self, old_bboxs, old_areas_cats):
++        if not self.scatt_mgr.data_set:
++            return
++        self.scatt_mgr.computingStarted.emit()
++
++        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
++                        new_bboxs = [], 
++                        old_bboxs = old_bboxs, 
++                        old_areas_cats = old_areas_cats,
++                        new_areas_cats = [])
++
++
++    def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
++        if not self.scatt_mgr.data_set:
++            return
++        self.scatt_mgr.computingStarted.emit()
++
++        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
++        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
++                        new_bboxs = new_bboxs, 
++                        old_bboxs = old_bboxs, 
++                        old_areas_cats = old_areas_cats,
++                        new_areas_cats = new_areas_cats)
++
++    def DigitDataChanged(self, vectMap, digit):
++
++        self.digit = digit
++        self.vectMap = vectMap
++
++        self.digit.EmitSignals(emit = True)
++
++        self.scatt_rast_updater.SetVectMap(vectMap)
++
++        self._connectSignals()
++
++
++class IClassConnection:
++    def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
++        self.iclass_frame = iclass_frame
++        self.stats_data = self.iclass_frame.stats_data
++        self.cats_mgr = cats_mgr
++        self.scatt_mgr= scatt_mgr
++
++        self.stats_data.statisticsAdded.connect(self.AddCategory)
++        self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
++        self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
++        self.stats_data.statisticsSet.connect(self.SetCategory)
++
++        self.iclass_frame.groupSet.connect(self.GroupSet)
++
++        self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
++        self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++        self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++        self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
++
++        self.SyncCats()
++
++    def SetData(self):
++        self.iclass_frame.AddBands()
++
++    def EmptyCategories(self):
++        self.iclass_frame.OnCategoryManager(None)
++
++    def SyncCats(self, cats_ids = None):
++        self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
++        cats = self.stats_data.GetCategories()
++        for c in cats:
++            if cats_ids and c not in cats_ids:
++                continue
++            stats = self.stats_data.GetStatistics(c)
++            self.cats_mgr.AddCategory(c, stats.name, stats.color, stats.nstd)
++        self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++    def CategoryChanged(self, cat):
++        self.cats_mgr.SetSelectedCat(cat) 
++
++    def AddCategory(self, cat, name, color):
++        self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
++        stats = self.stats_data.GetStatistics(cat)
++        self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color, nstd = stats.nstd)
++        self.cats_mgr.addedCategory.connect(self.AddStatistics)
++
++    def DeleteCategory(self, cat):
++        self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
++        self.cats_mgr.DeleteCategory(cat)
++        self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++
++    def DeletAllCategories(self):
++
++        self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
++        cats = self.stats_data.GetCategories()
++        for c in cats:
++            self.cats_mgr.DeleteCategory(c)
++        self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
++
++    def SetCategory(self, cat, stats):
++
++        self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
++        cats_attr = {}
++
++        for attr in ['name', 'color', 'nstd']:
++            if stats.has_key(attr):
++                cats_attr[attr] = stats[attr]
++
++        if cats_attr:
++            self.cats_mgr.SetCategoryAttrs(cat, cats_attr)
++        self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
++
++
++    def SetStatistics(self, cat_id, attrs_dict):
++        self.stats_data.statisticsSet.disconnect(self.SetCategory)
++        self.stats_data.GetStatistics(cat_id).SetStatistics(attrs_dict)
++        self.stats_data.statisticsSet.connect(self.SetCategory)
++
++    def AddStatistics(self, cat_id, name, color):
++        self.stats_data.statisticsAdded.disconnect(self.AddCategory)
++        self.stats_data.AddStatistics(cat_id, name, color)
++        self.stats_data.statisticsAdded.connect(self.AddCategory)
++
++    def DeleteStatistics(self, cat_id):
++        self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
++        self.stats_data.DeleteStatistics(cat_id)
++        self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
++
++    def GroupSet(self, group):
++        res = RunCommand('i.group',
++                         flags = 'g',
++                         group = group, subgroup = group,
++                         read = True).strip()
++        if res.split('\n')[0]:
++            bands = res.split('\n')
++            self.scatt_mgr.SetData(bands)
+\ No newline at end of file
 Index: gui/wxpython/scatt_plot/gthreading.py
 ===================================================================
 --- gui/wxpython/scatt_plot/gthreading.py	(revision 0)
@@ -1227,7 +4249,7 @@
 +    def _toolbarData(self):
 +
 +        icons = {
-+                'settings'   : BaseIcons['settings'].SetLabel( _('Ssettings')),
++                'settings'   : BaseIcons['settings'].SetLabel( _('Settings')),
 +                'help'       : MetaIcon(img = 'help',
 +                                         label = _('Show manual')),
 +                'add_scatt_pl'  : MetaIcon(img = 'layer-raster-analyze',
@@ -1415,7 +4437,7 @@
 ===================================================================
 --- gui/wxpython/scatt_plot/scatt_core.py	(revision 0)
 +++ gui/wxpython/scatt_plot/scatt_core.py	(working copy)
-@@ -0,0 +1,808 @@
+@@ -0,0 +1,813 @@
 +"""!
 + at package scatt_plot.scatt_plot
 +
@@ -1567,7 +4589,8 @@
 +            b1_info = self.an_data.GetBandInfo(b1)
 +            b2_info = self.an_data.GetBandInfo(b2)
 +
-+            raster_pol = RasterizePolygon(coords, b1_info['range'], b2_info['range'])
++            raster_pol = RasterizePolygon(coords, b1_info['range'], b1_info['min'], 
++                                                  b2_info['range'], b2_info['min'])
 +
 +            raster_ind = np.where(raster_pol > 0) 
 +            arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id)
@@ -2159,25 +5182,29 @@
 +
 +        return cats_rasts
 +
++def RasterizePolygon(pol, height, min_h, width, min_w):
 +
-+def RasterizePolygon(pol, height, width):
-+
 +    # Joe Kington
 +    # http://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask
 +
 +    #poly_verts = [(1,1), (1,4), (4,4),(4,1), (1,1)]
 +
-+    nx = width + 1
-+    ny = height + 1
++    nx = width
++    ny = height
 +
-+    x, y = np.meshgrid(np.arange(nx), np.arange(ny))
++    for p in pol:
++        p[0] = p[0] - min_w
++        p[1] = p[1] - min_h
++
++    x, y =  np.meshgrid(np.arange(-0.5, nx+0.5, dtype=float), np.arange(-0.5, ny+0.5, dtype=float))
 +    x, y = x.flatten(), y.flatten()
 +
 +    points = np.vstack((x,y)).T
 +
++
 +    p = Path(pol)
 +    grid = p.contains_points(points)
-+    grid = grid.reshape((ny,nx))
++    grid = grid.reshape((ny + 1,nx + 1))
 +
 +    raster = np.zeros((height, width), dtype=np.uint8)#TODO bool
 +
@@ -3077,1770 +6104,6 @@
 +    'scatt_core',
 +    'core_c',
 +    ]
-Index: gui/wxpython/scatt_plot/plots.py
-===================================================================
---- gui/wxpython/scatt_plot/plots.py	(revision 0)
-+++ gui/wxpython/scatt_plot/plots.py	(working copy)
-@@ -0,0 +1,936 @@
-+"""!
-+ at package scatt_plot.dialogs
-+
-+ at brief Ploting widgets.
-+
-+Classes:
-+
-+(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.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+import wx
-+import numpy as np
-+
-+#TODO testing
-+import time
-+from multiprocessing import Process, Queue
-+
-+#TODO
-+#from numpy.lib.stride_tricks import as_strided
-+from copy import deepcopy
-+from scatt_plot.core_c import MergeArrays, ApplyColormap
-+from core.settings import UserSettings
-+
-+try:
-+    import matplotlib
-+    matplotlib.use('WXAgg')
-+    from matplotlib.figure import Figure
-+    from matplotlib.backends.backend_wxagg import \
-+    FigureCanvasWxAgg as FigCanvas
-+    from matplotlib.lines import Line2D
-+    from matplotlib.artist import Artist
-+    from matplotlib.mlab import dist_point_to_segment
-+    from matplotlib.patches import Polygon, Ellipse, Rectangle
-+    import matplotlib.image as mi
-+    import matplotlib.colors as mcolors
-+    import matplotlib.cbook as cbook
-+except ImportError as e:
-+    raise ImportError(_("Unable to import matplotlib (try to install it).\n%s") % e)
-+
-+import grass.script as grass
-+from grass.pydispatch.signal import Signal
-+
-+class ScatterPlotWidget(wx.Panel):
-+    def __init__(self, parent, scatt_id, scatt_mgr, transpose,
-+                 id = wx.ID_ANY):
-+
-+        wx.Panel.__init__(self, parent, id)
-+
-+        self.parent = parent
-+        self.full_extend = None
-+        self.mode = None
-+
-+        self._createWidgets()
-+        self._doLayout()
-+        self.scatt_id = scatt_id
-+        self.scatt_mgr = scatt_mgr
-+
-+        self.cidpress = None
-+        self.cidrelease = None
-+
-+        self.transpose = transpose
-+
-+        self.inverse = False
-+
-+        self.SetSize((200, 100))
-+        self.Layout()
-+
-+        self.base_scale = 1.2
-+        self.Bind(wx.EVT_CLOSE,lambda event : self.CleanUp())
-+
-+        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
-+        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")
-+
-+        self.contex_menu = ScatterPlotContextMenu(plot = self)
-+
-+        self.ciddscroll = None
-+
-+        self.canvas.mpl_connect('motion_notify_event', self.Motion)
-+        self.canvas.mpl_connect('button_press_event', self.OnPress)
-+        self.canvas.mpl_connect('button_release_event', self.OnRelease)
-+        self.canvas.mpl_connect('draw_event', self.draw_callback)
-+
-+    def draw_callback(self, event):
-+        self.polygon_drawer.draw_callback(event)
-+        self.axes.draw_artist(self.zoom_rect)
-+
-+    def _createWidgets(self):
-+
-+        # Create the mpl Figure and FigCanvas objects. 
-+        # 5x4 inches, 100 dots-per-inch
-+        #
-+        self.dpi = 100
-+        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
-+        self.fig.autolayout = True
-+
-+        self.canvas = FigCanvas(self, -1, self.fig)
-+        
-+        self.axes = self.fig.add_axes([0,0,1,1])
-+
-+        pol = Polygon(list(zip([0], [0])), animated=True)
-+        self.axes.add_patch(pol)
-+        self.polygon_drawer = PolygonDrawer(self.axes, pol = pol, empty_pol = True)
-+
-+        self.zoom_wheel_coords = None
-+        self.zoom_rect_coords = None
-+        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor = 'none')
-+        self.zoom_rect.set_visible(False)
-+        self.axes.add_patch(self.zoom_rect)
-+
-+    def ZoomToExtend(self):
-+        if self.full_extend:
-+            self.axes.axis(self.full_extend)
-+            self.canvas.draw()
-+
-+    def SetMode(self, mode):
-+        self._deactivateMode()
-+        if mode == 'zoom':
-+            self.ciddscroll = self.canvas.mpl_connect('scroll_event', self.ZoomWheel)
-+            self.mode = 'zoom'
-+        elif mode == 'zoom_extend':
-+            self.mode = 'zoom_extend'
-+        elif mode == 'pan':
-+            self.mode = 'pan'
-+        elif mode:
-+            self.polygon_drawer.SetMode(mode)
-+
-+    def SetSelectionPolygonMode(self, activate):
-+        self.polygon_drawer.SetSelectionPolygonMode(activate)
-+
-+    def _deactivateMode(self):
-+        self.mode  = None
-+        self.polygon_drawer.SetMode(None)
-+
-+        if self.ciddscroll:
-+            self.canvas.mpl_disconnect(self.ciddscroll)
-+
-+        self.zoom_rect.set_visible(False)
-+        self._stopCategoryEdit()
-+
-+    def GetCoords(self):
-+
-+        coords = self.polygon_drawer.GetCoords()
-+        if coords is None:
-+            return
-+
-+        if self.transpose:
-+            for c in coords:
-+                tmp = c[0]
-+                c[0] = c[1]
-+                c[1] = tmp
-+
-+        return coords
-+
-+    def SetEmpty(self):
-+        return self.polygon_drawer.SetEmpty()
-+
-+    def OnRelease(self, event):
-+        if not self.mode == "zoom": return
-+        self.zoom_rect.set_visible(False)
-+        self.ZoomRectangle(event)
-+        self.canvas.draw()
-+    
-+    def OnPress(self, event):
-+        'on button press we will see if the mouse is over us and store some data'
-+        if not event.inaxes:
-+            return
-+        if self.mode == "zoom_extend":
-+            self.ZoomToExtend()
-+
-+        if event.xdata and event.ydata:
-+            self.zoom_wheel_coords = { 'x' : event.xdata, 'y' : event.ydata}
-+            self.zoom_rect_coords = { 'x' : event.xdata, 'y' : event.ydata}
-+        else:
-+            self.zoom_wheel_coords = None
-+            self.zoom_rect_coords = None
-+
-+    def _stopCategoryEdit(self):
-+        'disconnect all the stored connection ids'
-+
-+        if self.cidpress:
-+            self.canvas.mpl_disconnect(self.cidpress)
-+        if self.cidrelease:
-+            self.canvas.mpl_disconnect(self.cidrelease)
-+        #self.canvas.mpl_disconnect(self.cidmotion)
-+
-+    def _doLayout(self):
-+        
-+        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
-+        self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
-+        self.SetSizer(self.main_sizer)
-+        self.main_sizer.Fit(self)
-+    
-+    def Plot(self, cats_order, scatts, ellipses, styles):
-+        """ Redraws the figure
-+        """
-+
-+        callafter_list = []
-+
-+        if self.full_extend:
-+            cx = self.axes.get_xlim()
-+            cy = self.axes.get_ylim()
-+            c = cx + cy
-+        else:
-+            c = None
-+
-+        
-+        #q = Queue()
-+        #p = Process(target=MergeImg, args=(cats_order, scatts, styles, self.transpose, q))
-+        #p.start()
-+        #merged_img, self.full_extend = q.get()
-+        #p.join()
-+        
-+        merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
-+        self.axes.clear()
-+        self.axes.axis('equal')
-+
-+        if self.transpose:
-+            merged_img = np.transpose(merged_img, (1, 0, 2))
-+
-+        img = imshow(self.axes, merged_img,
-+                     origin = 'lower',
-+                     interpolation='nearest',
-+                     aspect = "equal")
-+
-+        callafter_list.append([self.axes.draw_artist, [img]])
-+        callafter_list.append([grass.try_remove, [merged_img.filename]])
-+
-+        for cat_id in cats_order:
-+            if cat_id == 0:
-+                continue
-+            if not ellipses.has_key(cat_id):
-+                continue
-+                
-+            e = ellipses[cat_id]
-+            if not e:
-+                continue
-+
-+            colors = styles[cat_id]['color'].split(":")
-+            if self.transpose:
-+                e['theta'] = 360 - e['theta'] + 90
-+                if e['theta'] >= 360:
-+                    e['theta'] = abs(360 - e['theta']) 
-+                
-+                e['pos'] = [e['pos'][1], e['pos'][0]]
-+
-+            ellip = Ellipse(xy=e['pos'], 
-+                            width=e['width'], 
-+                            height=e['height'], 
-+                            angle=e['theta'], 
-+                            edgecolor="w",
-+                            linewidth=1.5, 
-+                            facecolor = 'None')
-+            self.axes.add_artist(ellip)
-+            callafter_list.append([self.axes.draw_artist, [ellip]])
-+
-+            color = map(lambda v : int(v)/255.0, styles[cat_id]['color'].split(":"))
-+
-+
-+            ellip = Ellipse(xy=e['pos'], 
-+                            width=e['width'], 
-+                            height=e['height'], 
-+                            angle=e['theta'], 
-+                            edgecolor=color,
-+                            linewidth=1, 
-+                            facecolor = 'None')
-+
-+            self.axes.add_artist(ellip)
-+            callafter_list.append([self.axes.draw_artist, [ellip]])
-+            
-+            center = Line2D([e['pos'][0]], [e['pos'][1]], 
-+                            marker='x',
-+                            markeredgecolor='w',
-+                            #markerfacecolor=color,
-+                            markersize=2)
-+            self.axes.add_artist(center)
-+            callafter_list.append([self.axes.draw_artist, [center]])
-+
-+        callafter_list.append([self.fig.canvas.blit, []])
-+
-+        if c:
-+            self.axes.axis(c)
-+        wx.CallAfter(lambda : self.CallAfter(callafter_list))
-+    
-+    def CallAfter(self, funcs_list):
-+        while funcs_list: 
-+            fcn, args = funcs_list.pop(0) 
-+            fcn(*args) 
-+
-+        self.canvas.draw()
-+
-+    def CleanUp(self):
-+        self.plotClosed.emit(scatt_id = self.scatt_id)
-+        self.Destroy()
-+
-+    def ZoomWheel(self, event):
-+        # get the current x and y limits
-+        if not event.inaxes:
-+            return
-+        # tcaswell
-+        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
-+        cur_xlim = self.axes.get_xlim()
-+        cur_ylim = self.axes.get_ylim()
-+        
-+        xdata = event.xdata
-+        ydata = event.ydata 
-+        if event.button == 'up':
-+            scale_factor = 1/self.base_scale
-+        elif event.button == 'down':
-+            scale_factor = self.base_scale
-+        else:
-+            scale_factor = 1
-+
-+        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
-+                  xdata + (cur_xlim[1] - xdata) * scale_factor, 
-+                  ydata - (ydata - cur_ylim[0]) * scale_factor,
-+                  ydata + (cur_ylim[1] - ydata) * scale_factor)
-+
-+        self.axes.axis(extend)
-+        
-+        self.canvas.draw()
-+
-+
-+    def ZoomRectangle(self, event):
-+        # get the current x and y limits
-+        if not self.mode == "zoom": return
-+        if event.inaxes is None: return
-+        if event.button != 1: return
-+
-+        cur_xlim = self.axes.get_xlim()
-+        cur_ylim = self.axes.get_ylim()
-+        
-+        x1, y1 = event.xdata, event.ydata
-+        x2 = deepcopy(self.zoom_rect_coords['x'])
-+        y2 = deepcopy(self.zoom_rect_coords['y'])
-+
-+        if x1 == x2 or y1 == y2:
-+            return
-+
-+        self.axes.set_xlim(x1, x2)#, auto = True)
-+        self.axes.set_ylim(y1, y2)#, auto = True)
-+        self.canvas.draw()
-+
-+    def Motion(self, event):
-+        self.PanMotion(event)
-+        self.ZoomRectMotion(event)
-+        
-+        if event.inaxes is None: 
-+            return
-+        
-+        self.cursorMove.emit(x=event.xdata, y=event.ydata)
-+
-+    def PanMotion(self, event):
-+        'on mouse movement'
-+        if not self.mode == "pan": 
-+            return
-+        if event.inaxes is None: 
-+            return
-+        if event.button != 1: 
-+            return
-+
-+        cur_xlim = self.axes.get_xlim()
-+        cur_ylim = self.axes.get_ylim()
-+
-+        x,y = event.xdata, event.ydata
-+        
-+        mx = (x - self.zoom_wheel_coords['x']) * 0.5
-+        my = (y - self.zoom_wheel_coords['y']) * 0.5
-+
-+        extend = (cur_xlim[0] + mx, cur_xlim[1] + mx, cur_ylim[0] + my, cur_ylim[1] + my)
-+
-+        self.zoom_wheel_coords['x'] = x
-+        self.zoom_wheel_coords['y'] = y
-+
-+        self.axes.axis(extend)
-+
-+        #self.canvas.copy_from_bbox(self.axes.bbox)
-+        #self.canvas.restore_region(self.background)
-+        self.canvas.draw()
-+        
-+    def ZoomRectMotion(self, event):
-+        if not self.mode == "zoom": return
-+        if event.inaxes is None: return
-+        if event.button != 1: return
-+
-+        x1, y1 = event.xdata, event.ydata
-+        self.zoom_rect.set_visible(True)
-+        x2 = self.zoom_rect_coords['x']
-+        y2 = self.zoom_rect_coords['y']
-+
-+        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))
-+
-+        #self.axes.draw_artist(self.zoom_rect)
-+        self.canvas.draw()
-+
-+class ScatterPlotContextMenu:
-+    def __init__(self, plot):
-+
-+        self.plot = plot
-+        self.canvas = plot.canvas
-+        self.cidpress = self.canvas.mpl_connect(
-+            'button_press_event', self.ContexMenu)
-+   
-+    def ContexMenu(self, event):
-+        if not event.inaxes:
-+            return
-+
-+        if event.button == 3:
-+            menu = wx.Menu()       
-+            menu_items = [["zoom_to_extend", _("Zoom to scatter plot extend"), lambda event : self.plot.ZoomToExtend()]]
-+
-+            for item in menu_items:
-+                item_id = wx.ID_ANY
-+                menu.Append(item_id, text = item[1])
-+                menu.Bind(wx.EVT_MENU, item[2], id = item_id)
-+
-+            wx.CallAfter(self.ShowMenu, menu) 
-+   
-+    def ShowMenu(self, menu):
-+        self.plot.PopupMenu(menu)
-+        menu.Destroy()
-+        self.plot.ReleaseMouse() 
-+
-+class PolygonDrawer:
-+    """
-+    An polygon editor.
-+    """
-+    def __init__(self, ax, pol, empty_pol):
-+        if pol.figure is None:
-+            raise RuntimeError('You must first add the polygon to a figure or canvas before defining the interactor')
-+        self.ax = ax
-+        self.canvas = pol.figure.canvas
-+
-+        self.showverts = True
-+
-+        self.pol = pol
-+        self.empty_pol = empty_pol
-+
-+        x, y = zip(*self.pol.xy)
-+
-+        style = self._getPolygonStyle()
-+
-+        self.line = Line2D(x, y, marker='o', markerfacecolor='r', animated=True)
-+        self.ax.add_line(self.line)
-+        #self._update_line(pol)
-+
-+        cid = self.pol.add_callback(self.poly_changed)
-+        self.moving_ver_idx = None # the active vert
-+
-+        self.mode = None
-+
-+        if self.empty_pol:
-+            self._show(False)
-+
-+        #self.canvas.mpl_connect('draw_event', self.draw_callback)
-+        self.canvas.mpl_connect('button_press_event', self.OnButtonPressed)
-+        self.canvas.mpl_connect('button_release_event', self.button_release_callback)
-+        self.canvas.mpl_connect('motion_notify_event', self.motion_notify_callback)
-+    
-+        self.it = 0
-+
-+    def _getPolygonStyle(self):
-+        style = {}
-+        style['sel_pol'] = UserSettings.Get(group='scatt', 
-+                                            key='selection', 
-+                                            subkey='sel_pol')
-+        style['sel_pol_vertex'] = UserSettings.Get(group='scatt', 
-+                                                   key='selection', 
-+                                                   subkey='sel_pol_vertex')
-+
-+        style['sel_pol'] = [i / 255.0 for i in style['sel_pol']] 
-+        style['sel_pol_vertex'] = [i / 255.0 for i in style['sel_pol_vertex']] 
-+
-+        return style
-+
-+    def _getSnapTresh(self):
-+        return UserSettings.Get(group='scatt', 
-+                                key='selection', 
-+                                subkey='snap_tresh')
-+
-+    def SetMode(self, mode):
-+        self.mode = mode
-+
-+    def SetSelectionPolygonMode(self, activate):
-+        
-+        self.Show(activate)
-+        if not activate and self.mode:
-+            self.SetMode(None) 
-+
-+    def Show(self, show):
-+        if show:
-+            if not self.empty_pol:
-+                self._show(True)
-+        else:
-+            self._show(False)
-+
-+    def GetCoords(self):
-+        if self.empty_pol:
-+            return None
-+
-+        coords = deepcopy(self.pol.xy)
-+        return coords
-+
-+    def SetEmpty(self):
-+        self._setEmptyPol(True)
-+
-+    def _setEmptyPol(self, empty_pol):
-+        self.empty_pol = empty_pol
-+        self._show(not empty_pol)
-+
-+    def _show(self, show):
-+
-+        self.show = show
-+
-+        self.line.set_visible(self.show)
-+        self.pol.set_visible(self.show)
-+
-+        self.Redraw()
-+
-+    def Redraw(self):
-+        if self.show:
-+            self.ax.draw_artist(self.pol)
-+            self.ax.draw_artist(self.line)
-+        self.canvas.blit(self.ax.bbox)
-+        self.canvas.draw()
-+
-+    def draw_callback(self, event):
-+
-+        style=self._getPolygonStyle()
-+        self.pol.set_facecolor(style['sel_pol'])
-+        self.line.set_markerfacecolor(style['sel_pol_vertex'])
-+
-+        self.background = self.canvas.copy_from_bbox(self.ax.bbox)
-+        self.ax.draw_artist(self.pol)
-+        self.ax.draw_artist(self.line)
-+    
-+    def poly_changed(self, pol):
-+        'this method is called whenever the polygon object is called'
-+        # only copy the artist props to the line (except visibility)
-+        vis = self.line.get_visible()
-+        Artist.update_from(self.line, pol)
-+        self.line.set_visible(vis)  # don't use the pol visibility state
-+
-+    def get_ind_under_point(self, event):
-+        'get the index of the vertex under point if within treshold'
-+
-+        # display coords
-+        xy = np.asarray(self.pol.xy)
-+        xyt = self.pol.get_transform().transform(xy)
-+        xt, yt = xyt[:, 0], xyt[:, 1]
-+        d = np.sqrt((xt-event.x)**2 + (yt-event.y)**2)
-+        indseq = np.nonzero(np.equal(d, np.amin(d)))[0]
-+        ind = indseq[0]
-+
-+        if d[ind]>=self._getSnapTresh():
-+            ind = None
-+
-+        return ind
-+
-+    def OnButtonPressed(self, event):
-+        if not event.inaxes:
-+            return
-+
-+        if event.button in [2, 3]: 
-+            return
-+
-+        if self.mode == "delete_vertex":
-+            self._deleteVertex(event)
-+        elif self.mode == "add_boundary_vertex":
-+            self._addVertexOnBoundary(event)
-+        elif self.mode == "add_vertex":
-+            self._addVertex(event)
-+        elif self.mode == "remove_polygon":
-+            self.SetEmpty()
-+        self.moving_ver_idx = self.get_ind_under_point(event)
-+
-+    def button_release_callback(self, event):
-+        'whenever a mouse button is released'
-+        if not self.showverts: return
-+        if event.button != 1: return
-+        self.moving_ver_idx = None
-+
-+    def ShowVertices(self, show):
-+        self.showverts = show
-+        self.line.set_visible(self.showverts)
-+        if not self.showverts: self.moving_ver_idx = None
-+
-+    def _deleteVertex(self, event):
-+        ind = self.get_ind_under_point(event)
-+
-+        if ind  is None or self.empty_pol:
-+            return
-+
-+        if len(self.pol.xy) <= 2:
-+            self.empty_pol = True
-+            self._show(False)
-+            return
-+
-+        coords = []
-+        for i,tup in enumerate(self.pol.xy): 
-+            if i == ind:
-+                continue
-+            elif i == 0 and ind == len(self.pol.xy) - 1:
-+                continue
-+            elif i == len(self.pol.xy) - 1 and ind == 0: 
-+                continue
-+
-+            coords.append(tup)
-+
-+        self.pol.xy = coords
-+        self.line.set_data(zip(*self.pol.xy))
-+
-+        self.Redraw()
-+
-+    def _addVertexOnBoundary(self, event):
-+        if self.empty_pol:
-+            return
-+
-+        xys = self.pol.get_transform().transform(self.pol.xy)
-+        p = event.x, event.y # display coords
-+        for i in range(len(xys)-1):
-+            s0 = xys[i]
-+            s1 = xys[i+1]
-+            d = dist_point_to_segment(p, s0, s1)
-+
-+            if d<=self._getSnapTresh():
-+                self.pol.xy = np.array(
-+                    list(self.pol.xy[:i + 1]) +
-+                    [(event.xdata, event.ydata)] +
-+                    list(self.pol.xy[i + 1:]))
-+                self.line.set_data(zip(*self.pol.xy))
-+                break
-+
-+        self.Redraw()
-+
-+    def _addVertex(self, event):
-+
-+        if self.empty_pol:
-+            pt = (event.xdata, event.ydata)
-+            self.pol.xy = np.array([pt, pt])
-+            self._show(True)
-+            self.empty_pol = False
-+        else:
-+            self.pol.xy = np.array(
-+                        [(event.xdata, event.ydata)] +
-+                        list(self.pol.xy[1:]) +
-+                        [(event.xdata, event.ydata)])
-+
-+        self.line.set_data(zip(*self.pol.xy))
-+        
-+        self.Redraw()
-+
-+    def motion_notify_callback(self, event):
-+        'on mouse movement'
-+        if not self.mode == "move_vertex": return
-+        if not self.showverts: return
-+        if self.empty_pol: return
-+        if self.moving_ver_idx is None: return
-+        if event.inaxes is None: return
-+        if event.button != 1: return
-+
-+        self.it += 1
-+
-+        x,y = event.xdata, event.ydata
-+
-+        self.pol.xy[self.moving_ver_idx] = x,y
-+        if self.moving_ver_idx == 0:
-+            self.pol.xy[len(self.pol.xy) - 1] = x,y
-+        elif self.moving_ver_idx == len(self.pol.xy) - 1:
-+            self.pol.xy[0] = x,y
-+
-+        self.line.set_data(zip(*self.pol.xy))
-+
-+        self.canvas.restore_region(self.background)
-+
-+        self.Redraw()
-+
-+class ModestImage(mi.AxesImage):
-+    """
-+    Computationally modest image class.
-+
-+    ModestImage is an extension of the Matplotlib AxesImage class
-+    better suited for the interactive display of larger images. Before
-+    drawing, ModestImage resamples the data array based on the screen
-+    resolution and view window. This has very little affect on the
-+    appearance of the image, but can substantially cut down on
-+    computation since calculations of unresolved or clipped pixels
-+    are skipped.
-+
-+    The interface of ModestImage is the same as AxesImage. However, it
-+    does not currently support setting the 'extent' property. There
-+    may also be weird coordinate warping operations for images that
-+    I'm not aware of. Don't expect those to work either.
-+
-+    Author: Chris Beaumont <beaumont at hawaii.edu>
-+    """
-+    def __init__(self, *args, **kwargs):
-+        if 'extent' in kwargs and kwargs['extent'] is not None:
-+            raise NotImplementedError("ModestImage does not support extents")
-+
-+        self._full_res = None
-+        self._sx, self._sy = None, None
-+        self._bounds = (None, None, None, None)
-+        super(ModestImage, self).__init__(*args, **kwargs)
-+
-+    def set_data(self, A):
-+        """
-+        Set the image array
-+
-+        ACCEPTS: numpy/PIL Image A
-+        """
-+        self._full_res = A
-+        self._A = A
-+
-+        if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype,
-+                                                         np.float):
-+            raise TypeError("Image data can not convert to float")
-+
-+        if (self._A.ndim not in (2, 3) or
-+            (self._A.ndim == 3 and self._A.shape[-1] not in (3, 4))):
-+            raise TypeError("Invalid dimensions for image data")
-+
-+        self._imcache =None
-+        self._rgbacache = None
-+        self._oldxslice = None
-+        self._oldyslice = None
-+        self._sx, self._sy = None, None
-+
-+    def get_array(self):
-+        """Override to return the full-resolution array"""
-+        return self._full_res
-+
-+    def _scale_to_res(self):
-+        """ Change self._A and _extent to render an image whose
-+        resolution is matched to the eventual rendering."""
-+
-+        ax = self.axes
-+        ext = ax.transAxes.transform([1, 1]) - ax.transAxes.transform([0, 0])
-+        xlim, ylim = ax.get_xlim(), ax.get_ylim()
-+        dx, dy = xlim[1] - xlim[0], ylim[1] - ylim[0]
-+
-+        y0 = max(0, ylim[0] - 5)
-+        y1 = min(self._full_res.shape[0], ylim[1] + 5)
-+        x0 = max(0, xlim[0] - 5)
-+        x1 = min(self._full_res.shape[1], xlim[1] + 5)
-+        y0, y1, x0, x1 = map(int, [y0, y1, x0, x1])
-+
-+        sy = int(max(1, min((y1 - y0) / 5., np.ceil(dy / ext[1]))))
-+        sx = int(max(1, min((x1 - x0) / 5., np.ceil(dx / ext[0]))))
-+
-+        # have we already calculated what we need?
-+        if sx == self._sx and sy == self._sy and \
-+            x0 == self._bounds[0] and x1 == self._bounds[1] and \
-+            y0 == self._bounds[2] and y1 == self._bounds[3]:
-+            return
-+
-+        self._A = self._full_res[y0:y1:sy, x0:x1:sx]
-+        self._A = cbook.safe_masked_invalid(self._A)
-+        x1 = x0 + self._A.shape[1] * sx
-+        y1 = y0 + self._A.shape[0] * sy
-+
-+        self.set_extent([x0 - .5, x1 - .5, y0 - .5, y1 - .5])
-+        self._sx = sx
-+        self._sy = sy
-+        self._bounds = (x0, x1, y0, y1)
-+        self.changed()
-+
-+    def draw(self, renderer, *args, **kwargs):
-+        self._scale_to_res()
-+        super(ModestImage, self).draw(renderer, *args, **kwargs)
-+
-+def imshow(axes, X, cmap=None, norm=None, aspect=None,
-+           interpolation=None, alpha=None, vmin=None, vmax=None,
-+           origin=None, extent=None, shape=None, filternorm=1,
-+           filterrad=4.0, imlim=None, resample=None, url=None, **kwargs):
-+    """Similar to matplotlib's imshow command, but produces a ModestImage
-+
-+    Unlike matplotlib version, must explicitly specify axes
-+    Author: Chris Beaumont <beaumont at hawaii.edu>
-+    """
-+
-+    if not axes._hold:
-+        axes.cla()
-+    if norm is not None:
-+        assert(isinstance(norm, mcolors.Normalize))
-+    if aspect is None:
-+        aspect = rcParams['image.aspect']
-+    axes.set_aspect(aspect)
-+    im = ModestImage(axes, cmap, norm, interpolation, origin, extent,
-+                            filternorm=filternorm,
-+                            filterrad=filterrad, resample=resample, **kwargs)
-+
-+    im.set_data(X)
-+    im.set_alpha(alpha)
-+    axes._set_artist_props(im)
-+
-+    if im.get_clip_path() is None:
-+        # image does not already have clipping set, clip to axes patch
-+        im.set_clip_path(axes.patch)
-+
-+    #if norm is None and shape is None:
-+    #    im.set_clim(vmin, vmax)
-+    if vmin is not None or vmax is not None:
-+        im.set_clim(vmin, vmax)
-+    else:
-+        im.autoscale_None()
-+    im.set_url(url)
-+
-+    # update ax.dataLim, and, if autoscaling, set viewLim
-+    # to tightly fit the image, regardless of dataLim.
-+    im.set_extent(im.get_extent())
-+
-+    axes.images.append(im)
-+    im._remove_method = lambda h: axes.images.remove(h)
-+
-+    return im
-+
-+def MergeImg(cats_order, scatts, styles, output_queue):
-+
-+        start_time = time.clock()
-+        cmap_time = 0
-+        merge_time = 0
-+        mask_time = 0
-+        norm_time = 0
-+        max_time = 0
-+
-+        init = True
-+        merge_tmp = grass.tempfile()
-+        for cat_id in cats_order:
-+            if not scatts.has_key(cat_id):
-+                continue
-+            scatt = scatts[cat_id]
-+            #print "color map %d" % cat_id
-+            #TODO make more general
-+            if cat_id != 0 and (styles[cat_id]['opacity'] == 0.0 or \
-+               not styles[cat_id]['show']):
-+                continue
-+            if init:
-+
-+                b2_i = scatt['bands_info']['b1']
-+                b1_i = scatt['bands_info']['b2']
-+
-+                full_extend = (b1_i['min'] - 0.5, b1_i['max'] + 0.5, b2_i['min'] - 0.5, b2_i['max'] + 0.5) 
-+            
-+            if cat_id == 0:
-+                cmap = matplotlib.cm.jet
-+                cmap.set_bad('w',1.)
-+                cmap._init()
-+                cmap._lut[len(cmap._lut) - 1, -1] = 0
-+            else:
-+                colors = styles[cat_id]['color'].split(":")
-+
-+                cmap = matplotlib.cm.jet
-+                cmap.set_bad('w',1.)
-+                cmap._init()
-+                cmap._lut[len(cmap._lut) - 1, -1] = 0
-+                cmap._lut[:, 0] = int(colors[0])/255.0
-+                cmap._lut[:, 1] = int(colors[1])/255.0
-+                cmap._lut[:, 2] = int(colors[2])/255.0
-+
-+
-+            #if init:
-+            tmp = time.clock()
-+
-+            masked_cat = np.ma.masked_less_equal(scatt['np_vals'], 0)
-+            mask_time += time.clock() - tmp
-+
-+            tmp = time.clock()
-+            vmax = np.amax(masked_cat)
-+            max_time += time.clock() - tmp
-+
-+            tmp = time.clock()
-+            masked_cat = np.uint8(masked_cat * (255.0 / float(vmax)))
-+            norm_time += time.clock() - tmp
-+
-+            #print "ahoj"
-+            #print masked_cat
-+            #print masked_cat.shape
-+            #print masked_cat.mask.dtype
-+            #
-+            #print "colored_cat.shape"
-+            #print colored_cat.shape
-+            #merged_img = np.memmap(merge_tmp, dtype='uint8',, shape=colored_cat.shape)
-+            tmp = time.clock()
-+            cmap = np.uint8(cmap._lut * 255)
-+            sh =masked_cat.shape
-+
-+            colored_cat = np.zeros(dtype='uint8', shape=(sh[0], sh[1], 4))
-+            ApplyColormap(masked_cat, masked_cat.mask, cmap, colored_cat)
-+
-+            #colored_cat = np.uint8(cmap(masked_cat) * 255)
-+            cmap_time += time.clock() - tmp
-+
-+            del masked_cat
-+            del cmap
-+            
-+            #colored_cat[...,3] = np.choose(masked_cat.mask, (255, 0))
-+
-+            tmp = time.clock()
-+            if init:
-+                merged_img = np.memmap(merge_tmp, dtype='uint8', mode='w+', shape=colored_cat.shape)
-+                merged_img[:] = colored_cat[:]
-+                init = False
-+            else:
-+                MergeArrays(merged_img, colored_cat, styles[cat_id]['opacity'])
-+
-+            """
-+                #c_img_a = np.memmap(grass.tempfile(), dtype="uint16", mode='w+', shape = shape) 
-+                c_img_a = colored_cat.astype('uint16')[:,:,3] * styles[cat_id]['opacity']
-+
-+                #TODO apply strides and there will be no need for loop
-+                #b = as_strided(a, strides=(0, a.strides[3], a.strides[3], a.strides[3]), shape=(3, a.shape[0], a.shape[1]))
-+                
-+                for i in range(3):
-+                    merged_img[:,:,i] = (merged_img[:,:,i] * (255 - c_img_a) + colored_cat[:,:,i] * c_img_a) / 255;
-+                merged_img[:,:,3] = (merged_img[:,:,3] * (255 - c_img_a) + 255 * c_img_a) / 255;
-+                
-+                del c_img_a
-+            """
-+            merge_time += time.clock() - tmp
-+
-+            del colored_cat
-+
-+        end_time =  time.clock() - start_time
-+        #print "all time:%f" % (end_time)
-+        #print "cmap_time:%f" % (cmap_time / end_time * 100.0 )
-+        #print "merge_time:%f" % (merge_time / end_time * 100.0 )
-+        #print "mask_time:%f" % (mask_time / end_time * 100.0 )
-+        #print "nax_time:%f" % (max_time / end_time * 100.0 )
-+        #print "norm_time:%f" % (norm_time / end_time * 100.0 )
-+        
-+        #output_queue.put((merged_img, full_extend))
-+        return merged_img, full_extend 
-\ No newline at end of file
-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,816 @@
-+"""!
-+ at package scatt_plot.controllers
-+
-+ at brief Controller layer for scatter plot tool.
-+
-+Classes:
-+
-+(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.
-+
-+ at author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
-+"""
-+import os
-+import sys
-+
-+#TODO just for testing
-+import time
-+
-+#TODO
-+import wx
-+
-+from core.gcmd import GException, GError, GMessage, RunCommand
-+
-+from scatt_plot.scatt_core import Core, idBandsToidScatt
-+
-+from scatt_plot.dialogs import AddScattPlotDialog, ExportCategoryRaster
-+from scatt_plot.gthreading import gThread
-+from core.gconsole import EVT_CMD_DONE
-+from core.settings import UserSettings
-+from grass.pydispatch.signal import Signal
-+import grass.script as grass
-+
-+from grass.pydispatch.signal import Signal
-+
-+class ScattsManager(wx.EvtHandler):
-+    def __init__(self, guiparent, giface, iclass_mapwin):
-+        #TODO remove iclass parameter
-+
-+        wx.EvtHandler.__init__(self)
-+        self.giface = giface
-+        self.mapDisp  = giface.GetMapDisplay()
-+
-+        if iclass_mapwin:
-+            self.mapWin = iclass_mapwin
-+        else:
-+            self.mapWin = giface.GetMapWindow()
-+
-+        self.guiparent = guiparent
-+
-+        self.show_add_scatt_plot = False
-+
-+        self.core = Core()
-+        self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()
-+
-+        self.cats_mgr = CategoriesManager(self, self.core)
-+
-+        self.thread = gThread(self);
-+        
-+        self.plots = {}
-+        self.added_cats_rasts = []
-+
-+        self.cats_to_update = []
-+
-+        self.plot_mode =  None
-+        self.pol_sel_mode = [False, None]
-+
-+        self.data_set = False
-+
-+        self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")
-+
-+        self.renderingStarted = Signal("ScattsManager.renderingStarted")
-+        self.renderingFinished = Signal("ScattsManager.renderingFinished")
-+
-+        self.computingStarted = Signal("ScattsManager.computingStarted")
-+
-+        if iclass_mapwin: 
-+            self.mapWin_conn = MapWinConnection(self, self.mapWin, self.core.CatRastUpdater())
-+            self.iclass_conn = IClassConnection(self, iclass_mapwin.parent, self.cats_mgr)
-+        else:
-+            self.mapWin_conn = None
-+            self.iclass_conn = None
-+
-+        self.tasks_pids = {
-+                            'add_scatt' : [],
-+                            'set_data' : -1,
-+                            'set_data_add' : -1,
-+                            'set_edit_cat_data' : -1,
-+                            'mapwin_conn' : [],
-+                            'render_plots' : -1,
-+                            'render' : [],
-+                            'export_cat_rast' : []
-+                           }
-+
-+        self._initSettings()
-+
-+        self.Bind(EVT_CMD_DONE, self.OnThreadDone)
-+
-+        self.modeSet = Signal("ScattsManager.mondeSet")
-+
-+    def CleanUp(self):
-+        self.core.CleanUp()
-+        self.added_cats_rasts = []
-+
-+        self.cats_mgr.CleanUp()
-+        for scatt_id, scatt in self.plots.items():
-+            scatt.CleanUp()
-+
-+    def _initSettings(self):
-+        """!Initialization of settings (if not already defined)"""
-+        # initializes default settings
-+        initSettings = [
-+                        ['selection', 'sel_pol', (255,255,0)],
-+                        ['selection', 'sel_pol_vertex', (255,0,0)],
-+                        ['selection', 'sel_area', (0,255,19)],
-+                        ['selection', "snap_tresh", 10],
-+                        ['selection', 'sel_area_opacty', 50],
-+                        ['ellipses', 'show_ellips', True],
-+                       ]
-+
-+        for init in initSettings: 
-+            UserSettings.ReadSettingsFile()
-+            UserSettings.Append(dict = UserSettings.userSettings, 
-+                                    group ='scatt',
-+                                    key = init[0],
-+                                    subkey =init[1],
-+                                    value = init[2],
-+                                    overwrite = False)
-+
-+    def OnThreadDone(self, event):
-+        
-+        if event.exception:
-+            GError(str(event.exception))
-+            return
-+
-+        if event.pid in self.tasks_pids['mapwin_conn']:
-+            self.tasks_pids['mapwin_conn'].remove(event.pid)
-+            updated_cats = event.ret
-+
-+            for cat in updated_cats:
-+                if cat not in  self.cats_to_update:
-+                    self.cats_to_update.append(cat)
-+
-+            if not self.tasks_pids['mapwin_conn']:
-+                self.tasks_pids['render_plots'] = self.thread.GetId()
-+                self.thread.Run(callable = self.core.ComputeCatsScatts, 
-+                                cats_ids = self.cats_to_update[:])
-+                del self.cats_to_update[:]
-+ 
-+            return
-+
-+        if self.tasks_pids['render_plots'] == event.pid:
-+            self.RenderScattPlts()
-+            return
-+
-+        if event.pid in self.tasks_pids['render']:
-+            self.tasks_pids['render'].remove(event.pid)
-+            if not self.tasks_pids['render']:
-+                self.renderingFinished.emit()
-+            return
-+            
-+        if event.pid in self.tasks_pids['add_scatt']:
-+            self.tasks_pids['add_scatt'].remove(event.pid)
-+            self.AddScattPlotDone(event)
-+            return
-+
-+        if self.tasks_pids['set_data'] == event.pid:
-+            self.SetDataDone(event)
-+            return
-+        
-+        if self.tasks_pids['set_data_add'] == event.pid:
-+            self.SetDataDone(event)
-+            self.AddScattPlot()
-+            return
-+        
-+        if self.tasks_pids['set_edit_cat_data'] == event.pid:
-+            self.SetEditCatDataDone(event)
-+            return
-+
-+        if event.pid in self.tasks_pids['export_cat_rast']:
-+            self.cats_mgr.OnExportCatRastDone(event)
-+            return
-+
-+    def SetData(self, bands):
-+        "setting datas"
-+        self.CleanUp()
-+        self.data_set = False
-+        
-+        if self.show_add_scatt_plot:
-+            self.tasks_pids['set_data_add'] = self.thread.GetId()
-+        else:
-+            self.tasks_pids['set_data'] = self.thread.GetId()
-+
-+        self.thread.Run(callable=self.core.SetData, bands=bands)
-+
-+    def SetDataDone(self, event):
-+        self.data_set = True
-+        self.iclass_conn.SyncCats()
-+
-+    def SettingsUpdated(self, event):
-+        #TODO optimization
-+        self.RenderScattPlts()
-+
-+    def OnOutput(self, event):
-+        """!Print thread output according to debug level.
-+        """
-+        print event.text
-+
-+    def GetBands(self):
-+        return self.core.GetBands()
-+
-+    def AddScattPlot(self):
-+        if not self.data_set and self.iclass_conn:
-+            self.show_add_scatt_plot = True
-+            self.iclass_conn.SetData()
-+            self.show_add_scatt_plot = False
-+            return
-+        if not self.data_set:
-+            GError(_('No data set.'))
-+            return
-+
-+        self.computingStarted.emit()
-+
-+        bands = self.core.GetBands()
-+        dlg = AddScattPlotDialog(parent = self.guiparent, bands = bands)
-+        if dlg.ShowModal() == wx.ID_OK:
-+
-+            b_1, b_2 = dlg.GetBands()
-+           
-+            #TODO axes selection
-+            transpose = False
-+            if b_1 > b_2:
-+                transpose = True
-+                tmp_band = b_2
-+                b_2 = b_1
-+                b_1 = tmp_band
-+
-+            self.scatt_id = idBandsToidScatt(b_1, b_2, len(bands))
-+            self._addScattPlot(self.scatt_id, transpose)
-+        
-+        dlg.Destroy()
-+
-+        self.mapWin_conn.Update()
-+         
-+    def _addScattPlot(self, scatt_id, transpose):
-+        if self.plots.has_key(scatt_id):
-+            GMessage(_("Scatter plot has been already added."))
-+            return
-+
-+        self.tasks_pids['add_scatt'].append(self.thread.GetId())
-+        
-+        self.thread.Run(callable = self.core.AddScattPlot, scatt_id = scatt_id, userdata = {'transpose' : transpose})
-+
-+    def RenderScattPlts(self, scatt_ids = None):
-+        if len(self.tasks_pids['render']) > 1:
-+            return 
-+        
-+        self.renderingStarted.emit()
-+        self.tasks_pids['render'].append(self.thread.GetId())
-+        self.thread.Run(callable = self._renderscattplts, scatt_ids = scatt_ids)
-+
-+    def _renderscattplts(self, scatt_ids):
-+        cats_attrs = self.cats_mgr.GetCategoriesAttrs()
-+        cats = self.cats_mgr.GetCategories()[:]
-+        cats.reverse()
-+        cats.insert(0, 0)
-+        for i_scatt_id, scatt in self.plots.items():
-+            if scatt_ids is not None and i_scatt_id not in scatt_ids:
-+                continue
-+
-+            scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
-+            if self._showConfEllipses():
-+                ellipses_dt = self.scatts_dt.GetEllipses(i_scatt_id, cats_attrs)
-+            else:
-+                ellipses_dt = {}
-+
-+            if self.pol_sel_mode[0]:
-+                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)
-+
-+            scatt.Plot(cats_order = cats, scatts = scatt_dt, ellipses = ellipses_dt, styles = cats_attrs)
-+
-+    def _showConfEllipses(self):
-+        return UserSettings.Get(group='scatt', 
-+                                key="ellipses", 
-+                                subkey="show_ellips")
-+
-+    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):
-+
-+        cat_id = self.cats_mgr.GetSelectedCat()
-+        if not cat_id:
-+            return
-+
-+        sel_a_cat_id = max(cats_attrs.keys()) + 1        
-+
-+        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
-+        if not s:
-+            return
-+
-+        cats_order.append(sel_a_cat_id)
-+
-+        col = UserSettings.Get(group='scatt', 
-+                                 key='selection', 
-+                                 subkey='sel_area')
-+
-+        col = ":".join(map(str, col))
-+        opac = UserSettings.Get(group='scatt', 
-+                                key='selection', 
-+                                subkey='sel_area_opacty') / 100.0
-+
-+        cats_attrs[sel_a_cat_id] = {'color' : col,
-+                                    'opacity' : opac,
-+                                    'show' : True}
-+
-+        scatt_dt[sel_a_cat_id] = s[cat_id]
-+
-+    def AddScattPlotDone(self, event):
-+        
-+        transpose = event.userdata['transpose']
-+        scatt_id = event.kwds['scatt_id']
-+
-+        #TODO guiparent - not very good
-+        self.plots[scatt_id] = self.guiparent.NewScatterPlot(scatt_id = scatt_id, transpose = transpose)
-+
-+        self.plots[scatt_id].plotClosed.connect(self.PlotClosed)
-+        self.plots[scatt_id].cursorMove.connect(lambda x, y: self.cursorPlotMove.emit(x = x, y = y))
-+
-+        if self.plot_mode:
-+            self.plots[scatt_id].SetMode(self.plot_mode)
-+            self.plots[scatt_id].ZoomToExtend()
-+
-+        self.RenderScattPlts(scatt_ids = [scatt_id])
-+
-+    def PlotClosed(self, scatt_id):
-+        del self.plots[scatt_id]
-+
-+    def SetPlotsMode(self, mode):
-+
-+        self.plot_mode = mode
-+        for scatt in self.plots.itervalues():
-+            scatt.SetMode(mode)
-+
-+        self.modeSet.emit(mode = mode)
-+
-+    def ActivateSelectionPolygonMode(self, activate):
-+        self.pol_sel_mode[0] = activate
-+        for scatt in self.plots.itervalues():
-+            scatt.SetSelectionPolygonMode(activate)
-+            if not activate and self.plot_mode not in ['zoom', 'pan', 'zoom_extend']:
-+                self.SetPlotsMode(None)
-+
-+        self.RenderScattPlts()
-+        return activate
-+
-+    def ProcessSelectionPolygons(self, process_mode):        
-+        scatts_polygons = {}        
-+        for scatt_id, scatt in self.plots.iteritems():
-+            coords = scatt.GetCoords()
-+            if coords is not None:
-+                scatts_polygons[scatt_id] = coords
-+
-+        if not scatts_polygons:
-+            return
-+
-+        value = 1
-+        if process_mode == 'remove':
-+            value = 0
-+
-+        sel_cat_id = self.cats_mgr.GetSelectedCat()
-+        if not sel_cat_id:
-+            dlg = wx.MessageDialog(parent = self.guiparent,
-+                      message = _("In order to select arrea in scatter plot, "
-+                                  "you have to select class first.\n\n"
-+                                  "There is no class yet, "
-+                                  "do you want to create one?"),
-+                      caption = _("No class selected"),
-+                      style = wx.YES_NO)
-+            if dlg.ShowModal() == wx.ID_YES:
-+                self.iclass_conn.EmptyCategories()
-+
-+        sel_cat_id = self.cats_mgr.GetSelectedCat()
-+        if not sel_cat_id:
-+            return
-+
-+        for scatt in self.plots.itervalues():
-+            scatt.SetEmpty()
-+
-+        self.computingStarted.emit()
-+
-+        self.tasks_pids['set_edit_cat_data'] = self.thread.GetId()
-+        self.thread.Run(callable = self.core.UpdateCategoryWithPolygons, 
-+                        cat_id = sel_cat_id,
-+                        scatts_pols = scatts_polygons,
-+                        value = value)
-+
-+    def SetEditCatDataDone(self, event):
-+        if event.exception:
-+            GError(_("Error occured during computation of scatter plot category:\n%s"), 
-+                      parent = self.guiparent, showTraceback = False)
-+
-+        cat_id = event.ret
-+
-+        self.RenderScattPlts()
-+
-+        cat_id = event.kwds["cat_id"]
-+        train_mgr, preview_mgr = self.iclass_conn.iclass_frame.GetMapManagers()
-+        
-+        if not cat_id in self.added_cats_rasts:
-+            cat_rast = self.core.GetCatRast(cat_id)
-+
-+            cat_name = self.cats_mgr.GetCategoryAttrs(cat_id)['name']
-+            self.UpdateCategoryRaster(cat_id, ['color'], render = False)
-+            train_mgr.AddLayer(cat_rast, alias = cat_name)
-+
-+            self.added_cats_rasts.append(cat_id)
-+        else: #TODO settings
-+            train_mgr.Render()
-+            
-+    def UpdateCategoryRaster(self, cat_id, attrs, render = True):
-+
-+        cat_rast = self.core.GetCatRast(cat_id)
-+        if not grass.find_file(cat_rast, element = 'cell', mapset = '.')['file']:
-+            return
-+        cats_attrs = self.cats_mgr.GetCategoryAttrs(cat_id)
-+        train_mgr, preview_mgr = self.iclass_conn.iclass_frame.GetMapManagers()
-+
-+        if "color" in attrs:
-+            ret, err_msg = RunCommand('r.colors',
-+                                      map=cat_rast,
-+                                      rules="-",
-+                                      stdin="1 %s" % cats_attrs["color"],
-+                                      getErrorMsg=True)
-+
-+            if ret != 0:
-+                GError("r.colors failed\n%s" % err_msg)
-+            if render:
-+                train_mgr.Render()
-+
-+        if "name" in attrs:
-+            cat_rast = self.core.GetCatRast(cat_id)
-+
-+            train_mgr.SetAlias(original=cat_rast, alias=cats_attrs['name'])
-+            cats_attrs["name"]
-+
-+
-+    def DigitDataChanged(self, vectMap, digit):
-+        
-+        if self.mapWin_conn:
-+            self.mapWin_conn.DigitDataChanged(vectMap, digit)
-+            return 1
-+        else:
-+            return 0
-+
-+    def GetCategoriesManager(self):
-+        return self.cats_mgr
-+
-+class CategoriesManager:
-+
-+    def __init__(self, scatt_mgr, core):
-+
-+        self.core = core
-+        self.scatt_mgr = scatt_mgr
-+
-+        self.cats = {}
-+        self.cats_ids = []
-+
-+        self.sel_cat_id = None
-+
-+        self.exportRaster = None
-+
-+        self.initialized = Signal('CategoriesManager.initialized')
-+        self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
-+        self.deletedCategory = Signal('CategoriesManager.deletedCategory')
-+        self.addedCategory = Signal('CategoriesManager.addedCategory')
-+
-+    def CleanUp(self):
-+        
-+        self.cats.clear()
-+        del self.cats_ids[:]
-+
-+        self.sel_cat_id = None  
-+    """
-+    def InitCoreCats(self):
-+        if self.scatt_mgr.data_set:
-+            for cat_id in self.cats_ids:
-+                self._addCategory(cat_id)
-+
-+        self.scatt_mgr.mapWin_conn.Update()
-+    """
-+    def ChangePosition(self, cat_id, new_pos):
-+        if new_pos >= len(self.cats_ids):
-+            return False
-+
-+        try:
-+            pos = self.cats_ids.index(cat_id)
-+        except:
-+            return False
-+
-+        if pos > new_pos:
-+            pos -= 1
-+
-+        self.cats_ids.remove(cat_id)
-+
-+        self.cats_ids.insert(new_pos, cat_id)
-+
-+        self.scatt_mgr.RenderScattPlts()
-+        return True
-+
-+    def _addCategory(self, cat_id):
-+        self.scatt_mgr.thread.Run(callable = self.core.AddCategory, 
-+                                  cat_id = cat_id)
-+
-+    def AddCategory(self, cat_id = None, name = None, color = None, nstd = None):
-+
-+        if cat_id is None:
-+            if self.cats_ids:
-+                cat_id = max(self.cats_ids) + 1
-+            else:
-+                cat_id = 1
-+
-+        if self.scatt_mgr.data_set:
-+            self.scatt_mgr.thread.Run(callable = self.core.AddCategory, 
-+                                      cat_id = cat_id)
-+            #TODO check number of cats
-+            #if ret < 0: #TODO
-+            #    return -1;
-+
-+        self.cats[cat_id] = {
-+                                'name' : _('Category %s' % cat_id ),
-+                                'color' : "0:0:0",
-+                                'opacity' : 1.0,
-+                                'show' : True,
-+                                'nstd' : 1.0,
-+                            }
-+
-+        self.cats_ids.insert(0, cat_id)
-+
-+        if name is not None:
-+            self.cats[cat_id]["name"] = name
-+   
-+        if color is not None:
-+            self.cats[cat_id]["color"] = color
-+
-+        if nstd is not None:
-+            self.cats[cat_id]["nstd"] = nstd
-+
-+        self.addedCategory.emit(cat_id = cat_id,
-+                                name = self.cats[cat_id]["name"], 
-+                                color = self.cats[cat_id]["color"] )
-+        return cat_id
-+
-+    def SetCategoryAttrs(self, cat_id, attrs_dict):
-+        render = False
-+        update_cat_rast = []
-+
-+        for k, v in attrs_dict.iteritems():
-+            if not render and k in ['color', 'opacity', 'show', 'nstd']:
-+                render = True
-+            if k in ['color', 'name']:
-+                update_cat_rast.append(k)
-+
-+            self.cats[cat_id][k] = v
-+
-+        if render:
-+            self.scatt_mgr.RenderScattPlts()
-+        
-+        if update_cat_rast:
-+            self.scatt_mgr.UpdateCategoryRaster(cat_id, update_cat_rast)
-+
-+        self.setCategoryAttrs.emit(cat_id = cat_id, attrs_dict = attrs_dict)
-+
-+    def DeleteCategory(self, cat_id):
-+
-+        if self.scatt_mgr.data_set:
-+            self.scatt_mgr.thread.Run(callable = self.core.DeleteCategory, 
-+                                      cat_id = cat_id)
-+        del self.cats[cat_id]
-+        self.cats_ids.remove(cat_id)
-+
-+        self.deletedCategory.emit(cat_id = cat_id)
-+
-+    #TODO emit event?
-+    def SetSelectedCat(self, cat_id):
-+        self.sel_cat_id = cat_id
-+        if self.scatt_mgr.pol_sel_mode[0]:
-+            self.scatt_mgr.RenderScattPlts()
-+
-+    def GetSelectedCat(self):
-+        return self.sel_cat_id
-+
-+    def GetCategoryAttrs(self, cat_id):
-+        #TODO is mutable
-+        return self.cats[cat_id]
-+
-+    def GetCategoriesAttrs(self):
-+        #TODO is mutable
-+        return self.cats
-+     
-+    def GetCategories(self):
-+        return self.cats_ids[:]
-+
-+    def SetCategoryPosition(self):
-+        if newindex > oldindex:
-+            newindex -= 1
-+        
-+        self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))
-+
-+    def ExportCatRast(self, cat_id):
-+
-+        cat_attrs = self.GetCategoryAttrs(cat_id)
-+
-+        dlg = ExportCategoryRaster(parent=self.scatt_mgr.guiparent, 
-+                                   rasterName=self.exportRaster, 
-+                                   title=_("Export scatter plot raster of class <%s>") % cat_attrs['name'])
-+        
-+        if dlg.ShowModal() == wx.ID_OK:
-+            self.exportCatRast = dlg.GetRasterName()
-+            dlg.Destroy()
-+
-+            self.scatt_mgr.tasks_pids['export_cat_rast'].append(self.scatt_mgr.thread.GetId())
-+            self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
-+                                      userdata={'name' : cat_attrs['name']},
-+                                      cat_id=cat_id,
-+                                      rast_name=self.exportCatRast)
-+
-+    def OnExportCatRastDone(self, event):
-+        ret, err = event.ret
-+        if ret == 0:
-+            cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
-+            GMessage(_("Scatter plot raster of class <%s> exported to raster map <%s>.") % 
-+                      (event.userdata['name'], event.kwds['rast_name']))
-+        else:
-+            GMessage(_("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") % 
-+                      (event.userdata['name'], event.kwds['rast_name'], err))
-+
-+
-+class MapWinConnection:
-+    def __init__(self, scatt_mgr, mapWin, scatt_rast_updater):
-+        self.mapWin = mapWin
-+        self.vectMap = None
-+        self.scatt_rast_updater = scatt_rast_updater
-+        self.scatt_mgr = scatt_mgr
-+        self.cats_mgr = scatt_mgr.cats_mgr
-+
-+        self.thread = self.scatt_mgr.thread
-+
-+        #TODO
-+        self.mapWin.parent.toolbars["vdigit"].editingStarted.connect(self.DigitDataChanged)
-+
-+        #def ChangeMap(self, vectMap, layers_cats):
-+        #     self.vectMap = vectMap
-+        #     self.layers_cats = layers_cats
-+
-+        #ret, region, msg = RunCommand("v.to.rast",
-+        #                              flags = "gp",
-+        #                              getErrorMsg = True,
-+        #                              read = True)
-+
-+    def Update(self):
-+        self.thread.Run(callable=self.scatt_rast_updater.SynchWithMap)
-+
-+    def _connectSignals(self):
-+        self.digit.featureAdded.connect(self.AddFeature)
-+        self.digit.areasDeleted.connect(self.DeleteAreas)
-+        self.digit.featuresDeleted.connect(self.DeleteAreas)
-+        self.digit.vertexMoved.connect(self.EditedFeature)
-+        self.digit.vertexRemoved.connect(self.EditedFeature)
-+        self.digit.lineEdited.connect(self.EditedFeature)
-+        self.digit.featuresMoved.connect(self.EditedFeature)
-+
-+    def AddFeature(self, new_bboxs, new_areas_cats):
-+        if not self.scatt_mgr.data_set:
-+            return
-+        self.scatt_mgr.computingStarted.emit()
-+
-+        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-+        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
-+                        new_bboxs = new_bboxs, 
-+                        old_bboxs = [], 
-+                        old_areas_cats = [],
-+                        new_areas_cats = new_areas_cats)
-+
-+    def DeleteAreas(self, old_bboxs, old_areas_cats):
-+        if not self.scatt_mgr.data_set:
-+            return
-+        self.scatt_mgr.computingStarted.emit()
-+
-+        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-+        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
-+                        new_bboxs = [], 
-+                        old_bboxs = old_bboxs, 
-+                        old_areas_cats = old_areas_cats,
-+                        new_areas_cats = [])
-+
-+
-+    def EditedFeature(self, new_bboxs, new_areas_cats, old_bboxs, old_areas_cats):
-+        if not self.scatt_mgr.data_set:
-+            return
-+        self.scatt_mgr.computingStarted.emit()
-+
-+        self.scatt_mgr.tasks_pids['mapwin_conn'].append(self.thread.GetId())
-+        self.thread.Run(callable = self.scatt_rast_updater.EditedFeature, 
-+                        new_bboxs = new_bboxs, 
-+                        old_bboxs = old_bboxs, 
-+                        old_areas_cats = old_areas_cats,
-+                        new_areas_cats = new_areas_cats)
-+
-+    def DigitDataChanged(self, vectMap, digit):
-+
-+        self.digit = digit
-+        self.vectMap = vectMap
-+
-+        self.digit.EmitSignals(emit = True)
-+
-+        self.scatt_rast_updater.SetVectMap(vectMap)
-+
-+        self._connectSignals()
-+
-+
-+class IClassConnection:
-+    def __init__(self, scatt_mgr, iclass_frame, cats_mgr):
-+        self.iclass_frame = iclass_frame
-+        self.stats_data = self.iclass_frame.stats_data
-+        self.cats_mgr = cats_mgr
-+        self.scatt_mgr= scatt_mgr
-+
-+        self.stats_data.statisticsAdded.connect(self.AddCategory)
-+        self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
-+        self.stats_data.allStatisticsDeleted.connect(self.DeletAllCategories)
-+        self.stats_data.statisticsSet.connect(self.SetCategory)
-+
-+        self.iclass_frame.groupSet.connect(self.GroupSet)
-+
-+        self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
-+        self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-+        self.cats_mgr.addedCategory.connect(self.AddStatistics)
-+
-+        self.iclass_frame.categoryChanged.connect(self.CategoryChanged)
-+
-+        self.SyncCats()
-+
-+    def SetData(self):
-+        self.iclass_frame.AddBands()
-+
-+    def EmptyCategories(self):
-+        self.iclass_frame.OnCategoryManager(None)
-+
-+    def SyncCats(self, cats_ids = None):
-+        self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
-+        cats = self.stats_data.GetCategories()
-+        for c in cats:
-+            if cats_ids and c not in cats_ids:
-+                continue
-+            stats = self.stats_data.GetStatistics(c)
-+            self.cats_mgr.AddCategory(c, stats.name, stats.color, stats.nstd)
-+        self.cats_mgr.addedCategory.connect(self.AddStatistics)
-+
-+    def CategoryChanged(self, cat):
-+        self.cats_mgr.SetSelectedCat(cat) 
-+
-+    def AddCategory(self, cat, name, color):
-+        self.cats_mgr.addedCategory.disconnect(self.AddStatistics)
-+        stats = self.stats_data.GetStatistics(cat)
-+        self.cats_mgr.AddCategory(cat_id = cat, name = name, color = color, nstd = stats.nstd)
-+        self.cats_mgr.addedCategory.connect(self.AddStatistics)
-+
-+    def DeleteCategory(self, cat):
-+        self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
-+        self.cats_mgr.DeleteCategory(cat)
-+        self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-+
-+    def DeletAllCategories(self):
-+
-+        self.cats_mgr.deletedCategory.disconnect(self.DeleteStatistics)
-+        cats = self.stats_data.GetCategories()
-+        for c in cats:
-+            self.cats_mgr.DeleteCategory(c)
-+        self.cats_mgr.deletedCategory.connect(self.DeleteStatistics)
-+
-+    def SetCategory(self, cat, stats):
-+
-+        self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics)
-+        cats_attr = {}
-+
-+        for attr in ['name', 'color', 'nstd']:
-+            if stats.has_key(attr):
-+                cats_attr[attr] = stats[attr]
-+
-+        if cats_attr:
-+            self.cats_mgr.SetCategoryAttrs(cat, cats_attr)
-+        self.cats_mgr.setCategoryAttrs.connect(self.SetStatistics)
-+
-+
-+    def SetStatistics(self, cat_id, attrs_dict):
-+        self.stats_data.statisticsSet.disconnect(self.SetCategory)
-+        self.stats_data.GetStatistics(cat_id).SetStatistics(attrs_dict)
-+        self.stats_data.statisticsSet.connect(self.SetCategory)
-+
-+    def AddStatistics(self, cat_id, name, color):
-+        self.stats_data.statisticsAdded.disconnect(self.AddCategory)
-+        self.stats_data.AddStatistics(cat_id, name, color)
-+        self.stats_data.statisticsAdded.connect(self.AddCategory)
-+
-+    def DeleteStatistics(self, cat_id):
-+        self.stats_data.statisticsDeleted.disconnect(self.DeleteCategory)
-+        self.stats_data.DeleteStatistics(cat_id)
-+        self.stats_data.statisticsDeleted.connect(self.DeleteCategory)
-+
-+    def GroupSet(self, group):
-+        res = RunCommand('i.group',
-+                         flags = 'g',
-+                         group = group, subgroup = group,
-+                         read = True).strip()
-+        if res.split('\n')[0]:
-+            bands = res.split('\n')
-+            self.scatt_mgr.SetData(bands)
-\ No newline at end of file
 Index: gui/wxpython/vnet/dialogs.py
 ===================================================================
 --- gui/wxpython/vnet/dialogs.py	(revision 57633)
@@ -5443,1247 +6706,3 @@
          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 57633)
-+++ 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
-+
-+   \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,799 @@
-+/*!
-+   \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 <grass/raster.h>
-+#include <grass/imagery.h>
-+#include <grass/gis.h>
-+#include <grass/glocale.h>
-+
-+#include <stdio.h>
-+#include <stdlib.h>
-+#include <math.h>
-+#include <string.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.
-+  
-+   \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.
-+
-+   \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;    
-+}
-+
-+
-+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[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;
-+}
-Index: lib/vector/Vlib/open.c
-===================================================================
---- lib/vector/Vlib/open.c	(revision 57633)
-+++ lib/vector/Vlib/open.c	(working copy)
-@@ -240,7 +240,9 @@
-         }
-         else {
-             char file_path[GPATH_MAX];
--            
-+            /* reduce to current mapset if search path was set */
-+            if(strcmp(Map->mapset, "") == 0)
-+                Map->mapset = G_store(G_mapset());
-             /* temporary map: reduce to current mapset if search path
-              * was set */
-             if (strcmp(Map->mapset, "") == 0)



More information about the grass-commit mailing list