Subversion Repositories public iLand

Rev

Rev 1221 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QtGui>
#include <QtXml>
#include <imagestamp.h>
#include "lightroom.h"
#include "stampcontainer.h"
#include "speciesset.h"
#include "exception.h"
#include "paintarea.h"
#include  "globalsettings.h"
//GlobalSettings *GlobalSettings::mInstance = 0;

// global settings
QDomDocument xmldoc;
QDomNode xmlparams;

// global Object pointers
LightRoom *lightroom = 0;
StampContainer *stamp_container=0;
StampContainer *reader_stamp_container=0;
QList<Species*> tree_species;

double distance(const QPointF &a, const QPointF &b)
{
    return sqrt( (a.x()-b.x())*(a.x()-b.x()) + (a.y()-b.y())*(a.y()-b.y()) );
}

QString setting(const QString& paramname)
{
    if (!xmlparams.isNull())
        return xmlparams.firstChildElement(paramname).text();
    else
        return "ERROR";
}


double nrandom(const float& p1, const float& p2)
{
    return p1 + (p2-p1)*(rand()/float(RAND_MAX));
}

void myMessageOutput(QtMsgType type, const char *msg)
 {
     switch (type) {
     case QtDebugMsg:
         MainWindow::logSpace()->appendPlainText(QString(msg));
         MainWindow::logSpace()->ensureCursorVisible();
         break;
     case QtWarningMsg:
         MainWindow::logSpace()->appendPlainText(QString("WARNING: %1").arg(msg));
         MainWindow::logSpace()->ensureCursorVisible();
         fprintf(stderr, "WARNING: %s\n", msg);
         break;
     case QtCriticalMsg:
         fprintf(stderr, "Critical: %s\n", msg);
         break;
     case QtFatalMsg:
         fprintf(stderr, "Fatal: %s\n", msg);
         abort();
     }


 }

QPlainTextEdit *MainWindow::mLogSpace=NULL;

QPlainTextEdit* MainWindow::logSpace()
{
   return mLogSpace;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindowClass)
{
    ui->setupUi(this);

    connect( ui->PaintWidget, SIGNAL(needsPainting(QPainter&)),
             this, SLOT(repaintArea(QPainter&)) );


    mLogSpace = ui->logOutput;
    qInstallMsgHandler(myMessageOutput);
    // load xml file
    xmldoc.clear();
    QString argText = QApplication::arguments().last();
    if (QApplication::arguments().count()>1 && !argText.isEmpty())
        ui->initFileName->setText(argText);

    QString xmlFile = Helper::loadTextFile(ui->initFileName->text());

    if (!xmlFile.isEmpty()) {
        ui->iniEdit->setPlainText(xmlFile);

        if (!xmldoc.setContent(xmlFile)) {
            QMessageBox::information(this, "title text", "Cannot set content of XML file " + ui->initFileName->text());
            return;
        }
    }

    on_actionEdit_XML_settings_triggered();
    // update

    qDebug() << "threadcount: " << QThread::idealThreadCount();

}

MainWindow::~MainWindow()
{
    delete ui;
    if (lightroom)
        delete lightroom;
}

void MainWindow::on_applyXML_clicked()
{
    xmldoc.clear();
    // load for edit
    QString xml = ui->iniEdit->toPlainText();
    QString errMsg;
    int errLine, errCol;
    if (!xmldoc.setContent(xml, &errMsg, &errLine, &errCol)) {
        QMessageBox::information(this, "Error applying XML",
                                 QString("Error applying xml line %1, col %2.\nMessage: %3").arg(errLine).arg(errCol).arg(errMsg));
        return;
    }
    qDebug() << "XML file read.";
}


void MainWindow::on_saveFile_clicked()
{
    QString content = ui->iniEdit->toPlainText();
    if (!content.isEmpty())
         Helper::saveToTextFile(ui->initFileName->text(), content);

}


