/*
 * (C) Copyright 1996-2016 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*! \file IsoPlot.cc
 \brief Implementation of the Template class IsoPlot.

 Magics Team - ECMWF 2004

 Started: Wed 3-Mar-2004

 Changes:
 */


#include "IsoPlot.h"
#include "Factory.h"
#include "MatrixHandler.h"
#include "Timer.h"
#include "UserPoint.h"
#include "IsoHighlight.h"
#include "Colour.h"
#include "AutoLock.h"
#include "ThreadControler.h"
#include "LegendVisitor.h"
#include "Histogram.h"



namespace magics {



static MutexCond producerMutex_;

 IsoPlot::IsoPlot() {
    setTag("isoline");
}

 IsoPlot::~IsoPlot() {
}

/*!
 Class information are given to the output-stream.
 */
 void IsoPlot::print(ostream& out) const {
    out << "IsoPlot[";
    IsoPlotAttributes::print(out);
    out << "]";
}


/*! \brief Helper class for Countouring
 *
 */

class CellBox : public VectorOfPointers<vector<CellBox* > >
{
public:
    CellBox(const CellArray* parent, int row1, int row2, int column1, int column2) :
             parent_(parent), row1_(row1), row2_(row2), column1_(column1), column2_(column2) {



    }

    CellBox(const CellArray* parent) :
            parent_(parent), row1_(0), row2_(parent->rows_-1), column1_(0), column2_(parent->columns_-1) {

    }

    CellBox() :
            parent_(0), row1_(0), row2_(0), column1_(0), column2_(0) {

    }

     ~CellBox() {


    }

     double value () {
        return (*parent_)(row1_, column1_)->value(0);
    }

     RangeType range()
    {
        // First try to finfd iif the cell is outOfRange...
        bool out = true;
        for (int row = row1_; row <= row2_; row++) {
            for (int column = column1_; column <= column2_; column++) {
                if ((*parent_)(row, column)->range() != outOfRange)
                {
                    out = false;
                    break;
                }
            }
        }
        if ( out )
            return outOfRange;

        bool oneout = false;
        int min = INT_MAX;
        int max = INT_MIN;
        for (int row = row1_; row <= row2_; row++)
        {
           for (int column = column1_; column <= column2_; column++)
           {
             Cell* cell = (*parent_)(row, column);
             RangeType range   = cell->range();
             if ( range == outOfRange) {
                 oneout = true;
                 continue;
             }

             if (range == multipleRange)
                return multipleRange;

             min = ( min < cell->min_ ) ? min : cell->min_;
             max = ( max > cell->max_ ) ? max : cell->max_;
             if (max-min > 0 )
                return multipleRange;
           }
        }
        if ( max-min == 0 ) {
            if ( oneout )
                return multipleRange;
            return singleRange;
        }
        return outOfRange;

        /// @warning What should be retuned if function arrives here?
    }

    void push_back(int index, double x1, double y1, double x2, double y2 ){
        if ( index == -1 )
            return;


        map<int, SegmentJoiner*>::iterator helper = helper_.find(index);
        if ( helper == helper_.end() ) {
            helper_.insert(make_pair(index, new SegmentJoiner()));
            helper = helper_.find(index);
        }
        helper->second->push_back(x1, y1, x2, y2);
    }

    void push_back(int index, vector<PaperPoint>& points) {
        if ( index == -1 || points.empty() )
            return;

        map<int, SegmentJoiner*>::iterator helper = helper_.find(index);
        if ( helper == helper_.end()  ) {
            helper_.insert(make_pair(index, new SegmentJoiner()));
            helper = helper_.find(index);
        }
        if ( helper->second-> size() == 0 ) {
            int size = points.size();
            for ( int i = 0; i < size; i++ ) {
                int j = (i+1)%size;
                helper->second->push_back(points[i].x_, points[i].y_, points[j].x_, points[j].y_);
            }



        }
        else {
            // intersect !
            // Create line from 1first

            typedef boost::geometry::model::polygon<PaperPoint > polygon;


            polygon previous, pts;

            for ( vector<PaperPoint>::iterator pt = points.begin();  pt != points.end(); ++pt ) {
                pts.outer().push_back(*pt);
            }
            pts.outer().push_back(points.front());
            vector<vector<Point> > result;
            helper->second->computePolygonLines(result);
            ASSERT( result.size() == 1);
            for ( vector<Point>::iterator pt = result.front().begin();  pt != result.front().end(); ++pt ) {
                previous.outer().push_back(PaperPoint(pt->x_, pt->y_));
            }

            helper_[index] = new SegmentJoiner();


            std::vector<polygon > output;


            try {
                boost::geometry::intersection(previous, pts, output);


                if (output.size() == 1){
                    vector<PaperPoint> xx;
                    for ( vector<PaperPoint>::iterator pt = output.front().outer().begin();  pt != output.front().outer().end(); ++pt ) {
                        xx.push_back(*pt);
                    }

                    push_back(index, xx);

                }
                else
                {

                    boost::geometry::union_(pts, previous, output);
                    if (output.size() == 1){
                        push_back(index, output.front().outer());
                    }
                    else
                        push_back(index, previous.outer());

                }
            }
            catch (...) {
                // ignore
            }

        }



    }

    void push_back(int index, const SegmentJoiner& segment) {
        if ( index == -1 )
            return;

        map<int, SegmentJoiner*>::iterator helper = helper_.find(index);
        if ( helper == helper_.end() ) {
            helper_.insert(make_pair(index, new SegmentJoiner()));
            helper = helper_.find(index);
        }
        helper->second->add(segment);
    }

    void push_back(CellBox* box) {
        VectorOfPointers<vector<CellBox* > >::push_back(box);
    }

    void split();
    void split(int);

