/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

/********************************************************************************
 Important information:

 1. The Bottom/Top Levels will be computed automatically from the data, if
    they are not given as a input parameters.

 2. Relationship among fields and interpolation:

                  ML    ML&LNSP   PL    PL&LNSP
         INTY    ok      ok       ok     ok1
         INTN    ok     error     ok     ok1

   INTY/N = interpolation yes/no
   ok1    = LNSP is ignored

 3. If fieldset contains ML&LNSP, a conversion to PL will be computed automatically
 ********************************************************************************/

#include "CrossS.h"

// Application mode
enum { XWIND,XVELOCITY,XCONTOUR};

// Wind values
enum {PERPENDICULAR, PARALLEL, INTENSITY };


//Wind scaling type
// W_SCALE_PB is derived from a formula given by Peter Bechtold
enum {W_SCALE_PB=998, W_SCALE_AUTO_MV3=999};

CrossS::CrossS() :
                   Xsect("MXSECTION"),
                   foundUV_(false), foundW_(false),
                   hcNumber_(0), hcPerpendicular_(0), hcParallel_(0),
                   hcIntensity_(0), currentHC_(0),
                   inputMode_(XCONTOUR),
                   oroColour_("CYAN"),
                   wScaling_(W_SCALE_AUTO_MV3)
{
   type_ = "MXSECTION";
   view_ = "MXSECTIONVIEW";
}

CrossS::CrossS(const char*kw) :
                   Xsect(kw),
                   foundUV_(false), foundW_(false),
                   hcNumber_(0), hcPerpendicular_(0), hcParallel_(0),
                   hcIntensity_(0), currentHC_(0),
                   inputMode_(XCONTOUR),
                   oroColour_("CYAN"),
                   wScaling_(W_SCALE_AUTO_MV3)
{
   type_ = kw;
   view_ = "MXSECTIONVIEW";
}

bool CrossS::getCoordinates( MvRequest& in, ApplicationInfo& appInfo )
{
   // Get input values
   double Y1 = in("LINE",0);
   double X1 = in("LINE",1);
   double Y2 = in("LINE",2);
   double X2 = in("LINE",3);

   // Save coordinates
   appInfo.setAreaLine(X1,X2,Y1,Y2);

   return true;
}