void MainWindow::repaintArea(QPainter &)
{
    // select drawing type...
    switch (m_gfxtype) {
        case 0: // paint FON cells
            return;
        case 1:  // paint Lightroom - studio --- painting is done on the background image of PaintArea
            break;
        case 2: // paint Lightroom
        default: break; // no painting
    }
    // fix viewpoint
    vp.setScreenRect(ui->PaintWidget->rect());
}

void MainWindow::on_actionLightroom_triggered()
{
    ui->editStack->setCurrentIndex(1);
    ui->headerCaption->setText("Lightroom");
    m_gfxtype = 1;
    ui->PaintWidget->update();
}

void MainWindow::on_actionEdit_XML_settings_triggered()
{

    ui->editStack->setCurrentIndex(0);

    ui->PaintWidget->update();
}

void MainWindow::on_actionFON_action_triggered()
{
    ui->editStack->setCurrentIndex(2);
    ui->headerCaption->setText("Field Of Neighborhood test environment");
    m_gfxtype = 0;
    ui->PaintWidget->update();

}

void MainWindow::on_pbCreateLightroom_clicked()
{
    if (xmldoc.isNull()) {
        Helper::msg("!!XML not loaded!!");
        return;
    }
    QDomElement docLightroom = xmldoc.documentElement(); // top element

    if (docLightroom.isNull()) MSGRETURN("cant find node 'lightroom' in xml");
    int x,y,z;
    x = docLightroom.firstChildElement("size").attribute("x").toInt();
    y = docLightroom.firstChildElement("size").attribute("y").toInt();
    z = docLightroom.firstChildElement("size").attribute("z").toInt();
    double cellsize = docLightroom.firstChildElement("size").attribute("cellsize").toDouble();

    int hemisize = docLightroom.firstChildElement("hemigrid").attribute("size").toInt();
    double lat = docLightroom.firstChildElement("hemigrid").attribute("latitude").toFloat();
    double diffus = docLightroom.firstChildElement("hemigrid").attribute("diffus").toFloat();

    // create a lightroom object...
    if (!lightroom)
        lightroom = new LightRoom();

    lightroom->setup(x,y,z,cellsize,
                    hemisize,lat,diffus);
    LightRoomObject *lro = new LightRoomObject();
    lro->setuptree(40., 20., "5*(1-x*x)");
    lightroom->setLightRoomObject(lro);

    qDebug() << "Lightroom setup complete";
}

void MainWindow::on_testLRO_clicked()
{
        // setup a lightroom object, and do some tests...
    double x = ui->lr_x->text().toDouble();
    double y = ui->lr_y->text().toDouble();
    double z = ui->lr_z->text().toDouble();
    double azi = ui->lrAzim->text().toDouble();
    double elev = ui->lrElevation->text().toDouble();

    LightRoomObject lro;
    lro.setuptree(40., 10., "5*(1-x*x)");
    qDebug()<<"x:"<<x<<"y:"<<y<<"z:"<<z<<"azimuth:"<<azi<<"elevation:"<<elev << lro.hittest(x,y,z,RAD(azi), RAD(elev));
    //qDebug()<<"-10,-10,0 - azimuth 42, elev: 45:" << lro.hittest(-10,-10,0,RAD(42), RAD(45));
}

void MainWindow::on_lroTestHemi_clicked()
{
    double x = double(ui->lrSliderX->value());// ui->lr_x->text().toDouble();
    double y = ui->lrSliderY->value();
    double z = ui->lrSliderZ->value();
    ui->lr_x->setText(QString::number(x));
    ui->lr_y->setText(QString::number(y));
    ui->lr_z->setText(QString::number(z));
    if (!lightroom)
        MSGRETURN("Lightroom NULL!");
    DebugTimer t("single point");
    double res = lightroom->calculateGridAtPoint(x,y,z);
    ui->lrHemiValue->setText(QString::number(res));
    // now paint...
    //ui->PaintWidget->drawImage();
    lightroom->shadowGrid().paintGrid(ui->PaintWidget->drawImage());
    ui->PaintWidget->update(); // repaint
    //qDebug() << lightroom->shadowGrid().dumpGrid();

}
void MainWindow::on_lrLightGrid_clicked()
{
    lightroom->solarGrid().paintGrid(ui->PaintWidget->drawImage());
    ui->PaintWidget->update(); // repaint
    qDebug() << lightroom->solarGrid().dumpGrid();
}

