Skip to content
Snippets Groups Projects
Forked from mlz / Frida
1045 commits behind the upstream repository.
import.cpp 16.87 KiB
//**************************************************************************************************
//*  FRIDA: fast reliable interactive data analysis                          
//*  (C) Joachim Wuttke 1990-, v2(C++) 2001-                                 
//*  http://apps.jcns.fz-juelich.de/frida                                    
//**************************************************************************************************

//! \file  import.cpp
//! \brief NImport: import tables or make data ex nihilo

#include <iostream>
#include <string>
#include <vector>

#include <../trivia/string_ops.hpp>
#include <../trivia/file_ops.hpp>
#include <../readplus/ask.hpp>

#include "defs.hpp"
#include "olf.hpp"
#include "mem.hpp"
#include "obj.hpp"
#include "slice.hpp"
#include "import.hpp"


//! Create a new online file from interactive input.

void NImport::read_in()
{
    POld fout;

    // *** set file name and coordinates ***

    static string fnam;
    fout->name = wask("Save as", fnam);

    static CCoord zco;
    fout->xco.ask_and_set( "x" );
    fout->yco.ask_and_set( "y" );
    cout << "if there is only one spectrum, answer \"\\\" to the following:\n";
    zco.ask_and_set( "z" );
    if ( zco.defined() ) fout->ZCo.push_back(zco);

    // *** set input mode ***

    static int mInpX = -1;
    cout << "input modes:\n"
        "  (1) enter x pointwise\n"
        "  (2) set regular x values\n"
        "  (3) enter x-y pairs\n"
        "  (4) select x,y from multicolumn input\n"
        "  (5) select x,y,z from multicolumn input\n";
    mInpX = iask( "Option", mInpX );
    if( mInpX==5 && !zco.defined() )
        throw "need z coordinate for this option";

    static int imcx, imcy, imcz; 
    int mInpY=-1; // ask

    switch(mInpX) {
    case 0: 
        return;
    case 1: case 2:
        break;
    case 3:
        imcx = 0;
        imcy = 1;
        mInpY = 0;
        break;
    case 4: case 5:
        imcx = iask("x from column #", imcx);
        if (imcx<0) return;
        imcy = iask("y from column #", imcy);
        if (imcy<0) return;
        mInpY = 0;
        break;
    default:
        throw "invalid option";
    }

    if (mInpY<0) {
        cout << "input modes:\n"
            "  (1) enter y pointwise\n"
            "  (2) set regular y values\n"
            "  (3) select y from multicolumn input\n";
        static int mInpY_in = -1;
        mInpY_in = iask( "Option", mInpY_in );
        if ( mInpY_in<1 || mInpY_in>3 )
            throw "invalid option";
        mInpY = mInpY_in;
    }

    switch(mInpY) {
    case 0: case 1: case 2:
        break;
    case 3:
        imcy = iask("y from column #", imcy);
        if (imcy<0) return;
        break;
    default:
        throw "PROGRAM ERROR in Make: invalid switch(mInpY)";
    }

    static int mInpZ;
    if        ( mInpX==5 ){
        mInpZ = 3;
    } else if (zco.defined()) {
        cout << "at present, we allow only _one_ z value per spectrum\n";
        cout << "input modes:\n"
            "  (1) enter z per spectrum\n"
            "  (2) select z from multi-z header line\n";
        mInpZ = iask( "Option", mInpZ );
        if ( mInpZ<0 || mInpZ>2 )
            throw "invalid option";
    } else {
        mInpZ = 0;
    }

    switch (mInpZ) {
    case 0: case 1:
        break;
    case 2:
        if (! (mInpX==4 || mInpY==3) )
            throw "invalid combination of options:"
                " y is not read from multicolumn format";
        break;
    case 3:
        imcz = iask( "z0 from column", imcz );
        if( imcz<0 ) return;
        break;
    default:
        throw "PROGRAM ERROR in Make: invalid switch(mInpZ)";
    }

    static int imcy_delta=0; 
    static int imcy_last;
    cout << "DEBUG "<< mInpZ << "\n";
    if (mInpZ==2) {
        imcy_delta = iask("step in number of y column", imcy_delta);
        if (imcy_delta) 
            imcy_last = iask("last y column", imcy_last);
    } else {
        imcy_delta = 0;
    }

    // *** loop over spectra ***

    int n = 0;
    int imcm;
    string quest, line;
    double val, zold=NAN;
    vector<double> linv;

    int ns = 0;
    while(1) {
        PSpec s( new CSpec );

        switch(mInpZ) {
        case 1:
            line = sask("Enter z value", "");
            // cout << "DEBUG: line [" << line << "]\n";
            if (!triv::str2vec(line, &linv, 2) < 0) {
                cout << "-> invalid line [" << line << "]\n";
                goto store;
            }
            // printf ("DEBUG: size = %d\n", linv.size());
            if (linv.size()<1) {
                cout << "found no z in line [" << line <<
                    "] => assuming end-of-input\n";
                goto store;
            }
            // printf ("DEBUG: val = %g\n", linv[0]);
            val = linv[0];
            break;
        case 2:
            line = sask("Enter header line with z values", "");
            if (!triv::str2vec(line, &linv, imcy+1)) {
                cout << "-> bad input\n";
                goto store;
            }
            if (linv.size()<=imcy) {
                cout << "-> not enough entries in header\n";
                goto store;
            }
            val = linv[imcy];
            break;
        }
        if (mInpZ<=2)
            s->z.push_back( PObjDbl( new CObjDbl(val) ) );

        switch(mInpX) {
        case 1:
            cout << "Enter x values (or empty line to exit):\n";
            while (1) {
                quest = "[" + S(n) + "] ";
                line = sask(quest.c_str(), "");
                if (sscanf(line.c_str(), "%lg", &val)!=1) break;
                s->x.push_back(val);
                n++;
            }
            break;
        case 2:
            //S.x.Ask("x");
            //n = S.x.size();
            throw "case 2 broken";
            break;
        case 3: case 4: case 5:
            cout<<"Enter data row (or empty line to exit):\n";
            imcm = (imcx>imcy) ? imcx : imcy;
            if( mInpY==5 && imcz>imcm )
                imcm = imcz;
            while (1) {
                quest = "[" + S(n) + "] ";
                line = sask(quest.c_str(), "");
                if (line==string("")) break;
                if (!triv::str2vec(line, &linv, imcm+1)) {
                    printf(
                        " could not extract value no. %u"
                        " from string [%s]\n", imcm, line.c_str());
                    continue;
                }
                if (linv.size()<=imcm) {
                    cout << "-> not enough entries in line\n";
                    continue;
                }
                if( mInpZ==3 ){
                    if( n==0 ){
                        s->z.push_back( PObjDbl( new CObjDbl(linv[imcz]) ) );
                        zold = linv[imcz];
                    } else if( linv[imcz]!=zold ){
                        fout->V.push_back(s);
                        ns++;
                        s->z.clear();
                        s->z.push_back( PObjDbl( new CObjDbl(linv[imcz]) ) );
                        zold = linv[imcz];
                    }
                }
                s->push_xy(linv[imcx], linv[imcy]);
                n++;
            }
            break;
        }
        if (n<=0) return;

        switch(mInpY) {
        case 0:
            break;
        case 1:
            cout << "Enter " << n << " y values:\n";
            s->y.resize(n);
            for (int i=0; i<n; i++) {
                quest = "x[" + S(i) + "] = " +
                    S(s->x[i]) + " -> ";
                do {
                    line = sask(quest.c_str());
                } while (sscanf(line.c_str(), "%lg", &val)!=1);
                s->y[i] = val;
            }
            break;
        case 2:
            throw "case 2 broken, ygrid undefined";
            //ygrid.Ask("y", n);
            //ygrid.AsVec(&(S.y));
            break;
        case 3:
            cout << "Enter " << n << " lines with y values:\n";
            s->y.resize(n);
            for (int i=0; i<n; i++) {
                quest = "x[" + S(i) + "] =" +
                    S(s->x[i]) + " -> ";
                while (1) {
                    line = sask(quest.c_str());
                    if (!triv::str2vec(line, &linv, imcy+1)) {
                        printf(
                            " could not extract y value no. %u"
                            " from string [%s]\n", imcy, line.c_str());
                        continue;
                    }
                    if (linv.size()<=imcy) {
                        cout << "-> not enough entries"
                            " in line\n";
                        continue;
                    }
                    break;
                }
                s->y[i] = linv[imcy];
            }
            break;
        }
        fout->V.push_back(s);
        ns++;

        if (!mInpZ || mInpZ==3) goto store;
        if (imcy_delta) {
            imcy += imcy_delta;
            if ( ( imcy_delta>0 && imcy>imcy_last ) ||
                 ( imcy_delta<0 && imcy<imcy_last) ) goto store;
        }
    }

    // *** store new on-line file ***
store:
    // cout << "DEBUG: store\n";
    if (ns<=0) return;
    // cout << "DEBUG: sel\n";
    // cout << "DEBUG: add\n";
    NOlm::mem_store( fout );
    // cout << "DEBUG: main\n";
}