    void addShape(int index)
    {
        if ( index == -1 )
            return;
        Cell* cell;
        vector<PaperPoint> points;
      

        // bottom
        for (int column = column1_; column <= column2_; column++) {
            cell = (*parent_)(row1_, column);
            points.push_back(PaperPoint(cell->column(0), cell->row(0)));

        }

        // right
        for (int row = row1_; row <= row2_; row++) {
            cell = (*parent_)(row, column2_);
            points.push_back(PaperPoint(cell->column(1), cell->row(1)));

        }
        // top
        for (int column = column2_; column >= column1_; column--) {
            cell = (*parent_)(row2_, column);
            points.push_back(PaperPoint(cell->column(2), cell->row(2)));

        }
        //left
        for (int row = row2_; row >= row1_; row--) {
            cell = (*parent_)(row, column1_);

            points.push_back(PaperPoint(cell->column(3), cell->row(3)));
        }


        push_back(index, points);
    }

    void reshape(CellBox* parent)
    {
        if ( parent == this) return;
        for (map<int, SegmentJoiner*>::iterator entry = helper_.begin(); entry != helper_.end(); ++entry) {
            parent->push_back(entry->first, *entry->second);
            delete (entry->second);
        }

        helper_.clear();
    }


    void shade(const IsoPlot& owner);


    void shade(const IsoPlot& owner, CellBox* parent) {
        RangeType def = range();
        switch (def)
        {
          case outOfRange :
                break;

          case singleRange:

                    addShape(owner.shadingIndex(value()));

                break;

          default:
                split();
                if (empty()) {
                    ASSERT( row1_ == row2_);
                    ASSERT( column1_ == column2_);
                    owner.isoline(*(*parent_)(row1_, column1_), this);

                }
                else {
                    for (CellBox::iterator cell = begin(); cell != end(); ++cell) {
                        (*cell)->shade(owner, parent);
                        (*cell)->reshape(parent);
                }

                }
        }
    }



    void contour(const IsoPlot& owner)
    {
           for (int row = row1_; row <= row2_; row++) {
                        for (int column = column1_; column <= column2_; column++) {
                                    owner.isoline(*(*parent_)(row, column));
                        }
            }
    }

    void feed(IsoPlot& owner, BasicGraphicsObjectContainer& out)
    {
        for (vector<Polyline*>::iterator poly = polylines_.begin(); poly != polylines_.end(); ++poly) {
            (*owner.shading_)(*poly);
            out.push_back(*poly);
        }
        polylines_.clear();
    }

    void finish()
    {

        Timer timer("Feed", "Feed");
        for ( map<int, SegmentJoiner*>::iterator index = helper_.begin(); index!= helper_.end(); ++index) {
            vector<vector<Point> > result;
            vector<vector<Point> > polys;
            list<vector<Point> > holes;
            SegmentJoiner& joiner = *index->second;


            joiner.computePolygonLines(result);
            Polyline* poly = 0;
            if ( result.empty() )
                    continue;
            bool reverse = joiner.isHole(result.front());

            for (vector<vector<Point> >::iterator j = result.begin() ; j != result.end(); ++j) {

                if ( reverse ) {
                if ( !joiner.isHole((*j)) ) {
                    holes.push_back(vector<Point>());
                    std::swap((*j),holes.back());

                }
                else {
                    polys.push_back(vector<Point>());
                    std::swap((*j),polys.back());

                }
                }
                else {
                if ( joiner.isHole((*j)) ) {
                    holes.push_back(vector<Point>());
                    std::swap((*j),holes.back());

                }
                else {
                    polys.push_back(vector<Point>());
                    std::swap((*j),polys.back());

                }
                }
            }

            for(vector<vector<Point> >::iterator j = polys.begin() ; j != polys.end(); ++j) {
                poly = new Polyline();
                polylines_.push_back(poly);

                poly->reserve(j->size());
                poly->index(index->first);


                for (vector<Point>::iterator point = j->begin(); point != j->end(); ++point)
                    poly->push_back(PaperPoint(point->x_, point->y_));


                for(std::list<vector<Point> >::iterator h = holes.begin() ; h != holes.end(); ) {
                    list<vector<Point> >::iterator next = h; next++;

                    if ( joiner.pointInPoly((*h).front(), *j) ) {

                        poly->newHole();
                        for (vector<Point>::iterator point = h->begin(); point != h->end(); ++point)
                            poly->push_back_hole(PaperPoint(point->x_, point->y_));
                        holes.erase(h);
                    }
                    h = next;
                }
            }
            delete index->second;
        }

    }


    const CellArray* parent_;
    int row1_;
    int row2_;
    int column1_;
    int column2_;

    map<int, SegmentJoiner*> helper_;
    vector<Polyline*> polylines_;
};


}  // end magics namespace


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void CellBox::shade(const IsoPlot& owner) {
        shade(owner, this);
}


void CellBox::split(int many)
{
    if (many == 1) {
        push_back(new CellBox(parent_, row1_ , row2_, column1_, column2_));
        return;
    }
    
    if (many == 4) {
        split();
        return;
    }

    // split in 9


    if ( row1_ == row2_ && column1_ ==  column2_ )
            return;

    const int row    = (row2_   - row1_) /3;
    const int column = (column2_ - column1_)/3;

    int row1 = row1_ + row;
    int row2 = row1 + row;
    int col1 = column1_ + column;
    int col2 = col1 + column;

    // Push 4 cells:
    push_back(new CellBox(parent_, row1_, row1, column1_, col1));
    push_back(new CellBox(parent_, row1+1, row2, column1_, col1));
    push_back(new CellBox(parent_, row2+1, row2_, column1_, col1)); 

    push_back(new CellBox(parent_, row1_, row1, col1+1, col2));
    push_back(new CellBox(parent_, row1+1, row2, col1+1, col2));
    push_back(new CellBox(parent_, row2+1, row2_, col1+1, col2));

    push_back(new CellBox(parent_, row1_, row1, col1+1, column2_));
    push_back(new CellBox(parent_, row1+1, row2, col1+1, column2_));
    push_back(new CellBox(parent_, row2+1, row2_, col1+1, column2_));

}