void MainWindow::on_lrCalcFullGrid_clicked()
{
    if (!lightroom)
        MSGRETURN("Lightroom NULL!");
    lightroom->calculateFullGrid();
    float maxvalue = lightroom->result().max();
    qDebug() << "maxvalue" << maxvalue;
    const FloatGrid &result = lightroom->result();
    QString res;
    for (int x=0;x<result.sizeX();x++){
        for (int y=0;y<result.sizeY();y++) {
            res+=QString::number(result.constValueAtIndex(QPoint(x,y))) + ";";
        }
        res+="\n";
    }
    qDebug()<< res;
}


void MainWindow::on_lrProcess_clicked()
{
    if (xmldoc.isNull()) {
        Helper::msg("!!XML not loaded!!");
        return;
    }

    QDomElement docElem = xmldoc.documentElement();

    QString output_file = docElem.firstChildElement("outputStamp").text();
    QDomElement tree = docElem.firstChildElement("trees").firstChildElement("tree");

    double cut_threshold = docElem.firstChildElement("cutvalue").text().toDouble();
    QString cut_mode = docElem.firstChildElement("cutoffMode").text();
    QString agg_mode = docElem.firstChildElement("aggregationMode").text();
    int mode=-1;
    if (agg_mode=="sum") {
        mode=1;
        qDebug() << "aggregation mode set to 'sum'";
    }
    if (agg_mode=="mean") {
        qDebug() << "aggregation mode set to 'mean'";
        mode=0;
    }
    if (mode==-1) {
        Helper::msg("Error: invalid or no aggregationMode specified!");
        return;
    }

    QString stamp_desc = docElem.firstChildElement("desc").text();
    QString binaryReaderStampFile =  docElem.firstChildElement("readerStamp").text();
    qDebug() << "cutting stamps when averaged absoulte value of rings is below"<<cut_threshold;
    qDebug() << "reading binary stamp reader file" << binaryReaderStampFile;

    if (!Helper::question(QString("Create writer stamps?\ntarget=%1, \nreader stamps=%2").arg(output_file, binaryReaderStampFile)))
        return;
    float crown, height, bhd;
    QString formula, name;

    LightRoomObject *lro = new LightRoomObject();
    lightroom->setLightRoomObject(lro);
    lightroom->setAggregationMode(mode);

    StampContainer readers;

    QFile infile(binaryReaderStampFile);
    infile.open(QIODevice::ReadOnly);
    QDataStream in(&infile);
    readers.load(in);
    infile.close();

    StampContainer container;
    container.useLookup(false); // disable lookup table (not necessary for creation)

    DebugTimer creation_time("total creation of stamps");

    while (!tree.isNull()) {
        name = tree.attribute("name");
        height = tree.attribute("h").toDouble();
        crown = tree.attribute("crown").toDouble();
        bhd = tree.attribute("bhd").toDouble();
        formula = tree.attribute("formula");
        ///////////////////////////
        lro->setuptree(height, crown, formula);
        qDebug() << "start" << name;
        lightroom->calculateFullGrid();

        // store as textfile:
        //result = gridToString( lightroom->result() );
        //Helper::saveToTextFile(path + "\\ " + name + ".txt", result);

        // save stamp as image:
        //QImage img = gridToImage( lightroom->result() );
        //img.save(path + "\\ " + name + ".jpg", "JPG", 100);

        // averaged copy: FloatGrid gr = lightroom->result().averaged(avg_cells);
        const FloatGrid &gr = lightroom->result();
        // calculate sums...
        QVector<double> sums; // stores sum per ring (rectangle)
        QVector<double> rel_sum; // stores sum/px
        QVector<double> max_vals; // stores values in north direction
        double sum;
        int ring_count;
        double total_sum = 0.;
        for (int o=0; o<gr.sizeX()/2; o++) {
            sum = 0; ring_count=0;
            // top and bottom
            for (int i=o; i<gr.sizeX()-o; i++) {
                sum += gr(i, o);
                sum += gr(i, gr.sizeX()-1-o);
                ring_count+=2;
            }
            // left, right :: do not calculate the edges two times....
            for (int i=o+1; i<gr.sizeX()-o-1; i++) {
                sum += gr(o, i);
                sum += gr(gr.sizeX()-1-o, i);
                ring_count+=2;
            }
            total_sum += sum;
            sums.push_back(sum);
            rel_sum.push_back(sum / double(ring_count) );
            max_vals.push_back(gr(gr.sizeX()/2, gr.sizeX()-1-o)); // bottom....
        }
        if (gr.sizeX()% 2) {
            total_sum += gr(gr.sizeX()/2, gr.sizeX()/2); // center pixel for unevenly sized grids
            sums.push_back(gr(gr.sizeX()/2, gr.sizeX()/2)); // center pixel for unevenly sized grids
            rel_sum.push_back(gr(gr.sizeX()/2, gr.sizeX()/2)); // center pixel for unevenly sized grids
            max_vals.push_back(gr(gr.sizeX()/2, gr.sizeX()/2)); // center pixel
        }
        int end_ring, target_grid_size;
        /* version < 20090905: average ring value
        for (end_ring=0;end_ring<rel_sum.count();end_ring++)
            if (rel_sum[end_ring]>cut_threshold)
                break;
        end_ring = rel_sum.count() - end_ring; // */


        // version > 20090905: based on total area
        if (cut_mode == "sum") {
            double rsum = 0;
            for (end_ring=0;end_ring<sums.count();end_ring++) {
                rsum += sums[end_ring];
                // threshold: sum of influence > threshold
                if (rsum>cut_threshold*total_sum)
                    break;
            }
        } else if (cut_mode == "north") {
            // 20100527: based on direction with maximum values
            for (end_ring=0;end_ring<sums.count();end_ring++) {
                if (max_vals[end_ring]>cut_threshold)
                   break;
            }

        } else {
           qDebug() << "ERROR: invalid cutoffMode (possible values: sum, north)!!! Exiting.";
           return;
        }
        end_ring = sums.count() - end_ring;
        if (end_ring<2) // minimum ring-count=2 (i.e. 9pixel)
            end_ring=2;

        target_grid_size = 2*end_ring - 1; // e.g. 3rd ring -> 5x5-matrix
        qDebug() << "break at ring" << end_ring << "with cutoff mode" << cut_mode;
        qDebug() << "circle sum relsum maxvals";
        for (int i=0;i<sums.count();i++)
            qDebug() << i << sums[i] << rel_sum[i] << max_vals[i];


        // test: use subpixel averages ....
        /*
        FloatGrid gr3x3 = lightroom->result().averaged(3);
        QImage img3x3 = gridToImage( gr3x3 );
        img3x3.save(path + "\\ " + name + "_3x3.jpg", "JPG", 100);
        Helper::saveToTextFile(path + "\\ " + name + "_3x3.txt", gridToString(gr3x3));
        result="";
        for (int x=0;x<3;x++)
            for (int y=0;y<3;y++) {
            FloatGrid gr3x3 = lightroom->result().averaged(3,x,y);
            result+="\r\n" + gridToString(gr3x3);

        }
        Helper::saveToTextFile(QString("%1\\%2_shift.txt").arg(path, name), result); */


        // store to container
        Stamp *stamp = stampFromGrid(gr, target_grid_size);
        if (!stamp)
            return;
        qDebug() << "created stamp with size (n x n)" << stamp->size() << "in an data-block of:" << stamp->dataSize();
        // process reading area
        double maxradius = lro->maxRadius();
        const Stamp *readerstamp = readers.readerStamp(maxradius);
        if (readerstamp) {
            int offset = stamp->offset() - readerstamp->offset();
            qDebug() << "fetching read-sum. offset stamp" << stamp->offset() << "offset readerstamp" << readerstamp->offset();
            double sum = 0;
            for (int x=0;x<readerstamp->size();x++)
                for (int y=0;y<readerstamp->size(); y++)
                    sum += *(readerstamp->data(x,y)) * *(stamp->data(x+offset, y+offset));
            qDebug() << "sum of reader-area over stamp" << sum;
        } else qDebug() << "!!! no readerstamp available!!!";

        double hd = qRound( height*100 / bhd );
        container.addStamp(stamp,bhd, hd, lro->maxRadius()); // 3rd param was: ,
        ///////////////////////////
        tree = tree.nextSiblingElement("tree");
    }
    qDebug() << "finished!!!";
    // write container to a file....
    QFile file(output_file);
    file.open(QIODevice::WriteOnly);
    container.setDescription(stamp_desc); // provided in xml...
    QDataStream out(&file);   // we will serialize the data into the file
    container.save(out);
    file.close();
    qDebug() << "current content of the container:";
    qDebug() << container.dump();
}


