QGIS/src/plugins/grass/qgsgrassmoduleoptions.cpp
Nyall Dawson 20c07a58e6 Remove support for disabling OTF reprojection
Because
- OTF reprojection is mature and stable
- Should be no cost when not required - transforms are shortcut
when not required
- Reduces code complexity
- Canvas OTF support was being incorrectly used as a flag for
whether measurements should be made in ellipsoidal mode. Instead
the project's ellipsoid setting should be checked for this.
- Should simplify behavior for new users
2017-03-01 07:29:35 +10:00

998 lines
29 KiB
C++

/***************************************************************************
qgsgrassmoduleoptions.cpp
-------------------
begin : March, 2005
copyright : (C) 2005 by Radim Blazek
email : radim.blazek@gmail.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <QDomElement>
#include <QFileDialog>
#include <QMessageBox>
#include <QScrollArea>
#include <QTextCodec>
#include "qgisinterface.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsmaplayer.h"
#include "qgsproject.h"
#include "qgsrasterlayer.h"
#include "qgsvectorlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsgrass.h"
#include "qgsgrassmodule.h"
#include "qgsgrassmoduleinput.h"
#include "qgsgrassmoduleoptions.h"
#include "qgsgrassmoduleparam.h"
#include "qgsgrassplugin.h"
extern "C"
{
#if GRASS_VERSION_MAJOR < 7
#else
#define G_adjust_Cell_head(cellhd,row_flag,col_flag) (G_adjust_Cell_head(cellhd,row_flag,col_flag),0)
#endif
}
/******************* QgsGrassModuleOptions *******************/
QgsGrassModuleOptions::QgsGrassModuleOptions(
QgsGrassTools *tools, QgsGrassModule *module,
QgisInterface *iface, bool direct )
: mIface( iface )
, mTools( tools )
, mModule( module )
, mRegionModeComboBox( 0 )
, mDirect( direct )
{
QgsDebugMsg( "called." );
mCanvas = mIface->mapCanvas();
}
QgsGrassModuleOptions::~QgsGrassModuleOptions()
{
}
QStringList QgsGrassModuleOptions::arguments()
{
return QStringList();
}
/*************** QgsGrassModuleStandardOptions ***********************/
QgsGrassModuleStandardOptions::QgsGrassModuleStandardOptions(
QgsGrassTools *tools, QgsGrassModule *module,
QgisInterface *iface,
QString xname, QDomElement confDocElem,
bool direct, QWidget * parent, Qt::WindowFlags f )
: QWidget( parent, f )
, QgsGrassModuleOptions( tools, module, iface, direct )
, mXName( xname )
{
//QgsDebugMsg( "called." );
QgsDebugMsg( QString( "PATH = %1" ).arg( getenv( "PATH" ) ) );
//
//Set up dynamic inside a scroll box
//
QVBoxLayout * mypOuterLayout = new QVBoxLayout( this );
mypOuterLayout->setContentsMargins( 0, 0, 0, 0 );
QScrollArea * mypScrollArea = new QScrollArea();
//transfers scroll area ownership so no need to call delete
mypOuterLayout->addWidget( mypScrollArea );
QFrame * mypInnerFrame = new QFrame();
mypInnerFrame->setFrameShape( QFrame::NoFrame );
mypInnerFrame->setFrameShadow( QFrame::Plain );
//transfers frame ownership so no need to call delete
mypScrollArea->setWidget( mypInnerFrame );
mypScrollArea->setWidgetResizable( true );
QVBoxLayout *mypInnerFrameLayout = new QVBoxLayout( mypInnerFrame );
QFrame * mypRegionModeFrame = new QFrame();
QHBoxLayout * mypRegionModeFrameLayout = new QHBoxLayout( mypRegionModeFrame );
QLabel * mypRegionModeLabel = new QLabel( tr( "Region" ) );
mRegionModeComboBox = new QComboBox();
mRegionModeComboBox->addItem( tr( "Input layers" ), RegionInput );
mRegionModeComboBox->addItem( tr( "Current map canvas" ), RegionCurrent );
mRegionModeComboBox->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ) );
mypRegionModeFrameLayout->addWidget( mypRegionModeLabel );
mypRegionModeFrameLayout->addWidget( mRegionModeComboBox );
// Add frames for simple/advanced options
QFrame * mypSimpleFrame = new QFrame();
mypSimpleFrame->setFrameShape( QFrame::NoFrame );
mypSimpleFrame->setFrameShadow( QFrame::Plain );
mAdvancedFrame.setFrameShape( QFrame::NoFrame );
mXName = xname;
mAdvancedFrame.setFrameShadow( QFrame::Plain );
QFrame * mypAdvancedPushButtonFrame = new QFrame();
QHBoxLayout *mypAdvancedPushButtonFrameLayout = new QHBoxLayout( mypAdvancedPushButtonFrame );
connect( &mAdvancedPushButton, SIGNAL( clicked() ), this, SLOT( switchAdvanced() ) );
mypAdvancedPushButtonFrameLayout->addWidget( &mAdvancedPushButton );
mypAdvancedPushButtonFrameLayout->addStretch( 1 );
if ( mDirect )
{
mypInnerFrameLayout->addWidget( mypRegionModeFrame );
}
mypInnerFrameLayout->addWidget( mypSimpleFrame );
mypInnerFrameLayout->addWidget( mypAdvancedPushButtonFrame );
mypInnerFrameLayout->addWidget( &mAdvancedFrame );
mypInnerFrameLayout->addStretch( 1 );
// Hide advanced and set button next
switchAdvanced();
QVBoxLayout *mypSimpleLayout = new QVBoxLayout( mypSimpleFrame );
QVBoxLayout *mypAdvancedLayout = new QVBoxLayout( &mAdvancedFrame );
QVBoxLayout *layout = 0;
QDomDocument gDomDocument = readInterfaceDescription( mXName, mErrors );
if ( !mErrors.isEmpty() )
{
return;
}
QDomElement descDocElem = gDomDocument.documentElement();
// Read QGIS options and create controls
QDomNode confDomNode = confDocElem.firstChild();
while ( !confDomNode.isNull() )
{
QDomElement confDomElement = confDomNode.toElement();
if ( !confDomElement.isNull() )
{
// Check GRASS version
QStringList errors;
if ( !QgsGrassModuleOption::checkVersion( confDomElement.attribute( QStringLiteral( "version_min" ) ), confDomElement.attribute( QStringLiteral( "version_max" ) ), errors ) )
{
mErrors << errors; // checkVersion returns falso also if parsing fails
confDomNode = confDomNode.nextSibling();
continue;
}
QString optionType = confDomElement.tagName();
QgsDebugMsg( "optionType = " + optionType );
if ( confDomElement.attribute( QStringLiteral( "advanced" ), QStringLiteral( "no" ) ) == QLatin1String( "yes" ) )
{
layout = mypAdvancedLayout;
}
else
{
layout = mypSimpleLayout;
}
QString key = confDomElement.attribute( QStringLiteral( "key" ) );
QgsDebugMsg( "key = " + key );
QDomNode gnode = QgsGrassModuleParam::nodeByKey( descDocElem, key );
if ( gnode.isNull() )
{
mErrors << tr( "Cannot find key %1" ).arg( key );
confDomNode = confDomNode.nextSibling();
continue;
}
if ( optionType == QLatin1String( "option" ) )
{
bool created = false;
// Check option type and create appropriate control
QDomNode promptNode = gnode.namedItem( QStringLiteral( "gisprompt" ) );
QDomElement promptElem = promptNode.toElement();
if ( !promptElem.isNull() )
{
QString element = promptElem.attribute( QStringLiteral( "element" ) );
QString age = promptElem.attribute( QStringLiteral( "age" ) );
//QgsDebugMsg("element = " + element + " age = " + age);
if ( age == QLatin1String( "old" ) && ( element == QLatin1String( "vector" ) || element == QLatin1String( "cell" ) ||
element == QLatin1String( "strds" ) || element == QLatin1String( "stvds" ) ||
element == QLatin1String( "str3ds" ) || element == QLatin1String( "stds" ) )
&& confDomElement.attribute( QStringLiteral( "widget" ) ) != QLatin1String( "text" ) )
{
QgsGrassModuleInput *mi = new QgsGrassModuleInput(
mModule, this, key, confDomElement, descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
created = true;
mParams.append( mi );
}
}
if ( !created )
{
QgsGrassModuleOption *so = new QgsGrassModuleOption(
mModule, key, confDomElement, descDocElem, gnode, mDirect, this );
layout->addWidget( so );
mParams.append( so );
if ( promptElem.attribute( QStringLiteral( "prompt" ) ) == QLatin1String( "dbcolumn" ) )
{
// Give only warning if the option is not hidden
if ( !so->hidden() )
{
// G_OPT_DB_COLUMN may be also used for new columns (v.in.db) so we check also if there is at least one input vector
// but a vector input may also exist (v.random).
QList<QDomNode> vectorNodes = QgsGrassModuleParam::nodesByType( descDocElem, G_OPT_V_INPUT, QStringLiteral( "old" ) );
QgsDebugMsg( QString( "vectorNodes.size() = %1" ).arg( vectorNodes.size() ) );
if ( !vectorNodes.isEmpty() )
{
mErrors << tr( "Option '%1' should be configured as field" ).arg( so->key() );
}
}
}
}
}
else if ( optionType == QLatin1String( "ogr" ) )
{
QgsGrassModuleGdalInput *mi = new QgsGrassModuleGdalInput(
mModule, QgsGrassModuleGdalInput::Ogr, key, confDomElement,
descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
mParams.append( mi );
}
else if ( optionType == QLatin1String( "gdal" ) )
{
QgsGrassModuleGdalInput *mi = new QgsGrassModuleGdalInput(
mModule, QgsGrassModuleGdalInput::Gdal, key, confDomElement,
descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
mParams.append( mi );
}
else if ( optionType == QLatin1String( "field" ) )
{
if ( confDomElement.hasAttribute( QStringLiteral( "layer" ) ) )
{
QgsGrassModuleVectorField *mi = new QgsGrassModuleVectorField(
mModule, this, key, confDomElement,
descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
mParams.append( mi );
}
else
{
QgsGrassModuleField *mi = new QgsGrassModuleField(
mModule, key, confDomElement,
descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
mParams.append( mi );
}
}
else if ( optionType == QLatin1String( "selection" ) )
{
QgsGrassModuleSelection *mi = new QgsGrassModuleSelection(
mModule, this, key, confDomElement,
descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
mParams.append( mi );
}
else if ( optionType == QLatin1String( "file" ) )
{
QgsGrassModuleFile *mi = new QgsGrassModuleFile(
mModule, key, confDomElement, descDocElem, gnode, mDirect, this );
layout->addWidget( mi );
mParams.append( mi );
}
else if ( optionType == QLatin1String( "flag" ) )
{
QgsGrassModuleFlag *flag = new QgsGrassModuleFlag(
mModule, key, confDomElement, descDocElem, gnode, mDirect, this );
layout->addWidget( flag );
mParams.append( flag );
}
}
confDomNode = confDomNode.nextSibling();
}
if ( mParams.size() == 0 )
{
QLabel *label = new QLabel( this );
label->setText( tr( "This module has no options" ) );
mypSimpleLayout->addWidget( label );
}
if ( mypAdvancedLayout->count() == 0 )
{
mypAdvancedPushButtonFrame->hide();
}
// Create list of flags
confDomNode = descDocElem.firstChild();
while ( !confDomNode.isNull() )
{
QDomElement confDomElement = confDomNode.toElement();
if ( !confDomElement.isNull() )
{
QString optionType = confDomElement.tagName();
QgsDebugMsg( "optionType = " + optionType );
if ( optionType == QLatin1String( "flag" ) )
{
QString name = confDomElement.attribute( QStringLiteral( "name" ) ).trimmed();
QgsDebugMsg( "name = " + name );
mFlagNames.append( name );
}
}
confDomNode = confDomNode.nextSibling();
}
#if 0
// This works, but it would mean to desable check if 'field' tag has 'layer' defined in qgm.
// It is probably better to require 'layer' attribute, so that it is always explicitly defined,
// and we are sure it is set for modules with more inputs, where auto connection cannot be used
// (guidependency missing in GRASS 6)
// Add default inter param relations
QList<QgsGrassModuleInput *>vectorInputs;
Q_FOREACH ( QgsGrassModuleParam *param, mParams )
{
QgsGrassModuleInput *vectorInput = dynamic_cast<QgsGrassModuleInput *>( param );
if ( vectorInput )
{
vectorInputs << vectorInput;
}
}
if ( vectorInputs.size() == 1 )
{
QgsDebugMsg( "One input found, try to connect with column options" );
QgsGrassModuleInput *vectorInput = vectorInputs[0];
Q_FOREACH ( QgsGrassModuleParam *param, mParams )
{
QgsGrassModuleField *moduleField = dynamic_cast<QgsGrassModuleField *>( param );
if ( moduleField )
{
if ( !moduleField->layerInput() )
{
QgsDebugMsg( "Set " + vectorInput->key() + " as layer input for " + moduleField->key() );
moduleField->setLayerInput( vectorInput );
}
}
}
}
else if ( vectorInputs.size() > 1 )
{
// TODO: check if type/field/column options have defined input layer
// TODO: use 'guidependency' interface description attribute once GRASS 6 is dropped
}
#endif
if ( layout )
{
layout->addStretch();
}
Q_FOREACH ( QgsGrassModuleParam* item, mParams )
{
mErrors << item->errors();
}
}
void QgsGrassModuleStandardOptions::switchAdvanced()
{
if ( mAdvancedFrame.isHidden() )
{
mAdvancedFrame.show();
mAdvancedPushButton.setText( tr( "<< Hide advanced options" ) );
}
else
{
mAdvancedFrame.hide();
mAdvancedPushButton.setText( tr( "Show advanced options >>" ) );
}
}
QStringList QgsGrassModuleStandardOptions::arguments()
{
QStringList arg;
for ( int i = 0; i < mParams.size(); i++ )
{
QStringList list = mParams[i]->options();
for ( QStringList::Iterator it = list.begin();
it != list.end(); ++it )
{
arg.append( *it );
}
}
return arg;
}
// id is not used in fact, was intended for field, but key is used instead
QgsGrassModuleParam *QgsGrassModuleStandardOptions::itemByKey( QString key )
{
QgsDebugMsg( "key = " + key );
for ( int i = 0; i < mParams.size(); i++ )
{
if ( mParams[i]->key() == key )
{
return mParams[i];
}
}
mErrors << tr( "Item with key %1 not found" ).arg( key );
return 0;
}
QgsGrassModuleParam *QgsGrassModuleStandardOptions::item( QString id )
{
QgsDebugMsg( "id = " + id );
for ( int i = 0; i < mParams.size(); i++ )
{
if ( mParams[i]->id() == id )
{
return mParams[i];
}
}
mErrors << tr( "Item with id %1 not found" ).arg( id );
return 0;
}
QStringList QgsGrassModuleStandardOptions::checkOutput()
{
QgsDebugMsg( "called." );
QStringList list;
for ( int i = 0; i < mParams.size(); i++ )
{
QgsGrassModuleOption *opt = dynamic_cast<QgsGrassModuleOption *>( mParams[i] );
if ( !opt )
continue;
QgsDebugMsg( "opt->key() = " + opt->key() );
if ( opt->isOutput() )
{
QString out = opt->outputExists();
if ( !out.isNull() )
{
list.append( out );
}
}
}
return list;
}
QList<QgsGrassProvider *> QgsGrassModuleStandardOptions::grassProviders()
{
QList<QgsGrassProvider *> providers;
Q_FOREACH ( QgsMapLayer *layer, QgsProject::instance()->mapLayers().values() )
{
if ( layer->type() == QgsMapLayer::VectorLayer )
{
QgsVectorLayer *vector = qobject_cast<QgsVectorLayer*>( layer );
if ( vector && vector->providerType() == QLatin1String( "grass" ) )
{
QgsGrassProvider *provider = qobject_cast<QgsGrassProvider *>( vector->dataProvider() );
if ( provider )
{
providers << provider;
}
}
}
}
return providers;
}
QList<QgsGrassRasterProvider *> QgsGrassModuleStandardOptions::grassRasterProviders()
{
QList<QgsGrassRasterProvider *> providers;
Q_FOREACH ( QgsMapLayer *layer, QgsProject::instance()->mapLayers().values() )
{
if ( layer->type() == QgsMapLayer::RasterLayer )
{
QgsRasterLayer *raster = qobject_cast<QgsRasterLayer*>( layer );
if ( raster && raster->providerType() == QLatin1String( "grassraster" ) )
{
QgsGrassRasterProvider *provider = qobject_cast<QgsGrassRasterProvider *>( raster->dataProvider() );
if ( provider )
{
providers << provider;
}
}
}
}
return providers;
}
// freezeOutput/thawOutput is only necessary in Windows, where files cannot be overwritten
// when open by another app. It is enabled on all platforms, so that it gets tested.
void QgsGrassModuleStandardOptions::freezeOutput( bool freeze )
{
QgsDebugMsg( "called." );
for ( int i = 0; i < mParams.size(); i++ )
{
QgsGrassModuleOption *opt = dynamic_cast<QgsGrassModuleOption *>( mParams[i] );
if ( !opt || !opt->isOutput() )
{
continue;
}
QgsDebugMsg( "opt->key() = " + opt->key() );
if ( opt->outputType() == QgsGrassModuleOption::Vector )
{
QgsDebugMsg( "freeze vector layers" );
QgsGrassObject outputObject = QgsGrass::getDefaultMapsetObject();
outputObject.setName( opt->value() );
outputObject.setType( QgsGrassObject::Vector );
QgsDebugMsg( "outputObject = " + outputObject.toString() );
Q_FOREACH ( QgsGrassProvider *provider, grassProviders() )
{
QgsGrassObject layerObject;
layerObject.setFromUri( provider->dataSourceUri() );
if ( layerObject == outputObject )
{
if ( freeze )
{
QgsDebugMsg( "freeze map " + provider->dataSourceUri() );
provider->freeze();
}
else
{
QgsDebugMsg( "thaw map " + provider->dataSourceUri() );
provider->thaw();
}
}
}
}
else if ( opt->outputType() == QgsGrassModuleOption::Raster )
{
QgsDebugMsg( "freeze raster layers" );
QgsGrassObject outputObject = QgsGrass::getDefaultMapsetObject();
outputObject.setName( opt->value() );
outputObject.setType( QgsGrassObject::Raster );
QgsDebugMsg( "outputObject = " + outputObject.toString() );
Q_FOREACH ( QgsGrassRasterProvider *provider, grassRasterProviders() )
{
QgsGrassObject layerObject;
layerObject.setFromUri( provider->dataSourceUri() );
if ( layerObject == outputObject )
{
if ( freeze )
{
QgsDebugMsg( "freeze map " + provider->dataSourceUri() );
provider->freeze();
}
else
{
QgsDebugMsg( "thaw map " + provider->dataSourceUri() );
provider->thaw();
}
}
}
}
}
}
QStringList QgsGrassModuleStandardOptions::output( int type )
{
QgsDebugMsg( "called." );
QStringList list;
for ( int i = 0; i < mParams.size(); i++ )
{
QgsGrassModuleOption *opt = dynamic_cast<QgsGrassModuleOption *>( mParams[i] );
if ( !opt )
continue;
QgsDebugMsg( "opt->key() = " + opt->key() );
if ( opt->isOutput() )
{
if ( opt->outputType() == type )
{
QString out = opt->value();
if ( !out.isEmpty() )
{
list.append( out );
}
}
}
}
return list;
}
bool QgsGrassModuleStandardOptions::hasOutput( int type )
{
QgsDebugMsg( "called." );
QStringList list;
for ( int i = 0; i < mParams.size(); i++ )
{
QgsGrassModuleOption *opt = dynamic_cast<QgsGrassModuleOption *>( mParams[i] );
if ( !opt )
continue;
QgsDebugMsg( "opt->key() = " + opt->key() );
if ( opt->isOutput() )
{
if ( opt->outputType() == type )
return true;
}
}
return false;
}
QStringList QgsGrassModuleStandardOptions::ready()
{
QStringList list;
for ( int i = 0; i < mParams.size(); i++ )
{
QString err = mParams[i]->ready();
if ( !err.isNull() )
{
list.append( err );
}
}
return list;
}
QStringList QgsGrassModuleStandardOptions::checkRegion()
{
QgsDebugMsg( "called." );
QStringList list;
struct Cell_head currentWindow;
try
{
QgsGrass::region( &currentWindow );
}
catch ( QgsGrass::Exception &e )
{
QgsGrass::warning( e );
return list;
}
for ( int i = 0; i < mParams.size(); i++ )
{
struct Cell_head window;
QgsGrassModuleInput *item = dynamic_cast<QgsGrassModuleInput *>( mParams[i] );
if ( !item )
continue;
QgsDebugMsg( "currentMap = " + item->currentMap() );
// The input may be empty, it means input is not used.
if ( item->currentMap().isEmpty() )
{
continue;
}
if ( !getCurrentMapRegion( item, &window ) )
{
continue;
}
if ( G_window_overlap( &currentWindow,
window.north, window.south, window.east, window.west ) == 0 )
{
list.append( item->currentMap() );
}
}
return list;
}
bool QgsGrassModuleStandardOptions::inputRegion( struct Cell_head *window, QgsCoordinateReferenceSystem &crs, bool all )
{
QgsDebugMsg( "called." );
RegionMode mode = ( QgsGrassModuleOptions::RegionMode ) mRegionModeComboBox->currentData().toInt();
if ( mDirect && mode == RegionCurrent )
{
// TODO: warn if outside region
crs = mCanvas->mapSettings().destinationCrs();
QgsRectangle rect = mCanvas->extent();
QgsGrass::initRegion( window );
window->west = rect.xMinimum();
window->south = rect.yMinimum();
window->east = rect.xMaximum();
window->north = rect.yMaximum();
window->rows = ( int ) mCanvas->mapSettings().outputSize().height();
window->cols = ( int ) mCanvas->mapSettings().outputSize().width();
try
{
QgsGrass::adjustCellHead( window, 1, 1 );
}
catch ( QgsGrass::Exception &e )
{
QgsGrass::warning( e );
return false;
}
}
else
{
if ( mDirect )
{
QgsGrass::initRegion( window );
}
else
{
// Get current resolution
try
{
QgsGrass::region( window );
}
catch ( QgsGrass::Exception &e )
{
QgsGrass::warning( e );
return false;
}
}
int rasterCount = 0;
int vectorCount = 0;
for ( int i = 0; i < mParams.size(); i++ )
{
struct Cell_head mapWindow;
QgsGrassModuleInput *item = dynamic_cast<QgsGrassModuleInput *>( mParams[i] );
if ( !item )
continue;
if ( !all && !item->useRegion() )
{
continue;
}
QgsDebugMsg( "currentMap = " + item->currentMap() );
// The input may be empty, it means input is not used.
if ( item->currentMap().isEmpty() )
{
continue;
}
if ( !getCurrentMapRegion( item, &mapWindow ) )
{
return false;
}
// TODO: best way to set resolution ?
if ( item->type() == QgsGrassObject::Raster
&& rasterCount == 0 )
{
QgsGrass::copyRegionResolution( &mapWindow, window );
}
if ( rasterCount + vectorCount == 0 )
{
QgsGrass::copyRegionExtent( &mapWindow, window );
}
else
{
QgsGrass::extendRegion( &mapWindow, window );
}
if ( item->type() == QgsGrassObject::Raster )
rasterCount++;
else if ( item->type() == QgsGrassObject::Vector )
vectorCount++;
}
G_adjust_Cell_head3( window, 0, 0, 0 );
}
return true;
}
bool QgsGrassModuleStandardOptions::requestsRegion()
{
QgsDebugMsg( "called." );
if ( mDirect ) return true;
for ( int i = 0; i < mParams.size(); i++ )
{
QgsGrassModuleInput *item = dynamic_cast<QgsGrassModuleInput *>( mParams[i] );
if ( !item )
continue;
if ( item->useRegion() )
return true;
}
return false;
}
bool QgsGrassModuleStandardOptions::usesRegion()
{
QgsDebugMsg( "called." );
for ( int i = 0; i < mParams.size(); i++ )
{
QgsGrassModuleInput *input = dynamic_cast<QgsGrassModuleInput *>( mParams[i] );
if ( input && input->usesRegion() )
return true;
QgsGrassModuleOption *option = dynamic_cast<QgsGrassModuleOption *>( mParams[i] );
if ( option && option->usesRegion() )
return true;
}
QgsDebugMsg( "NO usesRegion()" );
return false;
}
bool QgsGrassModuleStandardOptions::getCurrentMapRegion( QgsGrassModuleInput* input, struct Cell_head * window )
{
if ( !input )
{
return false;
}
QgsDebugMsg( "currentMap = " + input->currentMap() );
if ( input->currentMap().isEmpty() )
{
// The input may be empty, it means input is not used.
return false;
}
QStringList mm = input->currentMap().split( QStringLiteral( "@" ) );
QString map = mm.value( 0 );
QString mapset = QgsGrass::getDefaultMapset();
if ( mm.size() > 1 )
{
mapset = mm.value( 1 );
}
if ( !QgsGrass::mapRegion( input->type(),
QgsGrass::getDefaultGisdbase(),
QgsGrass::getDefaultLocation(), mapset, map,
window ) )
{
QgsGrass::warning( tr( "Cannot get region of map %1" ).arg( input->currentMap() ) );
return false;
}
return true;
}
QgsGrassModuleStandardOptions::~QgsGrassModuleStandardOptions()
{
}
QDomDocument QgsGrassModuleStandardOptions::readInterfaceDescription( const QString & xname, QStringList & errors )
{
QDomDocument gDoc( QStringLiteral( "task" ) );
// Attention!: sh.exe (MSYS) sets $0 in scripts to file name
// without full path. Strange because when run from msys.bat
// $0 is set to full path. GRASS scripts call
// exec g.parser "$0" "$@"
// and it fails -> find and run with full path
QStringList arguments = QgsGrassModule::execArguments( xname );
if ( arguments.size() == 0 )
{
errors << tr( "Cannot find module %1" ).arg( mXName );
return gDoc;
}
QString cmd = arguments.takeFirst();
arguments.append( QStringLiteral( "--interface-description" ) );
QProcess process( this );
QProcessEnvironment environment = QgsGrassModule::processEnvironment( mDirect );
process.setProcessEnvironment( environment );
process.start( cmd, arguments );
// ? Does binary on Win need .exe extension ?
// Return code 255 (-1) was correct in GRASS < 6.1.0
// Return code 1 is the value (correct?) .py modules actually returns (see #4667)
if ( !process.waitForStarted()
|| !process.waitForReadyRead()
|| !process.waitForFinished()
|| ( process.exitCode() != 0 && process.exitCode() != 255 &&
( !cmd.endsWith( QLatin1String( ".py" ) ) || process.exitCode() != 1 ) ) )
{
QString pathVariable = QgsGrassModule::libraryPathVariable();
QgsDebugMsg( "process.exitCode() = " + QString::number( process.exitCode() ) );
QString msg = tr( "Cannot start module %1" ).arg( mXName )
+ "<br><br>" + pathVariable + "=" + environment.value( pathVariable )
+ "<br><br>PATH=" + environment.value( QStringLiteral( "PATH" ) )
+ "<br><br>PYTHONPATH=" + environment.value( QStringLiteral( "PYTHONPATH" ) )
+ "<br><br>" + tr( "command" ) + QStringLiteral( ": %1 %2<br>%3<br>%4" )
.arg( cmd, arguments.join( QStringLiteral( " " ) ),
process.readAllStandardOutput().constData(),
process.readAllStandardError().constData() );
QgsDebugMsg( msg );
errors << msg;
return gDoc;
}
QByteArray baDesc = process.readAllStandardOutput();
// GRASS commands usually output text in system default encoding.
// Let's use the System codec whether Qt doesn't recognize the encoding
// of the interface description (see http://hub.qgis.org/issues/4547)
QTextCodec *codec = 0;
QgsDebugMsg( "trying to get encoding name from XML interface description..." );
// XXX: getting the encoding using a regular expression works
// until GRASS will use UTF-16 or UTF-32.
// TODO: We should check the correct encoding by using the BOM (Byte
// Order Mark) from the beginning of the data.
QString xmlDeclaration = QString::fromUtf8( baDesc ).section( '>', 0, 0, QString::SectionIncludeTrailingSep );
QRegExp reg( "<\\?xml\\s+.*encoding\\s*=\\s*(['\"])([A-Za-z][-a-zA-Z0-9_.]*)\\1\\s*\\?>" );
if ( reg.indexIn( xmlDeclaration ) != -1 )
{
QByteArray enc = reg.cap( 2 ).toLocal8Bit();
QgsDebugMsg( QString( "found encoding name '%1'" ).arg( QString::fromUtf8( enc ) ) );
codec = QTextCodec::codecForName( enc );
if ( !codec )
{
QgsDebugMsg( "unrecognized encoding name. Let's use 'System' codec" );
codec = QTextCodec::codecForName( "System" );
Q_ASSERT( codec );
}
}
else
{
QgsDebugMsg( "unable to get encoding name from XML content. Will let Qt detects encoding!" );
}
bool ok = false;
QString err;
int line = -1, column = -1;
if ( codec )
{
QgsDebugMsg( QString( "parsing XML interface description using '%1' codec..." ).arg( QString::fromUtf8( codec->name() ) ) );
ok = gDoc.setContent( codec->toUnicode( baDesc ), false, &err, &line, &column );
if ( !ok )
{
QgsDebugMsg( "parse FAILED. Will let Qt detects encoding" );
codec = 0;
}
}
if ( !codec )
{
ok = gDoc.setContent( baDesc, false, &err, &line, &column );
}
if ( !ok )
{
QString errmsg = tr( "Cannot read module description (%1):" ).arg( mXName )
+ tr( "\n%1\nat line %2 column %3" ).arg( err ).arg( line ).arg( column );
QgsDebugMsg( errmsg );
errors << errmsg;
}
return gDoc;
}