void CellBox::split()
{
    if ( row1_ == row2_ && column1_ ==  column2_ )
            return;

    const int row    = (row2_   + row1_) /2;
    const int column = (column2_+ column1_)/2;

    if (row2_- row1_ > 1&& column2_- column1_ > 1)
    {
       //try first 2 split in columns ...
       CellBox* cell = new CellBox(parent_, row1_, row2_, column1_, column);
       RangeType def = cell->range();

       if ( def != multipleRange ) {  
             push_back(cell);
       }
       else {
             delete cell;
             push_back(new CellBox(parent_, row1_, row, column1_, column));
             push_back(new CellBox(parent_, row+1, row2_, column1_, column));
       }
       cell = new CellBox(parent_, row1_, row2_, column+1, column2_);
       def = cell->range();

       if ( def != multipleRange ) {
             push_back(cell);
       }
       else {
             delete cell;
             push_back(new CellBox(parent_, row1_, row, column+1, column2_));
             push_back(new CellBox(parent_, row+1, row2_, column+1, column2_));
       }
       return;
    }

    if (row2_- row1_ > 0) {
       push_back(new CellBox(parent_, row1_, row, column1_, column2_));
       push_back(new CellBox(parent_, row+1, row2_, column1_, column2_));
       return;
    }

    if (column2_- column1_ > 0) {
       push_back(new CellBox(parent_, row1_, row2_, column1_, column));
       push_back(new CellBox(parent_, row1_, row2_, column+1, column2_));
       return;
    }
}





struct IsoProducerData {
public:
        IsoProducerData(bool shading, IsoPlot& parent, CellBox& cell):
              shading_(shading), parent_(parent), cell_(cell) {
           more_ = true;
        }
        bool shading_;
        IsoPlot& parent_;
        CellBox& cell_;
        bool more_;
        MutexCond cond_;
};


class IsoProducer: public Thread {

public:
    IsoProducer(int n, IsoProducerData& data) : n_(n), objects_(data) {}
    void run()
    {
        {
            Timer timer("cell", "shading");
            ( objects_.shading_ ) ? objects_.cell_.shade(objects_.parent_) : objects_.cell_.contour(objects_.parent_);

        }
        objects_.cell_.finish();
    }
     ~IsoProducer() {}

protected:
     //! Method to print string about this class on to a stream of type ostream (virtual).
     void print(ostream&) const {}
    int n_;
    IsoProducerData&  objects_;

private:
    //! Copy constructor - No copy allowedf
    IsoProducer(const IsoProducer&);
    //! Overloaded << operator to copy - No copy allowed
    IsoProducer& operator=(const IsoProducer&);

    // -- Friends
    //! Overloaded << operator to call print().
    friend ostream& operator<<(ostream& s,const IsoProducer& p)
        { p.print(s); return s; }
};




