mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
1663 lines
45 KiB
C++
1663 lines
45 KiB
C++
/***************************************************************************
|
|
qgsgrass.cpp - Data provider for GRASS format
|
|
-------------------
|
|
begin : March, 2004
|
|
copyright : (C) 2004 by Radim Blazek
|
|
email : blazek@itc.it
|
|
***************************************************************************/
|
|
/***************************************************************************
|
|
* *
|
|
* 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 <setjmp.h>
|
|
|
|
#include "qgsgrass.h"
|
|
|
|
#include "qgslogger.h"
|
|
#include "qgsapplication.h"
|
|
#include "qgscoordinatereferencesystem.h"
|
|
#include "qgsrectangle.h"
|
|
#include "qgsconfig.h"
|
|
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <QProcess>
|
|
#include <QSettings>
|
|
#include <QTextStream>
|
|
#include <QTemporaryFile>
|
|
#include <QHash>
|
|
|
|
#include <QTextCodec>
|
|
|
|
extern "C"
|
|
{
|
|
#ifndef _MSC_VER
|
|
#include <unistd.h>
|
|
#endif
|
|
#include <grass/version.h>
|
|
#include <grass/gprojects.h>
|
|
|
|
#if GRASS_VERSION_MAJOR < 7
|
|
#include <grass/Vect.h>
|
|
#else
|
|
#include <grass/vector.h>
|
|
#include <grass/raster.h>
|
|
#endif
|
|
}
|
|
|
|
#if !defined(GRASS_VERSION_MAJOR) || \
|
|
!defined(GRASS_VERSION_MINOR) || \
|
|
GRASS_VERSION_MAJOR<6 || \
|
|
(GRASS_VERSION_MAJOR == 6 && GRASS_VERSION_MINOR <= 2)
|
|
#define G__setenv(name,value) G__setenv( ( char * ) (name), (char *) (value) )
|
|
#endif
|
|
|
|
#define GRASS_LOCK sMutex.lock();
|
|
#define GRASS_UNLOCK sMutex.unlock();
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include <windows.h>
|
|
QString GRASS_LIB_EXPORT QgsGrass::shortPath( const QString &path )
|
|
{
|
|
TCHAR buf[MAX_PATH];
|
|
int len = GetShortPathName( path.toUtf8().constData(), buf, MAX_PATH );
|
|
|
|
if ( len == 0 || len > MAX_PATH )
|
|
{
|
|
QgsDebugMsg( QString( "GetShortPathName('%1') failed with %2: %3" )
|
|
.arg( path ).arg( len ).arg( GetLastError() ) );
|
|
return path;
|
|
}
|
|
|
|
QString res = QString::fromUtf8( buf );
|
|
// GRASS wxpyton GUI fails on paths with backslash, Windows do support forward slashesin paths
|
|
res.replace( "\\", "/" );
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::init( void )
|
|
{
|
|
// Warning!!!
|
|
// G_set_error_routine() once called from plugin
|
|
// is not valid in provider -> call it always
|
|
|
|
// Set error function
|
|
G_set_error_routine( &error_routine );
|
|
|
|
if ( initialized )
|
|
return;
|
|
|
|
QSettings settings;
|
|
|
|
// Is it active mode ?
|
|
if ( getenv( "GISRC" ) )
|
|
{
|
|
active = true;
|
|
// Store default values
|
|
defaultGisdbase = G_gisdbase();
|
|
defaultLocation = G_location();
|
|
defaultMapset = G_mapset();
|
|
}
|
|
else
|
|
{
|
|
active = false;
|
|
}
|
|
|
|
// Don't use GISRC file and read/write GRASS variables (from location G_VAR_GISRC) to memory only.
|
|
G_set_gisrc_mode( G_GISRC_MODE_MEMORY );
|
|
|
|
// Init GRASS libraries (required)
|
|
G_no_gisinit(); // Doesn't check write permissions for mapset compare to G_gisinit("libgrass++");
|
|
|
|
// I think that mask should not be used in QGIS as it can only confuses people,
|
|
// anyway, I don't think anybody is using MASK
|
|
// TODO7: Rast_suppress_masking (see G_suppress_masking() macro above) needs MAPSET
|
|
#if GRASS_VERSION_MAJOR < 7
|
|
G_suppress_masking();
|
|
#endif
|
|
|
|
// Set program name
|
|
G_set_program_name( "QGIS" );
|
|
|
|
// Require GISBASE to be set. This should point to the location of
|
|
// the GRASS installation. The GRASS libraries use it to know
|
|
// where to look for things.
|
|
|
|
// Look first to see if GISBASE env var is already set.
|
|
// This is set when QGIS is run from within GRASS
|
|
// or when set explicitly by the user.
|
|
// This value should always take precedence.
|
|
#ifdef Q_OS_WIN
|
|
QString gisBase = getenv( "WINGISBASE" ) ? getenv( "WINGISBASE" ) : getenv( "GISBASE" );
|
|
gisBase = shortPath( gisBase );
|
|
#else
|
|
QString gisBase = getenv( "GISBASE" );
|
|
#endif
|
|
QgsDebugMsg( QString( "GRASS gisBase from GISBASE env var is: %1" ).arg( gisBase ) );
|
|
if ( !isValidGrassBaseDir( gisBase ) )
|
|
{
|
|
// Look for gisbase in QSettings
|
|
gisBase = settings.value( "/GRASS/gisbase", "" ).toString();
|
|
QgsDebugMsg( QString( "GRASS gisBase from QSettings is: %1" ).arg( gisBase ) );
|
|
}
|
|
|
|
if ( !isValidGrassBaseDir( gisBase ) )
|
|
{
|
|
// Erase gisbase from settings because it does not exists
|
|
settings.setValue( "/GRASS/gisbase", "" );
|
|
|
|
#ifdef Q_OS_WIN
|
|
// Use the applicationDirPath()/grass
|
|
gisBase = shortPath( QCoreApplication::applicationDirPath() + "/grass" );
|
|
QgsDebugMsg( QString( "GRASS gisBase = %1" ).arg( gisBase ) );
|
|
#elif defined(Q_OS_MACX)
|
|
// check for bundled GRASS, fall back to configured path
|
|
gisBase = QCoreApplication::applicationDirPath().append( "/grass" );
|
|
if ( !isValidGrassBaseDir( gisBase ) )
|
|
{
|
|
gisBase = GRASS_BASE;
|
|
}
|
|
QgsDebugMsg( QString( "GRASS gisBase = %1" ).arg( gisBase ) );
|
|
#else
|
|
// Use the location specified --with-grass during configure
|
|
gisBase = GRASS_BASE;
|
|
QgsDebugMsg( QString( "GRASS gisBase from configure is: %1" ).arg( gisBase ) );
|
|
#endif
|
|
}
|
|
|
|
bool userGisbase = false;
|
|
bool valid = false;
|
|
while ( !( valid = isValidGrassBaseDir( gisBase ) ) )
|
|
{
|
|
|
|
// ask user if he wants to specify GISBASE
|
|
QMessageBox::StandardButton res = QMessageBox::warning( 0, QObject::tr( "GRASS plugin" ),
|
|
QObject::tr( "QGIS couldn't find your GRASS installation.\n"
|
|
"Would you like to specify path (GISBASE) to your GRASS installation?" ),
|
|
QMessageBox::Ok | QMessageBox::Cancel );
|
|
|
|
if ( res != QMessageBox::Ok )
|
|
{
|
|
userGisbase = false;
|
|
break;
|
|
}
|
|
|
|
// XXX Need to subclass this and add explantory message above to left side
|
|
userGisbase = true;
|
|
// For Mac, GISBASE folder may be inside GRASS bundle. Use Qt file dialog
|
|
// since Mac native dialog doesn't allow user to browse inside bundles.
|
|
gisBase = QFileDialog::getExistingDirectory(
|
|
0, QObject::tr( "Choose GRASS installation path (GISBASE)" ), gisBase,
|
|
QFileDialog::DontUseNativeDialog );
|
|
if ( gisBase == QString::null )
|
|
{
|
|
// User pressed cancel. No GRASS for you!
|
|
userGisbase = false;
|
|
break;
|
|
}
|
|
#ifdef Q_OS_WIN32
|
|
gisBase = shortPath( gisBase );
|
|
#endif
|
|
}
|
|
|
|
if ( !valid )
|
|
{
|
|
// warn user
|
|
QMessageBox::information( 0, QObject::tr( "GRASS plugin" ),
|
|
QObject::tr( "GRASS data won't be available if GISBASE is not specified." ) );
|
|
}
|
|
|
|
if ( userGisbase )
|
|
{
|
|
settings.setValue( "/GRASS/gisbase", gisBase );
|
|
}
|
|
|
|
QgsDebugMsg( QString( "Valid GRASS gisBase is: %1" ).arg( gisBase ) );
|
|
putEnv( "GISBASE", gisBase );
|
|
|
|
// Add path to GRASS modules
|
|
#ifdef Q_OS_WIN
|
|
QString sep = ";";
|
|
#else
|
|
QString sep = ":";
|
|
#endif
|
|
QString path = gisBase + "/bin";
|
|
path.append( sep + gisBase + "/scripts" );
|
|
path.append( sep + QgsApplication::pkgDataPath() + "/grass/scripts/" );
|
|
|
|
// On windows the GRASS libraries are in
|
|
// QgsApplication::prefixPath(), we have to add them
|
|
// to PATH to enable running of GRASS modules
|
|
// and database drivers
|
|
#ifdef Q_OS_WIN
|
|
// It seems that QgsApplication::prefixPath()
|
|
// is not initialized at this point
|
|
path.append( sep + shortPath( QCoreApplication::applicationDirPath() ) );
|
|
|
|
// Add path to MSYS bin
|
|
// Warning: MSYS sh.exe will translate this path to '/bin'
|
|
if ( QFileInfo( QCoreApplication::applicationDirPath() + "/msys/bin/" ).isDir() )
|
|
path.append( sep + shortPath( QCoreApplication::applicationDirPath() + "/msys/bin/" ) );
|
|
#endif
|
|
|
|
QString p = getenv( "PATH" );
|
|
path.append( sep + p );
|
|
|
|
QgsDebugMsg( QString( "set PATH: %1" ).arg( path ) );
|
|
putEnv( "PATH", path );
|
|
|
|
// Set PYTHONPATH
|
|
QString pythonpath = gisBase + "/etc/python";
|
|
QString pp = getenv( "PYTHONPATH" );
|
|
pythonpath.append( sep + pp );
|
|
QgsDebugMsg( QString( "set PYTHONPATH: %1" ).arg( pythonpath ) );
|
|
putEnv( "PYTHONPATH", pythonpath );
|
|
|
|
// Set GRASS_PAGER if not set, it is necessary for some
|
|
// modules printing to terminal, e.g. g.list
|
|
// We use 'cat' because 'more' is not present in MSYS (Win)
|
|
// and it doesn't work well in built in shell (Unix/Mac)
|
|
// and 'less' is not user friendly (for example user must press
|
|
// 'q' to quit which is definitely difficult for normal user)
|
|
// Also scroling can be don in scrollable window in both
|
|
// MSYS terminal and built in shell.
|
|
if ( !getenv( "GRASS_PAGER" ) )
|
|
{
|
|
QString pager;
|
|
QStringList pagers;
|
|
//pagers << "more" << "less" << "cat"; // se notes above
|
|
pagers << "cat";
|
|
|
|
for ( int i = 0; i < pagers.size(); i++ )
|
|
{
|
|
int state;
|
|
|
|
QProcess p;
|
|
p.start( pagers.at( i ) );
|
|
p.waitForStarted();
|
|
state = p.state();
|
|
p.write( "\004" ); // Ctrl-D
|
|
p.closeWriteChannel();
|
|
p.waitForFinished( 1000 );
|
|
p.kill();
|
|
|
|
if ( state == QProcess::Running )
|
|
{
|
|
pager = pagers.at( i );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( pager.length() > 0 )
|
|
{
|
|
putEnv( "GRASS_PAGER", pager );
|
|
}
|
|
}
|
|
|
|
initialized = 1;
|
|
}
|
|
|
|
/*
|
|
* Check if given directory contains a GRASS installation
|
|
*/
|
|
bool QgsGrass::isValidGrassBaseDir( QString const gisBase )
|
|
{
|
|
QgsDebugMsg( "isValidGrassBaseDir()" );
|
|
// GRASS currently doesn't handle paths with blanks
|
|
if ( gisBase.isEmpty() || gisBase.contains( " " ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* TODO: G_is_gisbase() was added to GRASS 6.1 06-05-24,
|
|
enable its use after some period (others do update) */
|
|
#if 0
|
|
if ( QgsGrass::versionMajor() > 6 || QgsGrass::versionMinor() > 0 )
|
|
{
|
|
if ( G_is_gisbase( gisBase.toUtf8().constData() ) )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
QFileInfo gbi( gisBase + "/etc/element_list" );
|
|
if ( gbi.exists() )
|
|
return true;
|
|
#if 0
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool QgsGrass::activeMode( void )
|
|
{
|
|
init();
|
|
return active;
|
|
}
|
|
|
|
QString QgsGrass::getDefaultGisdbase( void )
|
|
{
|
|
init();
|
|
return defaultGisdbase;
|
|
}
|
|
|
|
QString QgsGrass::getDefaultLocation( void )
|
|
{
|
|
init();
|
|
return defaultLocation;
|
|
}
|
|
|
|
QString QgsGrass::getDefaultMapset( void )
|
|
{
|
|
init();
|
|
return defaultMapset;
|
|
}
|
|
|
|
void QgsGrass::setLocation( QString gisdbase, QString location )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
setMapset( gisdbase, location, "PERMANENT" );
|
|
}
|
|
|
|
void QgsGrass::setMapset( QString gisdbase, QString location, QString mapset )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2 mapset = %3" ).arg( gisdbase ).arg( location ).arg( mapset ) );
|
|
init();
|
|
|
|
// Set principal GRASS variables (in memory)
|
|
#ifdef Q_OS_WIN
|
|
G__setenv( "GISDBASE", shortPath( gisdbase ).toUtf8().data() );
|
|
#else
|
|
G__setenv( "GISDBASE", gisdbase.toUtf8().data() );
|
|
#endif
|
|
G__setenv( "LOCATION_NAME", location.toUtf8().data() );
|
|
G__setenv( "MAPSET", mapset.toUtf8().data() );
|
|
|
|
// Add all available mapsets to search path
|
|
char **ms = 0;
|
|
G_TRY
|
|
{
|
|
ms = G_available_mapsets();
|
|
}
|
|
G_CATCH( QgsGrass::Exception &e )
|
|
{
|
|
Q_UNUSED( e );
|
|
QgsDebugMsg( QString( "No available mapsets found: %1" ).arg( e.what() ) );
|
|
return;
|
|
}
|
|
|
|
for ( int i = 0; ms[i]; i++ )
|
|
G_add_mapset_to_search_path( ms[i] );
|
|
}
|
|
|
|
jmp_buf QgsGrass::jumper;
|
|
|
|
int QgsGrass::initialized = 0;
|
|
|
|
bool QgsGrass::active = 0;
|
|
|
|
QgsGrass::GERROR QgsGrass::lastError = QgsGrass::OK;
|
|
|
|
QString QgsGrass::error_message;
|
|
|
|
QString QgsGrass::defaultGisdbase;
|
|
QString QgsGrass::defaultLocation;
|
|
QString QgsGrass::defaultMapset;
|
|
|
|
QString QgsGrass::mMapsetLock;
|
|
QString QgsGrass::mGisrc;
|
|
QString QgsGrass::mTmp;
|
|
|
|
QMutex QgsGrass::sMutex;
|
|
|
|
int QgsGrass::error_routine( char *msg, int fatal )
|
|
{
|
|
return error_routine(( const char* ) msg, fatal );
|
|
}
|
|
|
|
int QgsGrass::error_routine( const char *msg, int fatal )
|
|
{
|
|
// G_fatal_error obviously is not thread safe (everything static in GRASS, especially fatal_jmp_buf)
|
|
// it means that anything which may end up with G_fatal_error must use mutex
|
|
|
|
// Unfortunately the exceptions thrown here can only be caught if GRASS libraries are compiled
|
|
// with -fexception option on Linux (works on Windows)
|
|
// GRASS developers are reluctant to add -fexception by default
|
|
// https://trac.osgeo.org/grass/ticket/869
|
|
QgsDebugMsg( QString( "error_routine (fatal = %1): %2" ).arg( fatal ).arg( msg ) );
|
|
|
|
error_message = msg;
|
|
|
|
if ( fatal )
|
|
{
|
|
QgsDebugMsg( "fatal -> longjmp" );
|
|
// Exceptions cannot be thrown from here if GRASS lib is not compiled with -fexceptions
|
|
//throw QgsGrass::Exception( QString::fromUtf8( msg ) );
|
|
lastError = FATAL;
|
|
|
|
#if (GRASS_VERSION_MAJOR < 7)
|
|
// longjump() is called by G_fatal_error in GRASS >= 7
|
|
longjmp( QgsGrass::jumper, 1 );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
lastError = WARNING;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::resetError( void )
|
|
{
|
|
lastError = OK;
|
|
}
|
|
|
|
int GRASS_LIB_EXPORT QgsGrass::error( void )
|
|
{
|
|
return lastError;
|
|
}
|
|
|
|
QString GRASS_LIB_EXPORT QgsGrass::errorMessage( void )
|
|
{
|
|
return error_message;
|
|
}
|
|
|
|
QString GRASS_LIB_EXPORT QgsGrass::openMapset( QString gisdbase, QString location, QString mapset )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1" ).arg( gisdbase.toUtf8().constData() ) );
|
|
QgsDebugMsg( QString( "location = %1" ).arg( location.toUtf8().constData() ) );
|
|
QgsDebugMsg( QString( "mapset = %1" ).arg( mapset.toUtf8().constData() ) );
|
|
|
|
QString mapsetPath = gisdbase + "/" + location + "/" + mapset;
|
|
|
|
// Check if the mapset is in use
|
|
QString gisBase = getenv( "GISBASE" );
|
|
if ( gisBase.isEmpty() )
|
|
return QObject::tr( "GISBASE is not set." );
|
|
|
|
QFileInfo fi( mapsetPath + "/WIND" );
|
|
if ( !fi.exists() )
|
|
{
|
|
return QObject::tr( "%1 is not a GRASS mapset." ).arg( mapsetPath );
|
|
}
|
|
|
|
QString lock = mapsetPath + "/.gislock";
|
|
|
|
#ifndef _MSC_VER
|
|
int pid = getpid();
|
|
#else
|
|
int pid = GetCurrentProcessId();
|
|
#endif
|
|
|
|
QgsDebugMsg( QString( "pid = %1" ).arg( pid ) );
|
|
|
|
#ifndef Q_OS_WIN
|
|
QFile lockFile( lock );
|
|
QProcess *process = new QProcess();
|
|
QString lockProgram( gisBase + "/etc/lock" );
|
|
|
|
process->start( lockProgram, QStringList() << lock << QString::number( pid ) );
|
|
if ( !process->waitForStarted() )
|
|
{
|
|
delete process;
|
|
return QObject::tr( "Cannot start %1/etc/lock" ).arg( gisBase );
|
|
}
|
|
|
|
process->waitForFinished( -1 );
|
|
|
|
int status = process->exitStatus();
|
|
QgsDebugMsg( QString( "status = %1" ).arg( status ) );
|
|
delete process;
|
|
|
|
if ( status > 0 )
|
|
return QObject::tr( "Mapset is already in use." );
|
|
#endif // Q_OS_WIN
|
|
|
|
// Create temporary directory
|
|
QFileInfo info( mapsetPath );
|
|
QString user = info.owner();
|
|
|
|
mTmp = QDir::tempPath() + "/grass6-" + user + "-" + QString::number( pid );
|
|
QDir dir( mTmp );
|
|
if ( dir.exists() )
|
|
{
|
|
QFileInfo dirInfo( mTmp );
|
|
if ( !dirInfo.isWritable() )
|
|
{
|
|
#ifndef Q_OS_WIN
|
|
lockFile.remove();
|
|
#endif
|
|
return QObject::tr( "Temporary directory %1 exists but is not writable" ).arg( mTmp );
|
|
}
|
|
}
|
|
else if ( !dir.mkdir( mTmp ) )
|
|
{
|
|
#ifndef Q_OS_WIN
|
|
lockFile.remove();
|
|
#endif
|
|
return QObject::tr( "Cannot create temporary directory %1" ).arg( mTmp );
|
|
}
|
|
|
|
// Create GISRC file
|
|
QString globalGisrc = QDir::home().path() + "/.grassrc6";
|
|
mGisrc = mTmp + "/gisrc";
|
|
|
|
QgsDebugMsg( QString( "globalGisrc = %1" ).arg( globalGisrc ) );
|
|
QgsDebugMsg( QString( "mGisrc = %1" ).arg( mGisrc ) );
|
|
|
|
QFile out( mGisrc );
|
|
if ( !out.open( QIODevice::WriteOnly ) )
|
|
{
|
|
#ifndef Q_OS_WIN
|
|
lockFile.remove();
|
|
#endif
|
|
return QObject::tr( "Cannot create %1" ).arg( mGisrc );
|
|
}
|
|
QTextStream stream( &out );
|
|
|
|
QFile in( globalGisrc );
|
|
QString line;
|
|
bool guiSet = false;
|
|
char buf[1000];
|
|
if ( in.open( QIODevice::ReadOnly ) )
|
|
{
|
|
while ( in.readLine( buf, 1000 ) != -1 )
|
|
{
|
|
line = buf;
|
|
if ( line.contains( "GISDBASE:" ) ||
|
|
line.contains( "LOCATION_NAME:" ) ||
|
|
line.contains( "MAPSET:" ) )
|
|
{
|
|
continue;
|
|
}
|
|
if ( line.contains( "GRASS_GUI:" ) )
|
|
guiSet = true;
|
|
stream << line;
|
|
}
|
|
in.close();
|
|
}
|
|
line = "GISDBASE: " + gisdbase + "\n";
|
|
stream << line;
|
|
line = "LOCATION_NAME: " + location + "\n";
|
|
stream << line;
|
|
line = "MAPSET: " + mapset + "\n";
|
|
stream << line;
|
|
if ( !guiSet )
|
|
{
|
|
stream << "GRASS_GUI: wxpython\n";
|
|
}
|
|
|
|
out.close();
|
|
|
|
// Set GISRC environment variable
|
|
|
|
/* _Correct_ putenv() implementation is not making copy! */
|
|
putEnv( "GISRC", mGisrc );
|
|
|
|
// Reinitialize GRASS
|
|
G__setenv( "GISRC", mGisrc.toUtf8().data() );
|
|
#ifdef Q_OS_WIN
|
|
G__setenv( "GISDBASE", shortPath( gisdbase ).toLocal8Bit().data() );
|
|
#else
|
|
G__setenv( "GISDBASE", gisdbase.toUtf8().data() );
|
|
#endif
|
|
G__setenv( "LOCATION_NAME", location.toLocal8Bit().data() );
|
|
G__setenv( "MAPSET", mapset.toLocal8Bit().data() );
|
|
defaultGisdbase = gisdbase;
|
|
defaultLocation = location;
|
|
defaultMapset = mapset;
|
|
|
|
active = true;
|
|
|
|
#ifndef Q_OS_WIN
|
|
// Close old mapset
|
|
if ( mMapsetLock.length() > 0 )
|
|
{
|
|
QFile file( mMapsetLock );
|
|
file.remove();
|
|
}
|
|
#endif
|
|
|
|
mMapsetLock = lock;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
QString QgsGrass::closeMapset()
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
|
|
if ( mMapsetLock.length() > 0 )
|
|
{
|
|
#ifndef Q_OS_WIN
|
|
QFile file( mMapsetLock );
|
|
if ( !file.remove() )
|
|
{
|
|
return QObject::tr( "Cannot remove mapset lock: %1" ).arg( mMapsetLock );
|
|
}
|
|
#endif
|
|
mMapsetLock = "";
|
|
|
|
putenv(( char * ) "GISRC" );
|
|
|
|
// Reinitialize GRASS
|
|
G__setenv( "GISRC", ( char * ) "" );
|
|
|
|
// Temporarily commented because of
|
|
// http://trac.osgeo.org/qgis/ticket/1900
|
|
// http://trac.osgeo.org/gdal/ticket/3313
|
|
// it can be uncommented once GDAL with patch gets deployed (probably GDAL 1.8)
|
|
//G__setenv( "GISDBASE", ( char * ) "" );
|
|
//G__setenv( "LOCATION_NAME", ( char * ) "" );
|
|
//G__setenv( "MAPSET", ( char * ) "" );
|
|
|
|
defaultGisdbase = "";
|
|
defaultLocation = "";
|
|
defaultMapset = "";
|
|
active = 0;
|
|
|
|
// Delete temporary dir
|
|
|
|
// To be sure that we don't delete '/' for example
|
|
if ( mTmp.left( 4 ) == "/tmp" )
|
|
{
|
|
QDir dir( mTmp );
|
|
for ( unsigned int i = 0; i < dir.count(); i++ )
|
|
{
|
|
if ( dir[i] == "." || dir[i] == ".." )
|
|
continue;
|
|
|
|
dir.remove( dir[i] );
|
|
if ( dir.remove( dir[i] ) )
|
|
{
|
|
QgsDebugMsg( QString( "Cannot remove temporary file %1" ).arg( dir[i] ) );
|
|
}
|
|
}
|
|
|
|
if ( !dir.rmdir( mTmp ) )
|
|
{
|
|
QgsDebugMsg( QString( "Cannot remove temporary directory %1" ).arg( mTmp ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::locations( QString gisbase )
|
|
{
|
|
QgsDebugMsg( QString( "gisbase = %1" ).arg( gisbase ) );
|
|
|
|
QStringList list;
|
|
|
|
if ( gisbase.isEmpty() )
|
|
return list;
|
|
|
|
QDir d = QDir( gisbase );
|
|
d.setFilter( QDir::NoDotAndDotDot | QDir::Dirs );
|
|
|
|
for ( unsigned int i = 0; i < d.count(); i++ )
|
|
{
|
|
if ( QFile::exists( gisbase + "/" + d[i]
|
|
+ "/PERMANENT/DEFAULT_WIND" ) )
|
|
{
|
|
list.append( QString( d[i] ) );
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::mapsets( QString gisbase, QString locationName )
|
|
{
|
|
QgsDebugMsg( QString( "gisbase = %1 locationName = %2" ).arg( gisbase ).arg( locationName ) );
|
|
|
|
if ( gisbase.isEmpty() || locationName.isEmpty() )
|
|
return QStringList();
|
|
|
|
return QgsGrass::mapsets( gisbase + "/" + locationName );
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::mapsets( QString locationPath )
|
|
{
|
|
QgsDebugMsg( QString( "locationPath = %1" ).arg( locationPath ) );
|
|
|
|
QStringList list;
|
|
|
|
if ( locationPath.isEmpty() )
|
|
return list;
|
|
|
|
QDir d = QDir( locationPath );
|
|
d.setFilter( QDir::NoDotAndDotDot | QDir::Dirs );
|
|
|
|
for ( unsigned int i = 0; i < d.count(); i++ )
|
|
{
|
|
if ( QFile::exists( locationPath + "/" + d[i] + "/WIND" ) )
|
|
{
|
|
list.append( d[i] );
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::vectors( QString gisbase, QString locationName,
|
|
QString mapsetName )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
|
|
if ( gisbase.isEmpty() || locationName.isEmpty() || mapsetName.isEmpty() )
|
|
return QStringList();
|
|
|
|
/* TODO: G_list() was added to GRASS 6.1 06-05-24,
|
|
enable its use after some period (others do update) */
|
|
/*
|
|
if ( QgsGrass::versionMajor() > 6 || QgsGrass::versionMinor() > 0 )
|
|
{
|
|
QStringList list;
|
|
|
|
char **glist = G_list( G_ELEMENT_VECTOR,
|
|
gisbase.toUtf8().constData(),
|
|
locationName.toUtf8().constData(),
|
|
mapsetName.toUtf8().constData() );
|
|
|
|
int i = 0;
|
|
|
|
while ( glist[i] )
|
|
{
|
|
list.append( QString(glist[i]) );
|
|
i++;
|
|
}
|
|
|
|
G_free_list ( glist );
|
|
|
|
return list;
|
|
}
|
|
*/
|
|
|
|
return QgsGrass::vectors( gisbase + "/" + locationName + "/" + mapsetName );
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::vectors( QString mapsetPath )
|
|
{
|
|
QgsDebugMsg( QString( "mapsetPath = %1" ).arg( mapsetPath ) );
|
|
|
|
QStringList list;
|
|
|
|
if ( mapsetPath.isEmpty() )
|
|
return list;
|
|
|
|
QDir d = QDir( mapsetPath + "/vector" );
|
|
d.setFilter( QDir::NoDotAndDotDot | QDir::Dirs );
|
|
|
|
for ( unsigned int i = 0; i < d.count(); i++ )
|
|
{
|
|
/*
|
|
if ( QFile::exists ( mapsetPath + "/vector/" + d[i] + "/head" ) )
|
|
{
|
|
list.append(d[i]);
|
|
}
|
|
*/
|
|
list.append( d[i] );
|
|
}
|
|
return list;
|
|
}
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::vectorLayers( QString gisdbase,
|
|
QString location, QString mapset, QString mapName )
|
|
{
|
|
GRASS_LOCK
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2 mapset = %3 mapName = %4" ).arg( gisdbase ).arg( location ).arg( mapset ).arg( mapName ) );
|
|
QStringList list;
|
|
|
|
// Set location
|
|
QgsGrass::setLocation( gisdbase, location );
|
|
|
|
/* Open vector */
|
|
QgsGrass::resetError();
|
|
//Vect_set_open_level( 2 );
|
|
struct Map_info map;
|
|
int level = -1;
|
|
|
|
// Vect_open_old_head GRASS is raising fatal error if topo exists but it is in different (older) version.
|
|
// It means that even we could open it on level one, it ends with exception,
|
|
// but we need level 2 anyway to get list of layers, so it does not matter, only the error message may be misleading.
|
|
|
|
G_TRY
|
|
{
|
|
level = Vect_open_old_head( &map, ( char * ) mapName.toUtf8().data(), ( char * ) mapset.toUtf8().data() );
|
|
}
|
|
G_CATCH( QgsGrass::Exception &e )
|
|
{
|
|
Q_UNUSED( e );
|
|
QgsDebugMsg( QString( "Cannot open GRASS vector: %1" ).arg( e.what() ) );
|
|
GRASS_UNLOCK
|
|
return list;
|
|
}
|
|
|
|
if ( level == 1 )
|
|
{
|
|
QgsDebugMsg( "Cannot open vector on level 2" );
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ), QObject::tr( "Cannot open vector %1 in mapset %2 on level 2 (topology not available, try to rebuild topology using v.build module)." ).arg( mapName ).arg( mapset ) );
|
|
// Vect_close here is correct, it should work, but it seems to cause
|
|
// crash on win http://trac.osgeo.org/qgis/ticket/2003
|
|
// disabled on win test it
|
|
#ifndef Q_OS_WIN
|
|
Vect_close( &map );
|
|
#endif
|
|
GRASS_UNLOCK
|
|
return list;
|
|
}
|
|
else if ( level < 1 )
|
|
{
|
|
QgsDebugMsg( "Cannot open vector" );
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ), QObject::tr( "Cannot open vector %1 in mapset %2" ).arg( mapName ).arg( mapset ) );
|
|
GRASS_UNLOCK
|
|
return list;
|
|
}
|
|
|
|
QgsDebugMsg( "GRASS vector successfully opened" );
|
|
|
|
|
|
// Get layers
|
|
int ncidx = Vect_cidx_get_num_fields( &map );
|
|
|
|
for ( int i = 0; i < ncidx; i++ )
|
|
{
|
|
int field = Vect_cidx_get_field_number( &map, i );
|
|
QString fs;
|
|
fs.sprintf( "%d", field );
|
|
|
|
QgsDebugMsg( QString( "i = %1 layer = %2" ).arg( i ).arg( field ) );
|
|
|
|
/* Points */
|
|
int npoints = Vect_cidx_get_type_count( &map, field, GV_POINT );
|
|
if ( npoints > 0 )
|
|
{
|
|
QString l = fs + "_point";
|
|
list.append( l );
|
|
}
|
|
|
|
/* Lines */
|
|
/* Lines without category appears in layer 0, but not boundaries */
|
|
int tp;
|
|
if ( field == 0 )
|
|
tp = GV_LINE;
|
|
else
|
|
tp = GV_LINE | GV_BOUNDARY;
|
|
|
|
int nlines = Vect_cidx_get_type_count( &map, field, tp );
|
|
if ( nlines > 0 )
|
|
{
|
|
QString l = fs + "_line";
|
|
list.append( l );
|
|
}
|
|
|
|
/* Faces */
|
|
int nfaces = Vect_cidx_get_type_count( &map, field, GV_FACE );
|
|
if ( nfaces > 0 )
|
|
{
|
|
QString l = fs + "_face";
|
|
list.append( l );
|
|
}
|
|
|
|
/* Polygons */
|
|
int nareas = Vect_cidx_get_type_count( &map, field, GV_AREA );
|
|
if ( nareas > 0 )
|
|
{
|
|
QString l = fs + "_polygon";
|
|
list.append( l );
|
|
}
|
|
}
|
|
|
|
// TODO: add option in GUI to set listTopoLayers
|
|
QSettings settings;
|
|
bool listTopoLayers = settings.value( "/GRASS/listTopoLayers", false ).toBool();
|
|
if ( listTopoLayers )
|
|
{
|
|
// add topology layers
|
|
if ( Vect_get_num_primitives( &map, GV_POINTS ) > 0 )
|
|
{
|
|
#if GRASS_VERSION_MAJOR < 7 /* no more point in GRASS 7 topo */
|
|
list.append( "topo_point" );
|
|
#endif
|
|
}
|
|
if ( Vect_get_num_primitives( &map, GV_LINES ) > 0 )
|
|
{
|
|
list.append( "topo_line" );
|
|
}
|
|
if ( Vect_get_num_nodes( &map ) > 0 )
|
|
{
|
|
list.append( "topo_node" );
|
|
}
|
|
}
|
|
|
|
Vect_close( &map );
|
|
|
|
GRASS_UNLOCK
|
|
return list;
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::rasters( QString gisbase, QString locationName,
|
|
QString mapsetName )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
|
|
if ( gisbase.isEmpty() || locationName.isEmpty() || mapsetName.isEmpty() )
|
|
return QStringList();
|
|
|
|
|
|
/* TODO: G_list() was added to GRASS 6.1 06-05-24,
|
|
enable its use after some period (others do update) */
|
|
/*
|
|
if ( QgsGrass::versionMajor() > 6 || QgsGrass::versionMinor() > 0 )
|
|
{
|
|
QStringList list;
|
|
|
|
char **glist = G_list( G_ELEMENT_RASTER,
|
|
gisbase.toUtf8().constData(),
|
|
locationName.toUtf8().constData(),
|
|
mapsetName.toUtf8().constData() );
|
|
|
|
int i = 0;
|
|
|
|
while ( glist[i] )
|
|
{
|
|
list.append( QString(glist[i]) );
|
|
i++;
|
|
}
|
|
|
|
G_free_list ( glist );
|
|
|
|
return list;
|
|
}
|
|
*/
|
|
|
|
return QgsGrass::rasters( gisbase + "/" + locationName + "/" + mapsetName );
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::rasters( QString mapsetPath )
|
|
{
|
|
QgsDebugMsg( QString( "mapsetPath = %1" ).arg( mapsetPath ) );
|
|
|
|
QStringList list;
|
|
|
|
if ( mapsetPath.isEmpty() )
|
|
return list;
|
|
|
|
QDir d = QDir( mapsetPath + "/cellhd" );
|
|
d.setFilter( QDir::Files );
|
|
|
|
for ( unsigned int i = 0; i < d.count(); i++ )
|
|
{
|
|
list.append( d[i] );
|
|
}
|
|
return list;
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::elements( QString gisbase, QString locationName,
|
|
QString mapsetName, QString element )
|
|
{
|
|
if ( gisbase.isEmpty() || locationName.isEmpty() || mapsetName.isEmpty() )
|
|
return QStringList();
|
|
|
|
return QgsGrass::elements( gisbase + "/" + locationName + "/" + mapsetName,
|
|
element );
|
|
}
|
|
|
|
QStringList GRASS_LIB_EXPORT QgsGrass::elements( QString mapsetPath, QString element )
|
|
{
|
|
QgsDebugMsg( QString( "mapsetPath = %1" ).arg( mapsetPath ) );
|
|
|
|
QStringList list;
|
|
|
|
if ( mapsetPath.isEmpty() )
|
|
return list;
|
|
|
|
QDir d = QDir( mapsetPath + "/" + element );
|
|
d.setFilter( QDir::Files );
|
|
|
|
for ( unsigned int i = 0; i < d.count(); i++ )
|
|
{
|
|
list.append( d[i] );
|
|
}
|
|
return list;
|
|
}
|
|
|
|
QString GRASS_LIB_EXPORT QgsGrass::regionString( struct Cell_head *window )
|
|
{
|
|
QString reg;
|
|
int fmt;
|
|
char buf[1024];
|
|
|
|
fmt = window->proj;
|
|
|
|
// TODO 3D
|
|
|
|
reg = "proj:" + QString::number( window->proj ) + ";";
|
|
reg += "zone:" + QString::number( window->zone ) + ";";
|
|
|
|
G_format_northing( window->north, buf, fmt );
|
|
reg += "north:" + QString( buf ) + ";";
|
|
|
|
G_format_northing( window->south, buf, fmt );
|
|
reg += "south:" + QString( buf ) + ";";
|
|
|
|
G_format_easting( window->east, buf, fmt );
|
|
reg += "east:" + QString( buf ) + ";";
|
|
|
|
G_format_easting( window->west, buf, fmt );
|
|
reg += "west:" + QString( buf ) + ";";
|
|
|
|
reg += "cols:" + QString::number( window->cols ) + ";";
|
|
reg += "rows:" + QString::number( window->rows ) + ";";
|
|
|
|
G_format_resolution( window->ew_res, buf, fmt );
|
|
reg += "e-w resol:" + QString( buf ) + ";";
|
|
|
|
G_format_resolution( window->ns_res, buf, fmt );
|
|
reg += "n-s resol:" + QString( buf ) + ";";
|
|
|
|
return reg;
|
|
}
|
|
|
|
bool GRASS_LIB_EXPORT QgsGrass::region( QString gisbase,
|
|
QString location, QString mapset,
|
|
struct Cell_head *window )
|
|
{
|
|
QgsGrass::setLocation( gisbase, location );
|
|
|
|
#if GRASS_VERSION_MAJOR < 7
|
|
if ( G__get_window( window, ( char * ) "", ( char * ) "WIND", mapset.toUtf8().data() ) )
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
// TODO7: unfortunately G__get_window does not return error code and calls G_fatal_error on error
|
|
G__get_window( window, ( char * ) "", ( char * ) "WIND", mapset.toUtf8().data() );
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
bool GRASS_LIB_EXPORT QgsGrass::writeRegion( QString gisbase,
|
|
QString location, QString mapset,
|
|
struct Cell_head *window )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
QgsDebugMsg( QString( "n = %1 s = %2" ).arg( window->north ).arg( window->south ) );
|
|
QgsDebugMsg( QString( "e = %1 w = %2" ).arg( window->east ).arg( window->west ) );
|
|
|
|
QgsGrass::setMapset( gisbase, location, mapset );
|
|
|
|
if ( G_put_window( window ) == -1 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::copyRegionExtent( struct Cell_head *source,
|
|
struct Cell_head *target )
|
|
{
|
|
target->north = source->north;
|
|
target->south = source->south;
|
|
target->east = source->east;
|
|
target->west = source->west;
|
|
target->top = source->top;
|
|
target->bottom = source->bottom;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::copyRegionResolution( struct Cell_head *source,
|
|
struct Cell_head *target )
|
|
{
|
|
target->ns_res = source->ns_res;
|
|
target->ew_res = source->ew_res;
|
|
target->tb_res = source->tb_res;
|
|
target->ns_res3 = source->ns_res3;
|
|
target->ew_res3 = source->ew_res3;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::extendRegion( struct Cell_head *source,
|
|
struct Cell_head *target )
|
|
{
|
|
if ( source->north > target->north )
|
|
target->north = source->north;
|
|
|
|
if ( source->south < target->south )
|
|
target->south = source->south;
|
|
|
|
if ( source->east > target->east )
|
|
target->east = source->east;
|
|
|
|
if ( source->west < target->west )
|
|
target->west = source->west;
|
|
|
|
if ( source->top > target->top )
|
|
target->top = source->top;
|
|
|
|
if ( source->bottom < target->bottom )
|
|
target->bottom = source->bottom;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::initRegion( struct Cell_head *window )
|
|
{
|
|
window->format = 0;
|
|
window->rows = 0;
|
|
window->rows3 = 0;
|
|
window->cols = 0;
|
|
window->cols3 = 0;
|
|
window->depths = 1;
|
|
window->proj = -1;
|
|
window->zone = -1;
|
|
window->compressed = -1;
|
|
window->ew_res = 0.0;
|
|
window->ew_res3 = 1.0;
|
|
window->ns_res = 0.0;
|
|
window->ns_res3 = 1.0;
|
|
window->tb_res = 1.0;
|
|
window->top = 1.0;
|
|
window->bottom = 0.0;
|
|
window->west = 0;
|
|
window->south = 0;
|
|
window->east = 1;
|
|
window->north = 1;
|
|
window->rows = 1;
|
|
window->cols = 1;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::setRegion( struct Cell_head *window, QgsRectangle rect )
|
|
{
|
|
window->west = rect.xMinimum();
|
|
window->south = rect.yMinimum();
|
|
window->east = rect.xMaximum();
|
|
window->north = rect.yMaximum();
|
|
}
|
|
|
|
bool GRASS_LIB_EXPORT QgsGrass::mapRegion( int type, QString gisbase,
|
|
QString location, QString mapset, QString map,
|
|
struct Cell_head *window )
|
|
{
|
|
QgsDebugMsg( "entered." );
|
|
QgsDebugMsg( QString( "map = %1" ).arg( map ) );
|
|
QgsDebugMsg( QString( "mapset = %1" ).arg( mapset ) );
|
|
|
|
QgsGrass::setLocation( gisbase, location );
|
|
|
|
if ( type == Raster )
|
|
{
|
|
|
|
#if GRASS_VERSION_MAJOR < 7
|
|
if ( G_get_cellhd( map.toUtf8().data(),
|
|
mapset.toUtf8().data(), window ) < 0 )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot read raster map region" ) );
|
|
return false;
|
|
}
|
|
#else
|
|
// TODO7: unfortunately Rast_get_cellhd does not return error code and calls G_fatal_error on error
|
|
Rast_get_cellhd( map.toUtf8().data(), mapset.toUtf8().data(), window );
|
|
#endif
|
|
}
|
|
else if ( type == Vector )
|
|
{
|
|
// Get current projection
|
|
if ( !region( gisbase, location, mapset, window ) )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot read vector map region" ) );
|
|
return false;
|
|
}
|
|
|
|
struct Map_info Map;
|
|
|
|
int level = Vect_open_old_head( &Map,
|
|
map.toUtf8().data(), mapset.toUtf8().data() );
|
|
|
|
if ( level < 2 )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot read vector map region" ) );
|
|
return false;
|
|
}
|
|
|
|
BOUND_BOX box;
|
|
Vect_get_map_box( &Map, &box );
|
|
window->north = box.N;
|
|
window->south = box.S;
|
|
window->west = box.W;
|
|
window->east = box.E;
|
|
window->top = box.T;
|
|
window->bottom = box.B;
|
|
|
|
// Is this optimal ?
|
|
window->ns_res = ( window->north - window->south ) / 1000;
|
|
window->ew_res = window->ns_res;
|
|
if ( window->top > window->bottom )
|
|
{
|
|
window->tb_res = ( window->top - window->bottom ) / 10;
|
|
}
|
|
else
|
|
{
|
|
window->top = window->bottom + 1;
|
|
window->tb_res = 1;
|
|
}
|
|
G_adjust_Cell_head3( window, 0, 0, 0 );
|
|
|
|
Vect_close( &Map );
|
|
}
|
|
else if ( type == Region )
|
|
{
|
|
#if GRASS_VERSION_MAJOR < 7
|
|
if ( G__get_window( window, ( char * ) "windows",
|
|
map.toUtf8().data(),
|
|
mapset.toUtf8().data() ) != NULL )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot read region" ) );
|
|
return false;
|
|
}
|
|
#else
|
|
// TODO7: unfortunately G__get_window does not return error code and calls G_fatal_error on error
|
|
G__get_window( window, ( char * ) "windows", map.toUtf8().data(), mapset.toUtf8().data() );
|
|
#endif
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QProcess GRASS_LIB_EXPORT *QgsGrass::startModule( QString gisdbase, QString location,
|
|
QString module, QStringList arguments, QTemporaryFile &gisrcFile )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
QProcess *process = new QProcess();
|
|
|
|
module += QString::number( QgsGrass::versionMajor() );
|
|
#ifdef Q_OS_WIN
|
|
module += ".exe";
|
|
#endif
|
|
|
|
// We have to set GISRC file, uff
|
|
if ( !gisrcFile.open() )
|
|
{
|
|
throw QgsGrass::Exception( QObject::tr( "Cannot open GISRC file" ) );
|
|
}
|
|
|
|
QTextStream out( &gisrcFile );
|
|
out << "GISDBASE: " << gisdbase << "\n";
|
|
out << "LOCATION_NAME: " << location << "\n";
|
|
out << "MAPSET: PERMANENT\n";
|
|
out.flush();
|
|
QgsDebugMsg( gisrcFile.fileName() );
|
|
gisrcFile.close();
|
|
|
|
QStringList environment = QProcess::systemEnvironment();
|
|
environment.append( "GISRC=" + gisrcFile.fileName() );
|
|
|
|
process->setEnvironment( environment );
|
|
|
|
QgsDebugMsg( module + " " + arguments.join( " " ) );
|
|
process->start( module, arguments );
|
|
if ( !process->waitForStarted() )
|
|
{
|
|
throw QgsGrass::Exception( QObject::tr( "Cannot start module" ) + "\n"
|
|
+ QObject::tr( "command: %1 %2" )
|
|
.arg( module ).arg( arguments.join( " " ) ) );
|
|
}
|
|
return process;
|
|
}
|
|
|
|
QByteArray GRASS_LIB_EXPORT QgsGrass::runModule( QString gisdbase, QString location,
|
|
QString module, QStringList arguments, int timeOut )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2 timeOut = %3" ).arg( gisdbase ).arg( location ).arg( timeOut ) );
|
|
|
|
QTemporaryFile gisrcFile;
|
|
QProcess *process = QgsGrass::startModule( gisdbase, location, module, arguments, gisrcFile );
|
|
|
|
if ( !process->waitForFinished( timeOut )
|
|
|| ( process->exitCode() != 0 && process->exitCode() != 255 ) )
|
|
{
|
|
QgsDebugMsg( "process->exitCode() = " + QString::number( process->exitCode() ) );
|
|
|
|
throw QgsGrass::Exception( QObject::tr( "Cannot run module" ) + "\n"
|
|
+ QObject::tr( "command: %1 %2<br>%3<br>%4" )
|
|
.arg( module ).arg( arguments.join( " " ) )
|
|
.arg( process->readAllStandardOutput().constData() )
|
|
.arg( process->readAllStandardError().constData() ) );
|
|
}
|
|
QByteArray data = process->readAllStandardOutput();
|
|
delete process;
|
|
return data;
|
|
}
|
|
|
|
QString GRASS_LIB_EXPORT QgsGrass::getInfo( QString info, QString gisdbase, QString location,
|
|
QString mapset, QString map, MapType type, double x, double y, QgsRectangle extent, int sampleRows, int sampleCols, int timeOut )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
|
|
QStringList arguments;
|
|
|
|
QString cmd = QgsApplication::libexecPath() + "grass/modules/qgis.g.info";
|
|
|
|
arguments.append( "info=" + info );
|
|
if ( !map.isEmpty() )
|
|
{
|
|
QString opt;
|
|
switch ( type )
|
|
{
|
|
case Raster:
|
|
opt = "rast";
|
|
break;
|
|
case Vector:
|
|
opt = "vect";
|
|
break;
|
|
default:
|
|
QgsDebugMsg( QString( "unexpected type:%1" ).arg( type ) );
|
|
return "";
|
|
}
|
|
arguments.append( opt + "=" + map + "@" + mapset );
|
|
}
|
|
if ( info == "query" )
|
|
{
|
|
arguments.append( QString( "coor=%1,%2" ).arg( x ).arg( y ) );
|
|
}
|
|
if ( info == "stats" )
|
|
{
|
|
arguments.append( QString( "north=%1" ).arg( extent.yMaximum() ) );
|
|
arguments.append( QString( "south=%1" ).arg( extent.yMinimum() ) );
|
|
arguments.append( QString( "east=%1" ).arg( extent.xMaximum() ) );
|
|
arguments.append( QString( "west=%1" ).arg( extent.xMinimum() ) );
|
|
arguments.append( QString( "rows=%1" ).arg( sampleRows ) );
|
|
arguments.append( QString( "cols=%1" ).arg( sampleCols ) );
|
|
}
|
|
|
|
QByteArray data = QgsGrass::runModule( gisdbase, location, cmd, arguments, timeOut );
|
|
QgsDebugMsg( data );
|
|
return QString( data );
|
|
}
|
|
|
|
QgsCoordinateReferenceSystem GRASS_LIB_EXPORT QgsGrass::crs( QString gisdbase, QString location )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem();
|
|
try
|
|
{
|
|
QString wkt = QgsGrass::getInfo( "proj", gisdbase, location );
|
|
QgsDebugMsg( "wkt: " + wkt );
|
|
crs.createFromWkt( wkt );
|
|
QgsDebugMsg( "crs.toWkt: " + crs.toWkt() );
|
|
}
|
|
catch ( QgsGrass::Exception &e )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot get projection " ) + "\n" + e.what() );
|
|
}
|
|
|
|
return crs;
|
|
}
|
|
|
|
QgsCoordinateReferenceSystem GRASS_LIB_EXPORT QgsGrass::crsDirect( QString gisdbase, QString location )
|
|
{
|
|
QString Wkt;
|
|
|
|
struct Cell_head cellhd;
|
|
|
|
QgsGrass::resetError();
|
|
QgsGrass::setLocation( gisdbase, location );
|
|
|
|
const char *oldlocale = setlocale( LC_NUMERIC, NULL );
|
|
setlocale( LC_NUMERIC, "C" );
|
|
|
|
G_TRY
|
|
{
|
|
G_get_default_window( &cellhd );
|
|
}
|
|
G_CATCH( QgsGrass::Exception &e )
|
|
{
|
|
Q_UNUSED( e );
|
|
setlocale( LC_NUMERIC, oldlocale );
|
|
QgsDebugMsg( QString( "Cannot get default window: %1" ).arg( e.what() ) );
|
|
return QgsCoordinateReferenceSystem();
|
|
}
|
|
|
|
if ( cellhd.proj != PROJECTION_XY )
|
|
{
|
|
struct Key_Value *projinfo = G_get_projinfo();
|
|
struct Key_Value *projunits = G_get_projunits();
|
|
char *wkt = GPJ_grass_to_wkt( projinfo, projunits, 0, 0 );
|
|
Wkt = QString( wkt );
|
|
G_free( wkt );
|
|
}
|
|
|
|
setlocale( LC_NUMERIC, oldlocale );
|
|
|
|
QgsCoordinateReferenceSystem srs;
|
|
srs.createFromWkt( Wkt );
|
|
|
|
return srs;
|
|
}
|
|
|
|
QgsRectangle GRASS_LIB_EXPORT QgsGrass::extent( QString gisdbase, QString location, QString mapset, QString map, MapType type )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
|
|
try
|
|
{
|
|
QString str = QgsGrass::getInfo( "window", gisdbase, location, mapset, map, type );
|
|
QStringList list = str.split( "," );
|
|
if ( list.size() != 4 )
|
|
{
|
|
throw QgsGrass::Exception( "Cannot parse GRASS map extent: " + str );
|
|
}
|
|
return QgsRectangle( list[0].toDouble(), list[1].toDouble(), list[2].toDouble(), list[3].toDouble() );
|
|
}
|
|
catch ( QgsGrass::Exception &e )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot get raster extent" ) + "\n" + e.what() );
|
|
}
|
|
return QgsRectangle( 0, 0, 0, 0 );
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::size( QString gisdbase, QString location, QString mapset, QString map, int *cols, int *rows )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
|
|
*cols = 0;
|
|
*rows = 0;
|
|
try
|
|
{
|
|
QString str = QgsGrass::getInfo( "size", gisdbase, location, mapset, map, QgsGrass::Raster );
|
|
QStringList list = str.split( "," );
|
|
if ( list.size() != 2 )
|
|
{
|
|
throw QgsGrass::Exception( "Cannot parse GRASS map size: " + str );
|
|
}
|
|
*cols = list[0].toInt();
|
|
*rows = list[1].toInt();
|
|
}
|
|
catch ( QgsGrass::Exception &e )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot get raster extent" ) + "\n" + e.what() );
|
|
}
|
|
|
|
QgsDebugMsg( QString( "raster size = %1 %2" ).arg( *cols ).arg( *rows ) );
|
|
}
|
|
|
|
QHash<QString, QString> GRASS_LIB_EXPORT QgsGrass::info( QString gisdbase, QString location, QString mapset, QString map, MapType type, QString info, QgsRectangle extent, int sampleRows, int sampleCols, int timeOut )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
QHash<QString, QString> inf;
|
|
|
|
try
|
|
{
|
|
//QString str = QgsGrass::getInfo( "info", gisdbase, location, mapset, map, type );
|
|
QString str = QgsGrass::getInfo( info, gisdbase, location, mapset, map, type, 0, 0, extent, sampleRows, sampleCols, timeOut );
|
|
QgsDebugMsg( str );
|
|
QStringList list = str.split( "\n" );
|
|
for ( int i = 0; i < list.size(); i++ )
|
|
{
|
|
QStringList keyVal = list[i].split( ':' );
|
|
if ( list[i].isEmpty() )
|
|
continue;
|
|
if ( keyVal.size() != 2 )
|
|
{
|
|
throw QgsGrass::Exception( "Cannot parse GRASS map info key value : " + list[i] + " (" + str + " ) " );
|
|
}
|
|
inf[keyVal[0]] = keyVal[1];
|
|
}
|
|
}
|
|
catch ( QgsGrass::Exception &e )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot get map info" ) + "\n" + e.what() );
|
|
}
|
|
return inf;
|
|
}
|
|
|
|
QList<QgsGrass::Color> GRASS_LIB_EXPORT QgsGrass::colors( QString gisdbase, QString location, QString mapset, QString map )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
QList<QgsGrass::Color> ct;
|
|
|
|
try
|
|
{
|
|
QString str = QgsGrass::getInfo( "colors", gisdbase, location, mapset, map, QgsGrass::Raster );
|
|
QgsDebugMsg( str );
|
|
QStringList list = str.split( "\n" );
|
|
for ( int i = 0; i < list.size(); i++ )
|
|
{
|
|
QgsGrass::Color c;
|
|
if ( list[i].isEmpty() )
|
|
continue;
|
|
if ( sscanf( list[i].toUtf8().data(), "%lf %lf %d %d %d %d %d %d", &( c.value1 ), &( c.value2 ), &( c.red1 ), &( c.green1 ), &( c.blue1 ), &( c.red2 ), &( c.green2 ), &( c.blue2 ) ) != 8 )
|
|
{
|
|
throw QgsGrass::Exception( "Cannot parse GRASS colors" + list[i] + " (" + str + " ) " );
|
|
}
|
|
ct.append( c );
|
|
}
|
|
}
|
|
catch ( QgsGrass::Exception &e )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot get colors" ) + "\n" + e.what() );
|
|
}
|
|
return ct;
|
|
}
|
|
|
|
QMap<QString, QString> GRASS_LIB_EXPORT QgsGrass::query( QString gisdbase, QString location, QString mapset, QString map, MapType type, double x, double y )
|
|
{
|
|
QgsDebugMsg( QString( "gisdbase = %1 location = %2" ).arg( gisdbase ).arg( location ) );
|
|
|
|
QMap<QString, QString> result;
|
|
// TODO: multiple values (more rows)
|
|
try
|
|
{
|
|
QString str = QgsGrass::getInfo( "query", gisdbase, location, mapset, map, type, x, y );
|
|
QStringList list = str.trimmed().split( ":" );
|
|
if ( list.size() == 2 )
|
|
{
|
|
result[list[0]] = list[1];
|
|
}
|
|
}
|
|
catch ( QgsGrass::Exception &e )
|
|
{
|
|
QMessageBox::warning( 0, QObject::tr( "Warning" ),
|
|
QObject::tr( "Cannot query raster " ) + "\n" + e.what() );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// GRASS version constants have been changed on 26.4.2007
|
|
// http://freegis.org/cgi-bin/viewcvs.cgi/grass6/include/version.h.in.diff?r1=1.4&r2=1.5
|
|
// The following lines workaround this change
|
|
|
|
int GRASS_LIB_EXPORT QgsGrass::versionMajor()
|
|
{
|
|
#ifdef GRASS_VERSION_MAJOR
|
|
return GRASS_VERSION_MAJOR;
|
|
#else
|
|
return QString( GRASS_VERSION_MAJOR ).toInt();
|
|
#endif
|
|
}
|
|
|
|
int GRASS_LIB_EXPORT QgsGrass::versionMinor()
|
|
{
|
|
#ifdef GRASS_VERSION_MINOR
|
|
return GRASS_VERSION_MINOR;
|
|
#else
|
|
return QString( GRASS_VERSION_MINOR ).toInt();
|
|
#endif
|
|
}
|
|
|
|
int GRASS_LIB_EXPORT QgsGrass::versionRelease()
|
|
{
|
|
#ifdef GRASS_VERSION_RELEASE
|
|
#define QUOTE(x) #x
|
|
return QString( QUOTE( GRASS_VERSION_RELEASE ) ).toInt();
|
|
#else
|
|
return QString( GRASS_VERSION_RELEASE ).toInt();
|
|
#endif
|
|
}
|
|
QString GRASS_LIB_EXPORT QgsGrass::versionString()
|
|
{
|
|
return QString( GRASS_VERSION_STRING );
|
|
}
|
|
|
|
bool GRASS_LIB_EXPORT QgsGrass::isMapset( QString path )
|
|
{
|
|
#if 0
|
|
/* TODO: G_is_mapset() was added to GRASS 6.1 06-05-24,
|
|
enable its use after some period (others do update) */
|
|
|
|
if ( QgsGrass::versionMajor() > 6 || QgsGrass::versionMinor() > 0 )
|
|
{
|
|
if ( G_is_mapset( path.toUtf8().constData() ) )
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
QString windf = path + "/WIND";
|
|
if ( QFile::exists( windf ) )
|
|
return true;
|
|
#if 0
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
QString GRASS_LIB_EXPORT QgsGrass::lockFilePath()
|
|
{
|
|
return mMapsetLock;
|
|
}
|
|
|
|
QString GRASS_LIB_EXPORT QgsGrass::gisrcFilePath()
|
|
{
|
|
if ( mGisrc.isEmpty() )
|
|
{
|
|
// Started from GRASS shell
|
|
if ( getenv( "GISRC" ) )
|
|
{
|
|
return QString( getenv( "GISRC" ) );
|
|
}
|
|
}
|
|
return mGisrc;
|
|
}
|
|
|
|
void GRASS_LIB_EXPORT QgsGrass::putEnv( QString name, QString value )
|
|
{
|
|
QString env = name + "=" + value;
|
|
/* _Correct_ putenv() implementation is not making copy! */
|
|
char *envChar = new char[env.toUtf8().length()+1];
|
|
strcpy( envChar, env.toUtf8().constData() );
|
|
putenv( envChar );
|
|
}
|