bool CrossS::getAppParameters( MvRequest& in, ApplicationInfo& appInfo )
{
   // Retrieve geographical coordinates
   if ( !this->getCoordinates(in,appInfo) )
      return false;

   // Retrieve Wind input info
   hcNumber_ = 0;
   if( (const char*)in("WIND_PERPENDICULAR") &&
       strcmp(in("WIND_PERPENDICULAR"),"ON") == 0 )
   {
      hcNumber_++;
      hcPerpendicular_ = PERPENDICULAR;
   }

   if( (const char*)in("WIND_PARALLEL") &&
       strcmp(in("WIND_PARALLEL"),"ON") == 0 )
          hcParallel_ = PARALLEL;

   if( (const char*)in("WIND_INTENSITY") &&
       strcmp(in("WIND_INTENSITY"),"ON") == 0 )
   {
      hcNumber_++;
      hcIntensity_ = INTENSITY;
   }

   // Retrive wind & lnsp param numbers (mostly for non-ECMWF data)
   if( (const char*)in( "U_WIND_PARAM" ) )
      U_FIELD = (int)in( "U_WIND_PARAM" );

   if( (const char*)in( "V_WIND_PARAM" ) )
      V_FIELD = (int)in( "V_WIND_PARAM" );

   if( (const char*)in( "W_WIND_PARAM" ) )
      W_FIELD = (int)in( "W_WIND_PARAM" );

   if( (const char*)in( "LNSP_PARAM" ) )
      LNSP_FIELD = (int)in( "LNSP_PARAM" );

   if ( (const char*)in("HORIZONTAL_POINT_MODE") )
       appInfo.horPointMode( (const char*)in("HORIZONTAL_POINT_MODE") );

   // Retrieve vertical coordinates information
   if ( (const char*)in("VERTICAL_COORDINATES") &&
        (const char*)in("VERTICAL_COORDINATES") == string("USER") )
          appInfo.haveGHBC(true);

   if ( (const char*)in("VERTICAL_COORDINATE_PARAM") )
       appInfo.setGHBC( (int)in("VERTICAL_COORDINATE_PARAM") );

   // Retrieve W component scaling factor
   if( (const char*)in( "W_WIND_SCALING_FACTOR_MODE" ) &&
       strcmp(in("W_WIND_SCALING_FACTOR_MODE"),"AUTOMATIC") == 0 )
      wScaling_ = W_SCALE_AUTO_MV3;

   else if( (const char*)in( "W_WIND_SCALING_FACTOR_MODE" ) &&
            strcmp(in("W_WIND_SCALING_FACTOR_MODE"),"USER") == 0 )
           if( (const char*)in( "W_WIND_SCALING_FACTOR" ) )
              wScaling_ = (double)in( "W_WIND_SCALING_FACTOR" );



   // Retrieve parameters related to the vertical levels definition
   if( (const char*)in( "LEVEL_SELECTION_TYPE" ) )
      appInfo.levelSelectionType((const char*)in( "LEVEL_SELECTION_TYPE" ));

   if( (const char*)in( "LEVEL_LIST" ) )
   {
      double val;
      double factor = 100.; // input values are in hPa
      int cnt = in.iterInit("LEVEL_LIST");
      vector<double> lslist;
      lslist.reserve(cnt);
      for( int i = 0; i < cnt; ++i )
      {
         in.iterGetNextValue(val);
         lslist.push_back(val*factor);
      }
      
      // Make sure the values are in crescent order
      std::sort(lslist.begin(),lslist.end());

      appInfo.levelList(lslist);
   }

   if( (const char*)in( "LEVEL_COUNT" ) )
      appInfo.levelCount((int)in( "LEVEL_COUNT" ));

   if( (const char*)in( "VERTICAL_SCALING" ) )
   {
      string vs = (const char*)in( "VERTICAL_SCALING" );
      bool bvs = (vs == "LINEAR") ? true : false;
      appInfo.vLinearScaling(bvs);
   }

   if( (const char*)in( "BOTTOM_LEVEL" ) )
      appInfo.bottomLevel( (double)in( "BOTTOM_LEVEL" ) * 100. );   //hPa

   if( (const char*)in( "TOP_LEVEL" ) )
      appInfo.topLevel( (double)in( "TOP_LEVEL" ) * 100. );         //hPa

    // Retrieve orography colour
   if( (const char*)in( "OROGRAPHY_COLOUR" ) )
      oroColour_ = (const char*)in( "OROGRAPHY_COLOUR" );
   else if ( (const char*)in("_CONTEXT") )
   {
      MvRequest contReq = in("_CONTEXT");
      if ( (const char*)contReq("_ORIGINAL_REQUEST") )
      {
         MvRequest oriReq = contReq("_ORIGINAL_REQUEST");
         if ( (const char*)oriReq("OROGRAPHY_COLOUR") )
            oroColour_ = (const char*)oriReq("OROGRAPHY_COLOUR");
      }
   }

   return true;
}

bool CrossS::consistencyCheck( MvRequest& req1, MvRequest& req2)
{
  // Check coordinates
   if ( (double)req1("LINE",0) != (double)req2("LINE",0) ||
        (double)req1("LINE",1) != (double)req2("LINE",1) ||
        (double)req1("LINE",2) != (double)req2("LINE",2) ||
        (double)req1("LINE",3) != (double)req2("LINE",3)
      )
   {
      setError(1,"LINE coordinates are not consistent");
      return false;
   }

   // Check Wind parameters
   if ( (const char*)req1("WIND_PARALLEL")      != (const char*)req2("WIND_PARALLEL")      ||
        (const char*)req1("WIND_PERPENDICULAR") != (const char*)req2("WIND_PERPENDICULAR") ||
        (const char*)req1("WIND_INTENSITY")     != (const char*)req2("WIND_INTENSITY")
      )
   {
      setError(1,"WIND parameters are not consistencies");
      return false;
   }

   return true;
}

bool CrossS::generateData ( ApplicationInfo& appInfo, ParamMap& params,
                            MvNetCDF &cdf, MvField& field,
                            const string& key )
{
   // Get parameter info
   ParamInfo *par = appInfo.getParamInfo(params,key);
   if ( !par )
      return false;

   // Generate data
   if ( appInfo.isLNSP(par->Parameter()) )
      return generateLNSP(appInfo,cdf,par);
   else if ( appInfo.isGHBC(par->Parameter()) )
      return true;   // do we need to add it to the netCDF file????
   else
      return generateXsectData(appInfo,params,cdf,field,key);
}