void IsoPlot::isoline(Cell& cell, CellBox* parent) const
{
    static int cases[3][3][3] = { { { 0, 1, 2 },   { 3, 4, 5 },    { 6, 7, 8 } },
            { { 9, 10, 11 }, { 12, 13, 14 }, { 15, 16, 17 } },
            { { 18, 19, 20 },{ 21, 22, 23 }, { 24, 25, 0 } } };
    int p1, p2, p3;
    int current;
    double x1=0, x2=0, y1=0, y2=0;

    RangeType def = cell.range();

    if ( def == outOfRange )
        return;
    if ( def == singleRange )
    {
        if ( !parent  ) // NO Shading == Nothing to do!
            return;
        else {

            int index  = shading_->shadingIndex(cell.value(0));

                  vector<PaperPoint> points;
            for (int i = 0; i < 4; i++) {
                 points.push_back(PaperPoint(cell.column(i), cell.row(i)));


            parent->push_back(index, points);

                return;
            }
        }
    }

    for (int p=0; p<2; p++)
    {
        p1 = p;
        p2 = p1+1;
        p3 = 3;


        if (cell.missing(p1) || cell.missing(p2) || cell.missing(p3)) {
            continue;
        }




        // First, build the list of isolines in this triangle...
        vector<int> levels;

        for (int l = cell.min_; l != cell.max_; ++l) {
            const double contour = levels_[l];
            const int    out     = cases[cell.coef(p1, contour)][cell.coef(p2, contour)][cell.coef(p3, contour)];
            if ( out != 0 ) {
                levels.push_back(l);
            }
        }


        if ( levels.empty() && parent ) {
            int index = shading_->shadingIndex(cell.value(p1));





            parent->push_back(index, cell.column(p1), cell.row(p1), cell.column(p2), cell.row(p2));
            parent->push_back(index, cell.column(p2), cell.row(p2), cell.column(p3), cell.row(p3));
            parent->push_back(index, cell.column(p3), cell.row(p3), cell.column(p1), cell.row(p1));

        }
        else {
            CellBox* box = (parent) ? new CellBox() : 0;

            for (vector<int>::const_iterator l = levels.begin(); l != levels.end(); ++l)
            {
                int level = *l;
                // First make a quich check to see if there is at l

                const double contour=levels_[level];


                current = cases[cell.coef(p1, contour)][cell.coef(p2, contour)][cell.coef(p3, contour)];

                int add = 2;
                int leftindex  = shading_->leftIndex(contour);
                int rightindex = shading_->rightIndex(contour);
                vector<PaperPoint> left, right;



                switch (current)
                        {
                        //-------------------------------------------------------------------------------------------------------
                        //     Case 0 -point out!
                        //-------------------------------------------------------------------------------------------------------
                        case 0:
                            add = 0;
                            break;
                            if ( !box ) break;

                            if (cell.height(p1, contour) < 0) {
                                left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }

                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 1 -Single point 3
                        //-------------------------------------------------------------------------------------------------------
                        case 1:
                            x1=cell.column(p3);
                            y1=cell.row(p3);
                            add = 1;
                            if ( !box) break;
                            if (cell.height(p1, contour) < 0) {
                                left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 2 - Line between sides 2-3 and 3-1
                        //-------------------------------------------------------------------------------------------------------
                        case 2:
                            cell.xysect(p2,p3, contour, x1, y1);
                            cell.xysect(p3,p1, contour, x2, y2);

                            if ( !box ) break;
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 3 -Single point 2
                        //-------------------------------------------------------------------------------------------------------
                        case 3:
                            x1=cell.column(p2);
                            y1=cell.row(p2);
                            add = 1;
                            if ( !box) break;

                            if (cell.height(p1, contour) < 0) {
                                  left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                  left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                  left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                  right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                  right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                  right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 4 - Line between vertex 2 and vertex 3
                        //-------------------------------------------------------------------------------------------------------
                        case 4:

                            x1=cell.column(p2);
                            y1=cell.row(p2);
                            x2=cell.column(p3);
                            y2=cell.row(p3);
                            if ( !box ) break;

                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 5 - Line between vertex 2 and side 3-1
                        //-------------------------------------------------------------------------------------------------------
                        case 5:
                            x1=cell.column(p2);
                            y1=cell.row(p2);

                            cell.xysect(p3,p1, contour,x2, y2);
                            if ( !box ) break;
                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 6 - Line between side 1-2  and side 2.3
                        //-------------------------------------------------------------------------------------------------------
                        case 6:

                            cell.xysect(p1,p2, contour, x1, y1);
                            cell.xysect(p2,p3, contour, x2, y2);

                            if ( !box ) break;
                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(x2, y2));

                            break;
                        //-------------------------------------------------------------------------------------------------------
                        //     Case 7 - Line between sides 1-2 aanve vertex 3
                        //-------------------------------------------------------------------------------------------------------
                        case 7:
                            cell.xysect(p1,p2, contour, x1, y1);
                            x2=cell.column(p3);
                            y2=cell.row(p3);

                            if ( !box ) break;
                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 8 - Line between sides 1-2 and 3-1
                        //-------------------------------------------------------------------------------------------------------
                        case 8:
                            cell.xysect(p1,p2, contour, x1, y1);
                            cell.xysect(p3,p1, contour, x2, y2);

                            if ( !box ) break;
                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 9 -single point 1
                        //-------------------------------------------------------------------------------------------------------
                        case 9:
                            x1=cell.column(p1);
                            y1=cell.row(p1);
                            add = 1;
                            if ( !box) break;
                            if (cell.height(p2, contour) < 0) {
                                   left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                   left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                   left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                   right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                   right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                   right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 10 - Line between vertex 3 and vertex 1
                        //-------------------------------------------------------------------------------------------------------
                        case 10:

                            x1=cell.column(p3);
                            y1=cell.row(p3);
                            x2=cell.column(p1);
                            y2=cell.row(p1);
                            if ( !box) break;

                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 11 - Line between side 3-2 and vertex 1
                        //-------------------------------------------------------------------------------------------------------
                        case 11:
                            cell.xysect(p3,p2, contour, x1, y1);
                            x2=cell.column(p1);
                            y2=cell.row(p1);
                            if ( !box ) break;
                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            left.push_back(PaperPoint(x1, y1));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 12 - Line between vertex 1 and vertex 2
                        //-------------------------------------------------------------------------------------------------------
                        case 12:
                            x1=cell.column(p1);
                            y1=cell.row(p1);
                            x2=cell.column(p2);
                            y2=cell.row(p2);
                            if ( !box ) break;

                            left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 13 - Flat Area all vertex have the isoline value!
                        //-------------------------------------------------------------------------------------------------------
                        case 13:
                            add = 0;
                            if ( !box ) break;
                            if (cell.height(p1, contour) < 0) {
                                   left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                   left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                   left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                   right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                   right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                   right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 14 - Line between vertex 2 and vertex 1
                        //-------------------------------------------------------------------------------------------------------
                        case 14:
                            x1=cell.column(p2);
                            y1=cell.row(p2);
                            x2=cell.column(p1);
                            y2=cell.row(p1);
                            if ( !box ) break;

                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 15 - Line between vertex 1 and side3-2
                        //-------------------------------------------------------------------------------------------------------
                        case 15:
                            x1=cell.column(p1);
                            y1=cell.row(p1);
                            cell.xysect(p3,p2, contour, x2, y2);
                            if ( !box ) break;

                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 16 - Line between vertex 1 and vertex 3
                        //-------------------------------------------------------------------------------------------------------
                        case 16:
                            x1=cell.column(p1);
                            y1=cell.row(p1);
                            x2=cell.column(p3);
                            y2=cell.row(p3);
                            if ( !box ) break;
                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(x2, y2));

                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 17 - single point 1
                        //-------------------------------------------------------------------------------------------------------
                        case 17:
                            x1=cell.column(p1);
                            y1=cell.row(p1);
                            add = 1;
                            if ( !box ) break;
                            if (cell.height(p2, contour) < 0) {
                                      left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                      left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                      left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                      right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                      right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                      right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 18 - Line between side3-1 and side1-2
                        //-------------------------------------------------------------------------------------------------------
                        case 18:
                            cell.xysect(p3,p1, contour, x1, y1);
                            cell.xysect(p1,p2, contour, x2, y2);

                            if ( !box ) break;
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(x2, y2));

                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 19 - Line between vertex 3 and side1-2
                        //-------------------------------------------------------------------------------------------------------
                        case 19:
                            x1=cell.column(p3);
                            y1=cell.row(p3);

                            cell.xysect(p1,p2, contour, x2, y2);

                            if ( !box ) break;
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 20 - Line between side3-2 and side1-2
                        //-------------------------------------------------------------------------------------------------------
                        case 20:
                            cell.xysect(p3,p2, contour, x1, y1);
                            cell.xysect(p1,p2, contour, x2, y2);

                            if ( !box ) break;
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 21 - Line between side 3-1 and vertex 2
                        //-------------------------------------------------------------------------------------------------------
                        case 21:
                            cell.xysect(p3,p1, contour, x1, y1);

                            x2=cell.column(p2);
                            y2=cell.row(p2);
                            if ( !box ) break;

                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 22 - Line between vertex 3 and vertex 2
                        //-------------------------------------------------------------------------------------------------------
                        case 22:
                            x1=cell.column(p3);
                            y1=cell.row(p3);
                            x2=cell.column(p2);
                            y2=cell.row(p2);
                            if ( !box) break;
                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 23 - single point 2
                        //-------------------------------------------------------------------------------------------------------
                        case 23:
                            x1=cell.column(p2);
                            y1=cell.row(p2);
                            add = 1;
                            if ( !box) break;
                            if (cell.height(p1, contour) < 0) {
                                     left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                     left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                     left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                     right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                     right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                     right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 24 - Line between side1-3  and side3-2
                        //-------------------------------------------------------------------------------------------------------
                        case 24:
                            cell.xysect(p1,p3, contour, x1, y1);
                            cell.xysect(p3,p2, contour, x2, y2);
                            if ( !box) break;
                            left.push_back(PaperPoint(x1, y1));
                            left.push_back(PaperPoint(x2, y2));
                            left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));

                            right.push_back(PaperPoint(x1, y1));
                            right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                            right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                            right.push_back(PaperPoint(x2, y2));
                            break;

                        //-------------------------------------------------------------------------------------------------------
                        //     Case 25 - single point C
                        //-------------------------------------------------------------------------------------------------------
                        case 25:
                            x1=cell.column(p3);
                            y1=cell.row(p3);
                            add = 1;
                            if ( !box) break;
                            if (cell.height(p1, contour) < 0) {
                                     left.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                     left.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                     left.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            else {
                                     right.push_back(PaperPoint(cell.column(p1), cell.row(p1)));
                                     right.push_back(PaperPoint(cell.column(p2), cell.row(p2)));
                                     right.push_back(PaperPoint(cell.column(p3), cell.row(p3)));
                            }
                            break;

                        default:
                            break;
                        }

                if (add == 2 && needIsolines() )
                {
                    // here we have 2 Points to add!
                    // We send it to a thread!
                    const int t = (*l) % threads_;
                    {
                        AutoLock<MutexCond> lockproducer(producerMutex_);
                        {
                            AutoLock<MutexCond> lock(segments_[t]->cond_);
                            segments_[t]->segments_.push_back(make_pair(levels_[*l],
                                    std::make_pair(make_pair(x1, y1), std::make_pair(x2, y2))));
                            if ( segments_[t]->segments_.size() >= 2000 )
                                segments_[t]->cond_.signal();
                        }
                        producerMutex_.signal();
                    }
                }

                if (box) {

                        box->push_back(leftindex, left);
                        box->push_back(rightindex, right);
                }



            } // end of levels...


            box->reshape(parent);
            delete box;



        }// step to next triangle


    }

}


inline bool getEnv(const string& name, bool def)
{
    string value = getEnvVariable(name);
    value = lowerCase(value);
    if (value == "no" || value == "off" || value == "false") return false;
    if (value == "yes"|| value == "on"  || value == "true")  return true;
    return def;
}


void IsoPlot::isoline(MatrixHandler& data, BasicGraphicsObjectContainer& parent)
{

    const Transformation& transformation = parent.transformation();





    levels_.clear();
    if ( levelSelection_->empty() )
        return;


    // Find the used levels!
    const vector<double>::const_iterator end = (*levelSelection_).end();
        vector<double>::const_iterator last = (*levelSelection_).end();

    double min = data.min();
    double max = data.max();


    if ( (*levelSelection_).front()  > min )
            levels_.push_back(min);

    vector<double>::const_iterator level = levelSelection_->begin();

    while (*level < min && level != end ) {
        last = level;
        ++level;
    }
    if ( last != end)
        levels_.push_back(*last);
    while (*level < max && level != end ) {
        levels_.push_back(*level);
        ++level;
    }
    if ( level != end ) {
        levels_.push_back(*level);
    }
    else
        levels_.push_back(max);




    missing_ = data.missing();



    IntervalMap<int> range;
    int r= 1;
    for (vector<double>::const_iterator level = levels_.begin(); level != levels_.end(); ++level) {
       MagLog::debug()  << " level " << *level << endl;
       if (level+1!= levels_.end() )
          range.insert(make_pair(Interval(*level, *(level+1)), r++));
       }
       
       CellArray* array = shading_->array(data, range, transformation, parent.widthResolution(), parent.heightResolution(),
            resolution_, technique_);
       if (!array)
            return;
       if ( shading_->shadingMode() )
           range.insert(make_pair(Interval(levels_.back(), levels_.back()+EPSILON), r-1));
       
       CellBox view(array);


       threads_ = (needIsolines())  ? 4: 0;
       
       if ( threads_ ) {
            if ( user_thread_ == 1 ) 
                threads_ = 1;
            else if ( user_thread_ == 9 ) 
                threads_ = 9;
            else 
                threads_ = 4;
       }

      
       vector<IsoHelper*> consumers_;
       vector<IsoProducer* >  producers_;

       {
        Timer timer("Threading", "Threading");
        VectorOfPointers<vector<ThreadControler *>  > consumers;
        VectorOfPointers<vector<ThreadControler *>  > producers;
        segments_.clear();
        colourShapes_.clear();
        lines_.clear();

        for (int c = 0; c < threads_; c++) {
            vector<Polyline* >* lines = new vector<Polyline*>();
            lines_.push_back(lines);
            segments_.push_back(new IsoData());
            consumers_.push_back(new IsoHelper(c, *lines,*(segments_.back())));
            consumers.push_back(new ThreadControler(consumers_.back(), false));
            consumers.back()->start();
        }

        view.split(threads_);
       
        
        
        
        int c = 0;
        VectorOfPointers<vector<IsoProducerData*> > datas;
        for ( int i = 0; i < view.size(); i++)
        //int i = 1;
        {

           IsoProducerData* data = new IsoProducerData(shading_->shadingMode(), *this, *(view[i]));
           datas.push_back(data);
           producers_.push_back(new IsoProducer(c, *data));
           producers.push_back(new ThreadControler(producers_.back(), false));
           producers.back()->start();
           c++;

        }

        for (vector<ThreadControler *>::iterator producer = producers.begin();
           producer != producers.end(); ++producer) {
           (*producer)->wait();
        }

        // No more
        {
           for (int i = 0; i < threads_; i++) {
                 AutoLock<MutexCond> lock(segments_[i]->cond_);
                 segments_[i]->more_ = false;
                 segments_[i]->cond_.signal();
           }
        }

        for (vector<ThreadControler *>::iterator consumer = consumers.begin(); consumer != consumers.end(); ++consumer) {
             (*consumer)->wait();
        }
       }

      
       
       for (CellBox::iterator cell = view.begin(); cell != view.end(); ++cell) {
                (*cell)->feed(*this,parent);
            }
       

       delete array;
       for ( vector<IsoData*>::iterator segment = segments_.begin(); segment != segments_.end(); ++segment)  {
           delete *segment;
           *segment = 0;
       }
       segments_.clear();

}


 bool IsoPlot::prepare(MatrixHandler& data)
{
    //double replaceMissing_ = 0;
    //double min = std::min(replaceMissing_, data.min());
    //double max = std::max(replaceMissing_, data.max());
    double min = data.min();
    double max = data.max();
    (*levelSelection_).clear();
    (*levelSelection_).calculate(min , max , true);
    bool need_isolines = (*shading_)(*levelSelection_);
    shading_->reset();
    (*label_).prepare(*levelSelection_,  (*colour_).name());
    return need_isolines;
}


/*!
 * Get the triangles list ...
 * Create the isolines...
 */
 void IsoPlot::operator()(MatrixHandler& data, BasicGraphicsObjectContainer& parent)
{

    prepare(data);
    if ( legend_only_ ) {
        if ( rainbow_ ) {
            rainbowMethod_->set(*this);
            rainbowMethod_->prepare(*levelSelection_, *levelSelection_, true);
            setThicknessAndStyle();
        }
        return;
    }

    {
        Timer timer("contouring", "Time spent in contouring");
        isoline(data, parent);
    }

#ifdef ISOPLOT_DEBUG
    vector<Colour> colours;
    colours.push_back(Colour("red"));
    colours.push_back(Colour("green"));
    colours.push_back(Colour("blue"));
    colours.push_back(Colour("orange"));
    vector<Colour>::iterator colour = colours.begin();
#endif

    (*shading_)(this, data, parent);
    (*highlight_).prepare(*levelSelection_);

    if ( rainbow_ ) {
        rainbowMethod_->set(*this);
        rainbowMethod_->prepare(*levelSelection_, *levelSelection_, true);
        setThicknessAndStyle();

    }
    // Now we feed the task...
    for (vector<vector<Polyline* >* >::const_iterator lines = lines_.begin(); lines != lines_.end(); ++lines)
    {
      for (vector<Polyline* >::const_iterator poly = (*lines)->begin(); poly != (*lines)->end(); ++poly)
      {
        if ( (*poly)->empty() ) continue;



        if ( !rainbow_ ) {
            (*poly)->setColour(*colour_);
            (*poly)->setLineStyle(style_);
            (*poly)->setThickness(thickness_);
            (*highlight_)(*(*poly));
        }
        else {
            double level = (*poly)->front().value();
            (*poly)->setColour((*rainbowMethod_)(level));
            (*poly)->setLineStyle(line_style(level));
            (*poly)->setThickness(thickness(level));
        }
#ifdef ISOPLOT_DEBUG
        (*poly)->setColour(*colour);
        colour++;
        if ( colour == colours.end())
            colour = colours.begin();
#endif

        if ( rainbow_ ) {
            (*poly)->setColour((*rainbowMethod_)((*poly)->front().value()));
        }
        (*label_)(**poly, (*poly)->front().value());



        parent.push_back(*poly);
      }
      delete *lines;

    }
    lines_.clear();
}



 void NoIsoPlot::operator()(MatrixHandler& data, BasicGraphicsObjectContainer& parent)
{
    // Create the isolines...
    prepare(data);

    if ( legend_only_ ) return;

    // The shading needs the isolines..
    (*shading_)(this, data, parent);

    // Now we feed the task...
        for (vector<vector<Polyline* >* >::const_iterator lines = lines_.begin(); lines != lines_.end(); ++lines)
        {
          for (vector<Polyline* >::const_iterator poly = (*lines)->begin(); poly != (*lines)->end(); ++poly)
          {
            if ( (*poly)->empty() ) continue;
            (*poly)->setThickness(0);
            (*poly)->setColour(Colour("black"));
            (*label_)(**poly, (*poly)->front().value());
            parent.push_back(*poly);
          }
        }
        lines_.clear();
}



void IsoPlot::visit(Data& data, LegendVisitor& legend) {

    if ( magCompare(legend_special_, "spaghetti") ) {
        Polyline* blue = new Polyline();
        blue->setColour(Colour("blue"));
        blue->setLineStyle(M_DASH);
        Polyline* red = new Polyline();
        red->setColour(Colour("red"));
        red->setLineStyle(M_DASH);
        Polyline* grey = new Polyline();
        grey->setColour(Colour("grey"));
        grey->setLineStyle(M_SOLID);
        legend.add(new DoubleLineEntry("Det", blue,0));
        legend.add(new DoubleLineEntry("Control", red,0));
        legend.add(new DoubleLineEntry("EPS members", grey,0));
        return;
    }
    switch (legend.legendType())  {
        case LegendMethod::CONTINUOUS :
        case LegendMethod::DISJOINT: {
            (*shading_).visit(legend);
            if (shading_->hasLegend() ) return;
            if ( rainbow_) {

                for ( vector<double>::iterator level = levelSelection_->begin(); level != levelSelection_->end(); ++level) {
                    Polyline* line = new Polyline();

                    line->setColour((*rainbowMethod_)(*level));
                    line->setLineStyle(line_style(*level));
                    line->setThickness(thickness(*level)*5);
                    legend.add(new RainbowEntry(*level, line));
                }
                break;
            }
            Polyline* line1 = new Polyline();
            Polyline* line2 = 0;
            line1->setColour(*colour_);
            line1->setLineStyle(style_);
            line1->setThickness(thickness_);

            highlight_->visit(line2);
            legend.add(new DoubleLineEntry(legend_text_, line1, line2));
            // Should do something for the wrep legend!!!
            if ( legend.size() < 3 && legend.wrep_ ) {
                legend.add(new EmptyEntry());
            }
            break;
        }
        case LegendMethod::HISTOGRAM: {
            legend.newLegend();
            IntervalMap<Colour> beans;
            vector<double>::iterator from = levelSelection_->begin();
            vector<double>::iterator to = levelSelection_->begin();
            ++to;
            for (;  to != levelSelection_->end(); ++to){
                Colour colour = *colour_;
                shading_->colour(*from, colour);
                beans.insert(make_pair(Interval(*from, *to), colour ));
                ++from;
            }
            Histogram helper;
            const Transformation& transformation = legend.transformation();
            IntervalMap<int>& histogram = helper.histogram(beans, data.points(transformation,false));
            int total = 0;
            for (IntervalMap<int>::const_iterator  interval = histogram.begin(); interval != histogram.end(); ++interval){
                total+=interval->second;
            }
            bool first = true;
            for ( IntervalMap<Colour>::const_iterator interval = beans.begin(); interval != beans.end(); ++interval) {
                   Polyline* box = new Polyline();

                   double min =interval->first.min_;
                   double max = interval->first.max_;

                   box->setShading(new FillShadingProperties());
                   box->setFillColour(interval->second);
                   box->setFilled(true);
                   BoxEntry* entry = new BoxEntry(min, max, box);
                   int count = histogram.find(min, 0);
                   entry->population(count);
                   entry->totalPopulation(total);
                   if (first) {
                       entry->first();
                       first = false;
                   }


                   legend.add(entry);

            }

            legend.last();
        }
    }

}


 void NoIsoPlot::visit(Data& data, LegendVisitor& legend) {

    switch (legend.legendType())  {
            case LegendMethod::CONTINUOUS :
            case LegendMethod::DISJOINT: {
                (*shading_).visit(legend);
                break;
            }
            case LegendMethod::HISTOGRAM: {
                legend.newLegend();
                IntervalMap<Colour> beans;
                vector<double>::iterator from = levelSelection_->begin();
                vector<double>::iterator to = levelSelection_->begin();
                ++to;
                for (;  to != levelSelection_->end(); ++to){
                    Colour colour = *colour_;
                    shading_->colour(*from, colour);
                    beans.insert(make_pair(Interval(*from, *to), colour ));
                    ++from;
                }
                Histogram helper;
                IntervalMap<int>& histogram = helper.histogram(beans, data.points(legend.transformation(), true));
                int total = 0;
                for (IntervalMap<int>::const_iterator  interval = histogram.begin(); interval != histogram.end(); ++interval){
                    total+=interval->second;
                }
                bool first = true;
                for ( IntervalMap<Colour>::const_iterator interval = beans.begin(); interval != beans.end(); ++interval) {
                       Polyline* box = new Polyline();

                       double min =interval->first.min_;
                       double max = interval->first.max_;

                       box->setShading(new FillShadingProperties());
                       box->setFillColour(interval->second);
                       box->setFilled(true);
                       BoxEntry* entry = new BoxEntry(min, max, box);
                       int count = histogram.find(min, 0);
                       entry->population(count);
                       entry->totalPopulation(total);
                       if (first) {
                           entry->first();
                           first = false;
                       }


                       legend.add(entry);

                }

                legend.last();
            }
        }
}



void IsoPlot::visit(Data& data, PointsHandler& points, HistoVisitor& visitor)
{
    IntervalMap<Colour> beans;
    if ( !visitor.basic() ) {

        vector<double>::iterator from = levelSelection_->begin();
        vector<double>::iterator to = levelSelection_->begin();
        ++to;
        for (;  to != levelSelection_->end(); ++to){
            Colour colour = *colour_;
            shading_->colour(*from, colour);
            beans.insert(make_pair(Interval(*from, *to), colour ));
            ++from;
        }
    }
    Histogram helper;
    helper.visit(beans, data, points, visitor);
}

CellArray::CellArray(MatrixHandler& data, 
        IntervalMap<int>& range, 
        const Transformation& transformation, int width, int height, float resol, const string& technique) :
        rangeFinder_(range),data_(data)
{
    Timer timer("CellArray", "CellArray");
    int r = height/resol;
    int c = width/resol;

    rows_ = r;
    columns_ = c;

    points_.set(rows_+1, columns_+1);
    reserve(rows_* columns_);

    missing_ = data.missing();

    double firstx = transformation.getMinPCX();
    double firsty = transformation.getMinPCY();

    double stepx =  ( transformation.getMaxPCX() -  transformation.getMinPCX() )/ (columns_);
    double stepy =  ( transformation.getMaxPCY() -  transformation.getMinPCY() )/ (rows_);
    {
        Timer timer("compute matrix", "prepare");

        vector< std::pair<double, double> > xypoints;
        vector< std::pair<double, double> > geopoints;
        double x = firstx, y = firsty;
        xypoints.reserve(rows_+1 * columns_+1);
        for (int row = 0; row < rows_+1; row++) {
                x = firstx;
                y = firsty + (row*stepy);
                points_.rowsAxis().push_back(y);
                for (int column = 0; column < columns_+1; column++) {
                    x = firstx + (column*stepx);
                    xypoints.push_back(make_pair(x, y));
                    if ( row == 0) {
                        points_.columnsAxis().push_back(x);
                    }
                   
                }


        }
        points_.setMapsAxis();

        transformation.revert(xypoints, geopoints);
        //data.interpolate(points_, geopoints);
        
        vector< std::pair<double, double> >::iterator geo= geopoints.begin();
        double min =  data.min();
        double max =  data.max();
        double missing =  data.missing();

        


        int i = 0;

        MagLog::dev() << "min = " << data.min() << "  max = " << data.max() << endl;
        for (vector< std::pair<double, double> >::iterator xy = xypoints.begin(); xy != xypoints.end(); ++xy) {

                    double value;
                    if  ( geo->second == -1000) {
                        value = missing;
                        //cout << "MISSING AREA" << endl;
                    }
                    else {
                        value = (magCompare(technique, "nearest")) ?
                            data.nearest(geo->second, geo->first):data.interpolate(geo->second, geo->first);
                        //value = data.interpolate(geo->second, geo->first);
                    }
                    if (value != missing) {
                        if (value < min)
                            value = min;
                       
                        if (value > max)
                            value=max; 
                        
                    }   
                    else {
                        //cout << "MISSING VALUE-->" << geo->second << ", " << geo->first << endl;
                    }                 
                    points_[i] = value;
                    i++;
                    ++geo;
        }
       

    }

    for (int row = 0; row < rows_; row++)
        for (int column = 0; column < columns_; column++) {
            push_back(new Cell(*this, row, column));
        }
}


CellArray::CellArray(MatrixHandler& data, IntervalMap<int>& range) : rangeFinder_(range), data_(data)
{

}

GridArray::GridArray(MatrixHandler& data, IntervalMap<int>& range, const Transformation& transformation, int width, int height, float resol, const string& technique) : CellArray(data, range)
{
    Timer timer("GridArray", "GridArray");
    if ( magCompare(technique, "middle") ) {
        rows_ = data.rows();
        columns_ = data.columns();
    }
    else {
        rows_ = data.rows() -1;
        columns_ = data.columns() -1;
    }
    reserve(rows_*columns_);
    for (int row = 0; row < rows_; row++)
        for (int column = 0; column < columns_; column++) {
            push_back(new GridCell(*this, row, column, transformation, technique));
        }
}


GridCell::GridCell(const CellArray& data, int row, int column, const Transformation& transformation, const string& technique):
        Cell(data), transformation_(transformation)  {

        row_ = row;
        column_ = column;
        int row1 = (row) ? row - 1 : 0;
        int row2 = ( row == data.data_.rows() -1 ) ? row : row + 1;
        missing_ = data.missing_;
        value_ = data.data_(row_, column_);
        min_ = max_ = value_;
        int column1 = column ? column - 1 : 0;
        int column2 = ( column == data.data_.columns() -1 ) ? column : column + 1;


        range_ = data.rangeFinder_.find(value_, -1);
        min_ = max_ = range_;
        if ( range_ == -1 )
            outOfRange_ = 4;

        if ( magCompare(technique, "middle")) {

            double rc1 = data.data_.column(row, column1);
            double crc = data.data_.column(row, column);
            double rc2 = data.data_.column(row, column2);
            double r1c = data.data_.row(row1, column);
            double r2c = data.data_.row(row2, column);
            double rrc = data.data_.row(row, column);

            columns_[0] = (rc1 + crc)/2;
            columns_[1] = (crc + rc2)/2;
            columns_[2] = (crc + rc2)/2;
            columns_[3] = (rc1 + crc)/2;
            rows_[0] = (r1c + rrc)/2;
            rows_[1] = (r1c + rrc)/2;
            rows_[2] = (rrc + r2c)/2;
            rows_[3] = (rrc + r2c)/2;
        }

        else {

            columns_[0] = data.data_.column(row, column);
            columns_[1] = data.data_.column(row, column2);
            columns_[2] = data.data_.column(row, column2);
            columns_[3] = data.data_.column(row, column);
            rows_[0] = data.data_.row(row, column);
            rows_[1] = data.data_.row(row, column);
            rows_[2] = data.data_.row(row2, column);
            rows_[3] =  data.data_.row(row2, column);
            }

        double minx = transformation.getMinPCX();
        double maxx = transformation.getMaxPCX();
        double miny = transformation.getMinPCY();
        double maxy = transformation.getMaxPCY();

        bool clip = false;
        static int count = 0;
        count++;

        for (int i = 0; i < 4; i++) {


            transformation.fast_reproject(columns_[i], rows_[i]);

            if ( columns_[i] < minx ) {
                columns_[i] = minx;
                clip = true;
               }
            if ( columns_[i] > maxx ) {
                columns_[i] = maxx ;
                clip = true;
                }
            if( rows_[i] < miny ) {
                rows_[i] = miny ;
                clip = true;
                }
            if ( rows_[i] > maxy ) {
                rows_[i] = maxy;
                clip = true;
                }


        }
        if ( clip) {
            double xmin = std::min(columns_[0], columns_[2]);
            double xmax = std::max(columns_[0], columns_[2]);
            double ymin = std::min(rows_[0], rows_[2]);
            double ymax = std::max(rows_[0], rows_[2]);
            if ( xmin == xmax || ymin == ymax) {
                range_ = -1;
                outOfRange_ = 4;
            }

        }

    }

void IsoPlot::setThicknessAndStyle()
{
    if ( rainbowThicknessList_.empty() )
        rainbowThicknessList_.push_back(thickness_);

    vector<LineStyle> styles;
    MagTranslator<string, LineStyle> translator;
    for (vector<string>::iterator style = rainbowStyleList_.begin(); style != rainbowStyleList_.end(); ++style ) {
        styles.push_back(translator(*style));
    }

    if ( styles.empty() )
        styles.push_back(style_);

    vector<int>::iterator thickness = rainbowThicknessList_.begin();
    vector<LineStyle>::iterator style = styles.begin();

    for (vector<double>::iterator level = levelSelection_->begin(); level != levelSelection_->end(); ++level ) {
        thickness_list_.insert(make_pair(*level, *thickness));
        line_style_list_.insert(make_pair(*level, *style));
        ++thickness;
        if ( thickness == rainbowThicknessList_.end() ) {
            if ( rainbowThicknessListPolicy_ == M_LASTONE)
                --thickness;
            else
                thickness = rainbowThicknessList_.begin();
        }
        ++style;
        if ( style == styles.end() ) {
            if ( rainbowStyleListPolicy_ == M_LASTONE)
                --style;
            else
                style = styles.begin();
        }

    }
    //*levelSelection_
}

int IsoPlot::thickness(double level)
{
    map<double, int>::iterator thickness = thickness_list_.find(level);
    if ( thickness !=  thickness_list_.end() )
        return thickness->second;
    return thickness_;
}
LineStyle IsoPlot::line_style(double level)
{
    map<double, LineStyle>::iterator style = line_style_list_.find(level);
        if ( style !=  line_style_list_.end() )
            return style->second;
        return style_;
}