//! Create a new online file from table.

void NImport::read_tab( string qualif )
{
    // ** parse qualif **
    if( qualif.find_first_not_of("hvcsmd")!=string::npos )
        throw "ReadTab: invalid qualifier";
    char dir = qualif[0];
    if( !( dir=='h' || dir=='v' || dir=='c' ) )
        throw "ReadTab: missing qualifier h or v";
    bool horizontal = dir=='h';
    bool choosecol = dir=='c';
    static int iycol=0;
    bool fromscript = qualif.find('s')!=string::npos;
    bool multiblock = qualif.find('m')!=string::npos;
    bool with_d = qualif.find('d')!=string::npos;

    // ** input from script or from file **
    static string script;
    vector<string> inFiles;
    if( fromscript ){
        inFiles.push_back( "/ram/tab" );
        script = sask("Script (writing to "+inFiles[0]+")", script);
        triv::system( script );
    } else {
        string fnames = sask( "Read tab from file(s)" );
        triv::glob_file_list( fnames, "", &inFiles );
    }

    if( choosecol ){
        printf( "at present, x:=i\n" );
        iycol = iask( "Read y from column", iycol );
        if( iycol<0 ) return;
    }
    
    FILE *fd;
    string fdir, fshort, fext;
    for( int iF=0; iF<inFiles.size(); ++iF ) {
        if( !(fd = fopen(inFiles[iF].c_str(), "r")) )
            throw "cannot open file " + inFiles[iF];
        cout << ".. reading from " << inFiles[iF] << "\n";
        POld fout( new COld );
        if( fromscript ) {
            fout->lDoc.push_back( "ft"+qualif+" " + script );
            fout->name = script;
        } else {
            triv::fname_divide( inFiles[iF], &fdir, &fshort, &fext);
            fout->lDoc.push_back( "ft"+qualif+" " + inFiles[iF] );
            fout->name = fshort;
            if( choosecol ){
                fout->lDoc.push_back( "y from column " + S(iycol) );
                fout->name += "_" + S(iycol);
            }
        }

        // ** file-level settings **
        if( choosecol ){
            fout->xco = CCoord("i", "");
            fout->yco = CCoord("y"+S(iycol), "");
        } else {
            fout->xco = CCoord("x", "");
            fout->yco = CCoord("y", "");
        }

        // *** read input ***
        string lin, s1, s2;
        vector<double> dat, zdat;
        int n_in = -1; // line number
        int nblock = 0;
        int nline = -1; // line number within block (starting out of block)
        int nz = 0;
        int nzdat = 0;
        double val;
        PSpec s;
        while( triv::freadln(fd, &lin)>-1 ) {
            ++n_in;
            if( lin.substr(0,4)=="#rpa" ){
                triv::string_extract_word( lin.substr(4), &s1, &s2 );
                if( !triv::any2dbl( s2, &val ) )
                    printf( "invalid parameter line [%s]\n", lin.c_str() );
                else
                    fout->RPar.push_back( CParam( s2, "", val) );
                continue;
            }
            if( lin[0]=='#' )
                continue;
            
            if( nline==-1 ) { // start of block 
                if( !horizontal && nblock!=0 && s && s->size()>0 )
                    fout->V.push_back(s);
                if ( multiblock ) {
                    if( !triv::str2vec(lin, &zdat, 0, false) )
                        throw "invalid header line [" + lin +
                            "] (or legitimate break ?)";
                    if( nblock==0 ){ // first header
                        nzdat = zdat.size();
                        nz += nzdat;
                        for( int iz=0; iz<nzdat; ++iz )
                            fout->ZCo.push_back( CCoord("z"+S(iz), "") );
                    } else if( zdat.size() != nzdat )
                        throw "line " + S(n_in) +
                            ": header has length " + S(zdat.size()) +
                            " instead of " + S(nzdat);
                }
// reactivate this in future z-y mode
//                if ( nblock==0 && horizontal ) {
//                    nz += 1; 
//                    fout->ZCo.push_back( CCoord("line", "") );
//                }
                if( !horizontal ){
                    s = PSpec( new CSpec );
                    s->z.resize( nz );
                    for( int iz=0; iz<nzdat; ++iz )
                        s->z[iz] = PObjDbl( new CObjDbl( zdat[iz] ) );
                }
                nline = 0;
                ++nblock;
                if( multiblock ) // this was a header line
                    continue;
            }
            
            if( lin=="" && nline!=-1 ) { // end-of-block
                nline = -1;
                ++nblock;
                continue;
            }
            
            // regular data line
            if( !triv::str2vec(lin, &dat, 0, false) )
                throw "cannot parse line "+S(nline)+" in block "+
                    S(nblock)+" ["+lin+"]";
            if(dat.size()==0)
                throw "no data in line "+S(nline)+" in block "+
                    S(nblock)+" ["+lin+"]";
            
            if( horizontal ) { // new spectrum for every line 
                s = PSpec( new CSpec );
                s->z.resize( nz );
                for( int iz=0; iz<nzdat; ++iz )
                    s->z[iz] = PObjDbl( new CObjDbl( zdat[iz] ) );
// reactivate this in future z-y mode
//                s->z[nz-1] = nline;
                for( int i=0; i<dat.size(); ++i )
                    s->push_xy( (double)i, dat[i] );
                fout->V.push_back(s);
            } else { // vertical
                if( choosecol ){
                    if( iycol>=dat.size() )
                        throw "line "+S(n_in)+" (line "+S(nline)+
                            " in block "+S(nblock)+") ["+lin+
                            "] contains only "+S(dat.size())+" values; "+
                            "cannot read y from col "+S(iycol);
                    s->push_xy( (double)nline, dat[iycol] );
                } else {
                    if( dat.size()!=(with_d?3:2) ){
                        throw "line "+S(n_in)+" (line "+S(nline)+
                            " in block "+S(nblock)+") ["+ lin+
                            "] contains "+S(dat.size())+" values; "+
                            "at present, exactly "+S(with_d?3:2)+
                            " are required";
                    }
                    if (with_d)
                        s->push_xy( dat[0], dat[1] );
                    else
                        s->push_xyd( dat[0], dat[1], dat[2] );
                }
            }
            ++nline;
        } // end of file input loop
        
        if( !horizontal && s && s->size()>0 )
            fout->V.push_back(s);
        
        if( !(fout->nJ()) )
            throw "no input lines";
        if( fout->nJ()==1 ){
            fout->ZCo.clear();
            fout->VS(0)->z.clear();
        }
        NOlm::mem_store( fout );
    }
}