// Generate data for Xsection. If some parameters are generated together, like in some
// cases U&V or u,v&w, just return if we have not got all the params. Otherwise,
// generate the data according to the values set.
bool CrossS::generateXsectData ( ApplicationInfo& appInfo, ParamMap &params,
                                 MvNetCDF &cdf, MvField& field,
                                 const string& key )
{
   // Get parameter info
   ParamInfo *par = appInfo.getParamInfo(params,key);
   if ( !par )
      return false;

   // Since the data are sorted by parameter, we wait until we get the
   // highest interesting parameter before doing the work.
   if ( ( foundUV_ && par->Parameter() == U_FIELD) ||
        (( foundW_ && (par->Parameter() == U_FIELD || par->Parameter() == V_FIELD) ) && !foundUV_) )
      return true;

   // Generate the netCDF parameter variable
   if ( foundUV_ && par->Parameter() == V_FIELD )
   {
      inputMode_ = XWIND;
      return windValues(appInfo,params,cdf,key,field);
   }
   else if ( foundW_ && par->Parameter() == W_FIELD )
   {
      // Special 2 cases because Magics does not interpolate winds:
      // ML to PL or PL
      if( appInfo.levelType() == XS_ML_LNSP || appInfo.levelType() == XS_PL )
         appInfo.setVerticalInterpolationFlag(true);

      inputMode_ = XVELOCITY;
      return velocityValues(appInfo,params,cdf,key,field);
   }
   else
   {
      inputMode_ = XCONTOUR;
      return contourValues(appInfo,params,cdf,field,key);
   }
}

// Generate data for plotting lnsp curve in xsection view
bool CrossS::generateLNSP(ApplicationInfo& appInfo, MvNetCDF &cdf, ParamInfo* par )
{
   // Get initial info
   double low   = appInfo.topLevel()/100.;
   double high  = appInfo.bottomLevel()/100.;
   int nrPoints = appInfo.NrPoints();

   // Add/update LNSP and auxiliary variables to the netCDF
   // These auxiliary variables will be used to plot the orography
   string varname = Xsect::getNetcdfVarname(par->ParamName());  // varname is LNSP
   MvNcVar *ncx1 = cdf.getVariable("orography_x_values");
   MvNcVar *ncy1 = cdf.getVariable("orography_y1_values");
   MvNcVar *ncy2 = cdf.getVariable("orography_y2_values");
   MvNcVar *ncln = cdf.getVariable(varname);
   if ( !ncx1 || !ncy1 || !ncy2 || !ncln )   // Create new variables
   {
      ntime_ = -1;

      // Initialise dimensions
      vector<long> values_ndim;
      values_ndim.push_back(appInfo.NTimes());
      values_ndim.push_back(nrPoints);
      vector<string> values_sdim;
      values_sdim.push_back(XS_VARTIME);
      values_sdim.push_back("npoints");

      // Create LNSP variable
      if ( !ncln )
      {
         ncln = cdf.addVariable(varname,ncDouble,values_ndim,values_sdim);
         ncln->addAttribute("long_name", par->ParamLongName().c_str());
         ncln->addAttribute("units", par->Units().c_str());
         ncln->addAttribute("_FillValue", XMISSING_VALUE);
      }

      // Create orography x values variable
      if ( !ncx1 )
      {
         ncx1 = cdf.addVariable("orography_x_values",ncFloat,values_ndim,values_sdim);
         ncx1->addAttribute("long_name", "orography_x_values");
         ncx1->addAttribute("units", " ");
         ncx1->addAttribute("_FillValue", (float)XMISSING_VALUE);
      }

      // Create orography y1 values variable
      if ( !ncy1 )
      {
         ncy1 = cdf.addVariable("orography_y1_values",ncDouble,values_ndim, values_sdim);
         ncy1->addAttribute("long_name", "orography_y1_values");
         ncy1->addAttribute("units", " ");
         ncy1->addAttribute("_FillValue", XMISSING_VALUE);
      }

      // Create orography y2 values variable
      if ( !ncy2 )
      {
         ncy2 = cdf.addVariable("orography_y2_values",ncDouble,values_ndim, values_sdim);
         ncy2->addAttribute("long_name", "orography_y2_values");
         ncy2->addAttribute("units", " ");
         ncy2->addAttribute("_FillValue", XMISSING_VALUE);
      }
   }

   // Get LNSP values
   double *splin = par->getOneLevelValues("1");
   if ( !splin )
   {
      setError(1,"ERROR: Could not find LNSP Level values");
      return false;
   }

   // Memory allocation
   double *y1_values = new double[nrPoints];
   double *y2_values = new double[nrPoints];

   // Get X values
   const vector<double>& lon = (appInfo.haveLonsForPlotting()) ? appInfo.getLongitudeForPlotting() : appInfo.getLongitude();
   const vector<double>& lat = (appInfo.haveLatsForPlotting()) ? appInfo.getLatitudeForPlotting()  : appInfo.getLatitude();
   const vector<double>& x_values = ( lon[0] == lon[1] ) ? lat : lon;

   // Compute Y values
   for ( int i = 0; i < nrPoints; i++ )
   {
      if ( splin[i] >= XMISSING_VALUE )
         y1_values[i] = high;
      else
      {
         double w = exp(splin[i]);
         w /= 100;
         w = MAX(low,w);
         w = MIN(high,w);
         y1_values[i] = w;
      }
      y2_values[i] = high;
   }

   // Write values to the netCDF
   ntime_++;
   ncln->setCurrent(ntime_);
   ncln->put(splin,1,nrPoints);
   ncx1->setCurrent(ntime_);
   ncx1->put(x_values,1,nrPoints);
   ncy1->setCurrent(ntime_);
   ncy1->put(y1_values,1,nrPoints);
   ncy2->setCurrent(ntime_);
   ncy2->put(y2_values,1,nrPoints);

   // Release memory
   delete [] y1_values;
   delete [] y2_values;

   return true;
}