void MainWindow::on_lrLoadStamps_clicked()
{
    {
        QString fileName = Helper::fileDialog("Name for binary stamp file");
        if (fileName.isEmpty())
            return;
        QFile infile(fileName);
        infile.open(QIODevice::ReadOnly);
        QDataStream in(&infile);
        StampContainer container;
        container.load(in);
        infile.close();
        qDebug() << "Dumping content of Stamp-container" << fileName;
        qDebug() << container.dump();
        // and the reader-stamps....
    }
}


// create "reader" stamps....
void MainWindow::on_lrReadStamps_clicked()
{
    FloatGrid grid; // use copy ctor
    grid.setup(QRectF(-21., -21, 42, 42),2.); // 0/0 is the center of the center cell
    StampContainer container;
    int totcount=0;
    DebugTimer t;
    for (double radius=0.5; radius<=15; radius+=0.1) {
        qDebug() << "creation of reader stamp for radius" << radius;
        grid.initialize(0.);
        float x,y;
        int tested=0, yes=0;
        // the center point of the cell where the center of the tree is,
        // is at 1m/1m (at a cell width of 2m)
        for (x=-radius;x<=radius;x+=0.01)
            for (y=-radius;y<=radius;y+=0.01) {
                tested++;
                if ( x*x + y*y < radius*radius) {
                    grid.valueAt(x,y)++; yes++;
                }
            }
        qDebug() << "tested" << tested << "hit" << yes << "ratio" << 4.*yes/double(tested); // that should be pi, right?
        totcount+=tested;
        FloatGrid ngrid = grid.normalized(1.); // normalize with 1
        // create a stamp with a fitting size
        Stamp *stamp;
        int width=11;
        // radius: 0..<1: 1, >=1 <3: 3, >=3 < 5: 5, ...
        width = (((int)radius+1)/2)*2 + 1;

        stamp = stampFromGrid(ngrid,width);
        // save stamp
        container.addReaderStamp(stamp, radius);

    } // for (radius)
    qDebug() << "tested a total of" << totcount;
    QString targetFile = xmldoc.documentElement().firstChildElement("readerStamp").text();
    if (!Helper::question(QString("Save readerfile to %1?").arg(targetFile))) {
        qDebug() << container.dump();
        return;
    }
    QFile file(targetFile);
    if (!file.open(QIODevice::WriteOnly)) {
        qDebug() << "Error opening reader file" << targetFile << "with error:" << file.errorString();
    }
    QDataStream out(&file);   // we will serialize the data into the file
    container.setDescription("Reader-stamps for crown-radii ranging from 0.5m to 15m, stepwidth is 0.1m.");
    container.save(out);
    file.close();
    qDebug() << container.dump();


}



void MainWindow::on_openFile_clicked()
{
    QString fileName = Helper::fileDialog("select XML-ini file for FonStudio...");
    if (fileName.isEmpty())
        return;
    ui->initFileName->setText(fileName);
    QString xmlFile = Helper::loadTextFile(ui->initFileName->text());
    ui->iniEdit->setPlainText(xmlFile);
}


void MainWindow::on_reloadFile_clicked()
{
    QString xmlFile = Helper::loadTextFile(ui->initFileName->text());
    ui->iniEdit->setPlainText(xmlFile);
}