//! Create a new online file containing a regular x,z grid; let y=0.

void NImport::make_grid()
{
    POld fout( new COld );

    static int ni=1, nj=1;
    static string fnam;

    int niIn = iask("Number of points per spectrum", ni);
    if (niIn<1) return;
    ni = niIn;

    int njIn = iask("Number of spectra", nj);
    if (njIn<1) return;
    nj = njIn;

    fnam = "grid"+S(ni);
    if (nj>1) fnam += "x"+S(nj);

    fout->name = wask("Save as", fnam);
    fout->lDoc.push_back( "fm " + S(ni) + " " + S(nj) );

    // *** set coordinates ***
    fout->xco = CCoord("x", "");
    fout->yco = CCoord("y", "");
    if (nj>1)
        fout->ZCo.push_back(CCoord("z", ""));

    // *** loop over spectra ***
    for (int j=0; j<nj; ++j) {
        PSpec s( new CSpec );

        s->x.resize(ni);
        for( int i=0; i<ni; ++i )
            s->x[i] = ((double)i)/ni;
        s->y.clear();
        s->y.resize( ni, 0. );

        if (nj>1) s->z.push_back( PObjInt( new CObjInt(j) ) );

        fout->V.push_back(s);
    }

    NOlm::mem_store( fout );
}