// Generate values for contouring u&v
bool CrossS::windValues( ApplicationInfo& appInfo, ParamMap &params, MvNetCDF &cdf,
                         const string& key, MvField& field )
{
   int i,j;

   // Find the v field
   ParamInfo* par_v = appInfo.getParamInfo( params,key );
   if ( !par_v )
      return false;

   // The paraminfo contains the v field, now find the u field,
   // substitute u for v in the key and find info in list.
   ParamInfo *par_u = appInfo.getParamInfo( params,U_FIELD,key );
   if ( !par_u )
      return false;

   // Temporary used for holding mapped u/v values.
   ParamInfo par_uv( par_v->Parameter(), par_v->Date(), par_v->Time(), par_v->Step(), par_v->ParamName(), par_v->ParamLongName(), par_v->Units(), par_v->LevelType() );

   // Allocate memory
   int nrLevels = appInfo.outputLevels();
   int nrPoints = appInfo.NrPoints();
   vector<double> cp;
   cp.reserve(nrLevels*nrPoints);
//   =  new double* [ nrLevels ];
//   for (i = 0; i < nrLevels; i++)
//      cp[i] = new double [ nrPoints ];

   // Main loop
   int hc_perp = hcPerpendicular_;
   int hc_int  = hcIntensity_;
   double* splin;
   for (i = 0; i < hcNumber_; i++ )
   {
      // Get/create the netCDF variable
      string varname = this->getNetcdfVarname(par_v->ParamName()) + "_";
      varname += i;
      MvNcVar *ncv = cdf.getVariable(varname);
      if ( !ncv )   // Create new variable
      {
         if ( i == 0 )   // only initialize/update once
            ntime_ = -1;

         // Initialise dimensions
         vector<long> values_ndim;
         values_ndim.push_back(appInfo.NTimes());
         values_ndim.push_back(nrLevels);
         values_ndim.push_back(nrPoints);
         vector<string> values_sdim;
         values_sdim.push_back(XS_VARTIME);
         values_sdim.push_back(XS_VARLEVEL);
         values_sdim.push_back(XS_VARLON);

         // Create variable
         ncv = cdf.addVariable(varname,ncDouble,values_ndim, values_sdim);
         ncv->addAttribute("long_name", par_v->ParamLongName().c_str());
         ncv->addAttribute("units", par_v->Units().c_str());
         ncv->addAttribute("_FillValue", XMISSING_VALUE);
         ncv->addAttribute("title",titleVariable(appInfo,par_v).c_str());
      }

      // Compute values
      if ( hc_perp )
      {
         currentHC_ = hc_perp; hc_perp = 0;
      }
      else if ( hc_int )
      {
         currentHC_ = hc_int; hc_int = 0;
      }

      // Map U/V into planes
      this->mapValues(appInfo,par_uv,par_u,par_v);

      vector<double> y_values;
      if( appInfo.levelType() != XS_ML_LNSP )
      {
         // Compute level values and interpolate matrix
         appInfo.computeLevelInfo(&par_uv,y_values);
         appInfo.InterpolateVerticala(cp,&par_uv,y_values);
      }
      else
      {
         // Get LNSP only once
         if ( i == 0 )
         {
            ParamInfo* parLnsp = appInfo.getParamInfo( params,appInfo.LNSP(),key );
            if ( ! parLnsp )  //LNSP not found
            {
               setError(1,"ERROR: Could not find parameter LNSP");
               return false;
            }

            splin = parLnsp->getOneLevelValues("1");
            if ( !splin )  //LNSP not found
            {
               setError(1,"ERROR: Could not find LNSP Level values");
               return false;
            }
         }

         // Compute level values and interpolate matrix
         appInfo.computeLevelInfo(&par_uv,y_values,splin,field);
         appInfo.InterpolateVerticalb(field,cp,&par_uv,splin,y_values);
      }

      // Add values to the netCDF
      if ( i == 0 )  // it should update only once
         ntime_++;

      for ( j = 0; j < nrLevels; j++ )
      {
         ncv->setCurrent(ntime_,j);
         ncv->put(&cp[j*nrPoints],1,1,(long)nrPoints);
      }
   }

   return true;
}

// Generate wind data for U/V/W
bool CrossS::velocityValues( ApplicationInfo& appInfo, ParamMap &params,
                             MvNetCDF &cdf, const string& key, MvField& field )
{
   // Find the w field
   ParamInfo* par_w = appInfo.getParamInfo( params,key );
   if ( !par_w )
      return false;

   // Use the W field key to find U/V info in the list
   ParamInfo* par_u = appInfo.getParamInfo( params,U_FIELD,key );
   if ( !par_u )
      return false;

   ParamInfo* par_v = appInfo.getParamInfo( params,V_FIELD,key );
   if ( !par_v )
      return false;

   // Scale velocity
   currentHC_ = hcParallel_;
   if ( wScaling_ == W_SCALE_AUTO_MV3 )
      appInfo.scaleVelocity(par_w);
   else if ( wScaling_ == W_SCALE_PB )
      appInfo.scaleVelocityPB(par_w);
   else
      appInfo.scaleVelocity1(par_w,wScaling_);

   // Create a structure for the x_component
   ParamInfo par_uv( par_v->Parameter(), par_v->Date(), par_v->Time(), par_v->Step(), par_v->ParamName(), par_v->ParamLongName(), par_v->Units(), par_v->LevelType() );

   // Map U/V into planes
   this->mapValues(appInfo,par_uv,par_u,par_v);

   // Compute UV values (x_component).
   vector<double> cp;
   vector<double> y_values;
   double* splin = 0;
   if( !appInfo.isInterpolate() )
   {
      // Compute level values and interpolate matrix
      appInfo.computeLevelInfo(&par_uv,y_values);
      appInfo.InterpolateVerticala(cp,&par_uv,y_values);
   }
   else
   {
      // It will always go to interpolate on the vertical because Magics
      // can not interpolate wind arrows at the moment
      if( appInfo.levelType() == XS_ML_LNSP )
      {
         ParamInfo* parLnsp = appInfo.getParamInfo( params,appInfo.LNSP(),key );
         if ( ! parLnsp )  // LNSP not found
         {
            setError(1,"ERROR: Could not find parameter LNSP");
            return false;
         }

         splin = parLnsp->getOneLevelValues("1");
         if ( !splin )  // LNSP values not found
         {
            setError(1,"ERROR: Could not find LNSP Level values");
            return false;
         }

         // Compute level values and interpolate matrix
         appInfo.computeLevelInfoUI(&par_uv,y_values,splin,field);
         appInfo.InterpolateVerticalb(field,cp,&par_uv,splin,y_values);
      }
      else
      {
         // Compute level values and interpolate matrix
         appInfo.computeLevelInfoUI(&par_uv,y_values,splin,field);
         appInfo.InterpolateVerticala(cp,&par_uv,y_values);
      }
   }

   // Get/create the netCDF variable (x_component)
   string varname = this->getNetcdfXComponentVarname();
   MvNcVar *ncvx = this->getNetcdfVariable(cdf,varname,appInfo,varname,par_u);

   // Write UV values to the netCDF (x_component)
   int nrLevels = appInfo.outputLevels();
   int nrPoints = appInfo.NrPoints();
   ntime_ = 0;
   int i;
   for ( i = 0; i < nrLevels; i++)
   {
      ncvx->setCurrent(ntime_,i);
      ncvx->put(&cp[i*nrPoints],1,1,(long)nrPoints);
   }

   // Compute Velocity values (y_component)
   if( appInfo.levelType() != XS_ML_LNSP )
   {
      // Compute level values and interpolate matrix
      // Assume that the level values are the same as of x_component
      //  appInfo.computeLevelInfo(par_w,y_values);
      appInfo.InterpolateVerticala(cp,par_w,y_values);
   }
   else
   {
      // Compute level values and interpolate matrix
      // appInfo.computeLevelInfo(par_w,y_values,splin,field);
      appInfo.InterpolateVerticalb(field,cp,par_w,splin,y_values);
   }

   // Get/create the netCDF variable (y_component)
   varname = this->getNetcdfYComponentVarname();
   MvNcVar* ncvy = this->getNetcdfVariable(cdf,varname,appInfo,varname,par_w);

   // Write Velocity values to the netCDF (y_component)
   for ( i = 0; i < nrLevels; i++ )
   {
      ncvy->setCurrent(ntime_,i);
      ncvy->put(&cp[i*nrPoints],1,1,(long)nrPoints);
   }

   // Compute level values
   // Assume that the values are the same for the x and y component
   if ( appInfo.isInterpolate() )
      for ( i = 0; i < nrLevels ;i++ )
         y_values[i] /= 100.;

   // Get/create the netCDF variable (levels)
   varname = "lev";
   MvNcVar* ncvl = this->getNetcdfVariableLevel(cdf,varname,appInfo);

   // Write level values to the netCDF
   ncvl->setCurrent(ntime_);
   ncvl->put(y_values,1,(long)y_values.size());

   return true;
}

void CrossS::setHorComp( int no, int perp, int par, int intens )
{
   hcNumber_ = no; hcPerpendicular_ = perp; hcParallel_ = par;
   hcIntensity_ = intens;
}

void CrossS::setHorCompFlags( bool foundU, bool foundV, bool foundW )
{
   foundUV_ = foundU && foundV && hcNumber_;
   foundW_  = foundU && foundV && foundW && hcParallel_;
}

void CrossS::mapValues( ApplicationInfo& appInfo, ParamInfo &par_uv, ParamInfo *par_u, ParamInfo *par_v)
{
   // Map U/V into planes
   LevelMap  lmap_u = par_u->Levels();
   LevelMap  lmap_v = par_v->Levels();
   LevelIterator uu = lmap_u.begin(), vv = lmap_v.begin();
   double *uValues,*vValues;

   int nrPoints = appInfo.NrPoints();
   double *valuesuv = new double[nrPoints];
   double ansin = appInfo.Ansin();
   double ancos = appInfo.Ancos();

   for (; uu != lmap_u.end(), vv != lmap_v.end();uu++,vv++)
   {
      uValues = (*uu).second->XValues();
      vValues = (*vv).second->XValues();

      for ( int i=0; i < nrPoints; i++)
      {
         if (uValues[i] < XMISSING_VALUE && vValues[i] < XMISSING_VALUE)
         {
            if (currentHC_ == PERPENDICULAR )
               valuesuv[i] = -uValues[i]*ansin + vValues[i] * ancos;
            else if (currentHC_ == PARALLEL )
               valuesuv[i] = uValues[i]*ancos + vValues[i] * ansin;
            else
               valuesuv[i] =  sqrt (uValues[i] * uValues[i] + vValues[i] * vValues[i]);
         }
         else
            valuesuv[i] = XMISSING_VALUE;
      }

      bool modlev = appInfo.levelTypeFlag();
      par_uv.Level(valuesuv,(*uu).first,nrPoints,modlev);
   }

   delete [] valuesuv;
   return;
}

int CrossS::computeGeographicalPoints( ApplicationInfo& appInfo, MvField* field )
{
   // Compute distance
   double x1,x2,y1,y2;
   appInfo.getAreaLine(x1,x2,y1,y2);
   double dellat = ABS(y2-y1), dellon = ABS(x2-x1);

   // Compute initial number of points
   int np = int( sqrt( dellon*dellon + dellat*dellat ) / MAX(appInfo.GridNS(),appInfo.GridEW()) );
   np = MAX(np,64);

   // Compute the coordinates
   appInfo.computeLine(np,field);

   return np;
}

bool CrossS::fillValues( ApplicationInfo& appInfo, MvField &field, double *vals )
{
   int i;
   MvFieldExpander x(field);

   // Get number of points and lat/long interpolation points
   int npoint = appInfo.NrPoints();
   const vector<double>& lat = appInfo.getLatitude();
   const vector<double>& lon = appInfo.getLongitude();

   if ( appInfo.isHorInterpolate() )
   {
      // Interpolatate along XS line
      for ( i = 0; i < npoint; i++)
      {
         double val = field.interpolateAt(lon[i],lat[i]);
         vals[i] = ( val >= DBL_MAX ) ? XMISSING_VALUE : val;
      }
   }
   else
   {
      for ( i = 0; i < npoint; i++)
      {
         double val = field.nearestGridpoint(lon[i],lat[i]);
         vals[i] = ( val >= DBL_MAX ) ? XMISSING_VALUE : val;
      }
   }

   return true;
}

MvRequest CrossS::createOutputRequest( ApplicationInfo& appInfo, ParamInfo* parInfo )
{
   // Create netCDF data request
   MvRequest xs("NETCDF");
   xs("PATH") = ncName_.c_str();
   xs("TEMPORARY") = 1;

   // Create NetCDF output request
   string varname = getNetcdfVarname(parInfo->ParamName()).c_str();
   MvRequest out1("NETCDF_XY_MATRIX");
   out1("NETCDF_DATA") = xs;
   out1("NETCDF_MISSING_ATTRIBUTE") = "_FillValue";
   out1("NETCDF_VALUE_VARIABLE") = varname.c_str();
   out1("NETCDF_Y_VARIABLE") = getNetcdfLevelVarname(varname).c_str();
   double X1,X2,Y1,Y2;
   appInfo.getAreaLine(X1,X2,Y1,Y2);

   
  // across the pole?   
   if (X1 == X2)
   {
      const string latsVar =  (appInfo.haveLatsForPlotting()) ? XS_VARLATPLOT : XS_VARLAT;
      const string lonsVar =  (appInfo.haveLonsForPlotting()) ? XS_VARLONPLOT : XS_VARLON;
      out1("NETCDF_X_VARIABLE")           = latsVar.c_str();
      out1("NETCDF_X_AUXILIARY_VARIABLE") = lonsVar.c_str();
      out1("NETCDF_X_GEOLINE_CONVENTION") = "latlon";
   }
   else
   {
      out1("NETCDF_X_VARIABLE")           = "lon";
      out1("NETCDF_X_AUXILIARY_VARIABLE") = "lat";
      out1("NETCDF_X_GEOLINE_CONVENTION") = "lonlat";
   }

   // Add X/Y Component, if this is the case
   if ( inputMode_ == XVELOCITY )
   {
      out1("NETCDF_X_COMPONENT_VARIABLE") = (const char*)this->getNetcdfXComponentVarname().c_str();
      out1("NETCDF_Y_COMPONENT_VARIABLE") = (const char*)this->getNetcdfYComponentVarname().c_str();

      // Add flag to indicate that a MWIND visdef should be applied to this data
      out1("_VISDEF") = "MWIND";
   }

   // Add information to help the Macro Converter to translate
   // the output request to a Macro code
   out1("_ORIGINAL_REQUEST") = buildMacroConverterRequest();

   // Add the orography curve visualiser
   if ( appInfo.haveLNSP() && (appInfo.levelType() == XS_ML_LNSP || appInfo.levelType() == XS_PL) )
   {
      MvRequest out_orog("NETCDF_XY_POINTS");
      out_orog("NETCDF_DATA") = xs;
      out_orog("NETCDF_Y_VARIABLE")  = "orography_y2_values";
      out_orog("NETCDF_Y2_VARIABLE") = "orography_y1_values";
      out_orog("NETCDF_X_VARIABLE")  = "orography_x_values";
      out_orog("NETCDF_X2_VARIABLE") = "orography_x_values";//"orography_x2_values";
      out_orog("NETCDF_MISSING_ATTRIBUTE") = "_FillValue";
      out_orog("_SKIP_MACRO") = 1;  // tell MacroConverter to ignore this request

      // create Graph Plotting subrequest
      // OBS: itt should use a definition from the Default folder
      MvRequest visdefReq("MGRAPH");
      visdefReq("GRAPH_TYPE") = "AREA";
      visdefReq("GRAPH_SHADE_COLOUR") = oroColour_.c_str();
      visdefReq("_CLASS") = "MGRAPH";
      out_orog = out_orog + visdefReq;

      // Create an empty request.
      // This is very important because it indicates to uPlot that this
      // sequence of requests must be processed independently. For example,
      // if in uPlot there is a visdef request after this sequence of requests
      // then this visdef will be processed separately, e.g. it will not be
      // added together with the visdef MGRAPH request
      MvRequest empty("EMPTY");

      out1 = out1 + out_orog + empty;
   }

   // Create View request using some parameters from the xsection data request
   MvRequest viewReq("MXSECTIONVIEW");
   if ( (const char*)origReq_("LINE") )
   {
      viewReq.addValue("LINE",(double)origReq_("LINE",0));
      viewReq.addValue("LINE",(double)origReq_("LINE",1));
      viewReq.addValue("LINE",(double)origReq_("LINE",2));
      viewReq.addValue("LINE",(double)origReq_("LINE",3));
   }
   if ( (const char*)origReq_("WIND_PARALLEL") )
      viewReq("WIND_PARALLEL") = origReq_("WIND_PARALLEL");
   if ( (const char*)origReq_("WIND_PERPENDICULAR") )
      viewReq("WIND_PERPENDICULAR") = origReq_("WIND_PERPENDICULAR");
   if ( (const char*)origReq_("WIND_INTENSITY") )
      viewReq("WIND_INTENSITY") = origReq_("WIND_INTENSITY");

   viewReq("_CLASS") = "MXSECTIONVIEW";
   viewReq("_DEFAULT") = true;
   viewReq("_DATAATTACHED") = "YES";
   if ( appInfo.levelType() != cML_UKMO_ND &&
        appInfo.levelType() != XS_ML_GHBC )  // UK MetOffice model levels and GHBC fields are 'upside-down'
      viewReq("_Y_AUTOMATIC_REVERSE") = "on";

   if ( !appInfo.vLinearScaling() )
      viewReq("VERTICAL_SCALING")= "log";

   // If action is not visualisation related then return the netcdf data.
   // Also, add the visualisation and original requests as hidden parameters.
   // These may be used later to help the visualisation of the netcdf data.
   if ( !isVisualise( appInfo ) )
   {
      xs("_VIEW") = view_.c_str();
      xs("_VISUALISE") = out1;
      xs("_VIEW_REQUEST") = viewReq;
      return xs;
   }

   // Final output request
   MvRequest out = viewReq + out1;
   return out;
}

string CrossS::titleVariable( ApplicationInfo&, ParamInfo* par )
{
   char titlestr[150];

   // Generate title
   if ( inputMode_ == XWIND )
      sprintf(titlestr,"Cross section of wind %s %d %02d step %d %s",
              (currentHC_ == hcPerpendicular_ ) ? "perp." : "intens.",
              par->Date(),par->Time(),par->Step(),
              par->ExpVerTitle().c_str() );
   else if ( inputMode_ == XVELOCITY )
      sprintf(titlestr,"Cross section of UVW parallel %d %02d step %d %s",
              par->Date(),par->Time(),par->Step(),
              par->ExpVerTitle().c_str() );
   else
      sprintf(titlestr,"Cross section of %s %d %02d step %d %s",
              par->ParamLongName().c_str(), par->Date(),par->Time(),par->Step(),
              par->ExpVerTitle().c_str() );

   return string(titlestr);
}

string CrossS::getNetcdfLevelVarname( string& varname )
{
   if ( inputMode_ == XVELOCITY )
      return string("lev");

   return Xsect::getNetcdfLevelVarname(varname);
}

string CrossS::getNetcdfVarname( const string& varname)
{
   if ( inputMode_ == XVELOCITY )
      return getNetcdfXComponentVarname();

   return Xsect::getNetcdfVarname(varname);
}

//------------------------------------------------------------------------------

// All these methods are created to allow backwards compatibility with Metvew 3.
// They should be removed when the backwards compatibility is no longer needed.

// Translate Metview 3 Cross Section Data to Metview 4 definition. Call Metview 4
// server to process the job.
void CrossSM3::serve(MvRequest& in,MvRequest& out)
{
   // Send a general warning message
   setError(0, "Icon Metview 3 CROSS SECTION DATA is obsolete. An automatic translation to the equivalent icon in Metview 4 will be performed, but may not work for all cases. It is recommended to replace it to the new definition manually.");

   // There are input parameters that are no longer available in Metview 4.
   // Remove them and send a warning message.
   setError(0,"Icon Metview 3 CROSS SECTION DATA is obsolete. Parameters PRESSURE_LEVEL_AXIS, INTERPOLATE_VALUES, BOTTOM_PRESSURE and TOP_PRESSURE will not be automatically translated.");
   MvRequest req = in;
   req.unsetParam("PRESSURE_LEVEL_AXIS");
   req.unsetParam("INTERPOLATE_VALUES");
   req.unsetParam("BOTTOM_PRESSURE");
   req.unsetParam("TOP_PRESSURE");

   // Keep the remaining parameters and update the VERB
   req.setVerb("MXSECTION");

   // Call the Xsection server to process the job
   Xsect::serve(req,out);
}
