📄 qgsgrass.cpp
字号:
/*************************************************************************** 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. * * * ***************************************************************************//* $Id: qgsgrass.cpp 8350 2008-04-14 21:02:22Z jef $ */#include <iostream>#include "QString"#include "q3process.h"#include "QFile"#include "QFileInfo"#include "QFileDialog"#include "QDir"#include "QTextStream"#include "QSettings"#include <QMessageBox>#include <QCoreApplication>#include <QProcess>#include "qgsapplication.h"#include "qgsgrass.h"extern "C" {#ifndef _MSC_VER#include <unistd.h>#endif#include <grass/gis.h>#include <grass/Vect.h>#include <grass/version.h>}#if defined(WIN32)#include <windows.h>static QString getShortPath(const QString &path){ TCHAR buf[MAX_PATH]; GetShortPathName( path.ascii(), buf, MAX_PATH); return buf;}#endifvoid GRASS_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++"); // 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. QString gisBase = getenv("GISBASE");#ifdef QGISDEBUG qDebug( "%s:%d GRASS gisBase from GISBASE env var is: %s", __FILE__, __LINE__, (const char*)gisBase );#endif if ( !isValidGrassBaseDir(gisBase) ) { // Look for gisbase in QSettings gisBase = settings.readEntry("/GRASS/gisbase", "");#ifdef QGISDEBUG qDebug( "%s:%d GRASS gisBase from QSettings is: %s", __FILE__, __LINE__, (const char*)gisBase );#endif } if ( !isValidGrassBaseDir(gisBase) ) { // Erase gisbase from settings because it does not exists settings.writeEntry("/GRASS/gisbase", "");#ifdef WIN32 // Use the applicationDirPath()/grass gisBase = getShortPath( QCoreApplication::applicationDirPath() + "/grass" );#ifdef QGISDEBUG std::cerr << "GRASS gisBase = " << gisBase.ascii() << std::endl;#endif#else // Use the location specified --with-grass during configure gisBase = GRASS_BASE;#ifdef QGISDEBUG qDebug( "%s:%d GRASS gisBase from configure is: %s", __FILE__, __LINE__, (const char*)gisBase );#endif#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; }#if defined(WIN32) gisBase = getShortPath(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.writeEntry("/GRASS/gisbase", gisBase); }#ifdef QGISDEBUG qDebug( "%s:%d Valid GRASS gisBase is: %s", __FILE__, __LINE__, (const char*)gisBase );#endif QString gisBaseEnv = "GISBASE=" + gisBase; /* _Correct_ putenv() implementation is not making copy! */ char *gisBaseEnvChar = new char[gisBaseEnv.length()+1]; strcpy ( gisBaseEnvChar, const_cast<char *>(gisBaseEnv.ascii()) ); putenv( gisBaseEnvChar ); // Add path to GRASS modules#ifdef WIN32 QString sep = ";";#else QString sep = ":";#endif QString path = "PATH=" + gisBase + "/bin"; path.append ( sep + gisBase + "/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 WIN32 // It seems that QgsApplication::prefixPath() // is not initialized at this point path.append ( sep + getShortPath(QCoreApplication::applicationDirPath()) ); // Add path to MSYS bin // Warning: MSYS sh.exe will translate this path to '/bin' path.append ( sep + getShortPath(QCoreApplication::applicationDirPath() + "/msys/bin/") );#endif QString p = getenv ("PATH"); path.append ( sep + p );#ifdef QGISDEBUG std::cerr << "set PATH: " << path.toLocal8Bit().data() << std::endl;#endif char *pathEnvChar = new char[path.length()+1]; strcpy ( pathEnvChar, const_cast<char *>(path.ascii()) ); putenv( pathEnvChar ); // 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 ) { pager.prepend ( "GRASS_PAGER=" ); char *pagerEnvChar = new char[pager.length()+1]; strcpy ( pagerEnvChar, const_cast<char *>(pager.ascii()) ); putenv( pagerEnvChar ); } } initialized = 1;}/* * Check if given directory contains a GRASS installation */bool QgsGrass::isValidGrassBaseDir(QString const gisBase){#ifdef QGISDEBUG std::cerr << "isValidGrassBaseDir()" << std::endl;#endif // 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 ( QgsGrass::versionMajor() > 6 || QgsGrass::versionMinor() > 0 ) { if ( G_is_gisbase( gisBase.toLocal8Bit().constData() ) ) return TRUE; } else { */ QFileInfo gbi ( gisBase + "/etc/element_list" ); if ( gbi.exists() ) return TRUE; //} 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 ){#ifdef QGISDEBUG std::cerr << "QgsGrass::setLocation(): gisdbase = " << gisdbase.toLocal8Bit().data() << " location = " << location.toLocal8Bit().data() << std::endl;#endif init(); // Set principal GRASS variables (in memory)#if defined(WIN32) G__setenv( (char *)"GISDBASE", (char *) getShortPath(gisdbase).ascii() );#else G__setenv( (char *)"GISDBASE", (char *) gisdbase.ascii() );#endif G__setenv( (char *)"LOCATION_NAME", (char *) location.ascii() ); G__setenv( (char *)"MAPSET", (char *)"PERMANENT"); // PERMANENT must always exist // Add all available mapsets to search path char **ms = G_available_mapsets(); for ( int i = 0; ms[i]; i++ ) G_add_mapset_to_search_path ( ms[i] );}void QgsGrass::setMapset( QString gisdbase, QString location, QString mapset ){#ifdef QGISDEBUG std::cerr << "QgsGrass::setLocation(): gisdbase = " << gisdbase.toLocal8Bit().data() << " location = " << location.toLocal8Bit().data() << " mapset = " << mapset.toLocal8Bit().data() << std::endl;#endif init(); // Set principal GRASS variables (in memory)#if defined(WIN32) G__setenv( (char *)"GISDBASE", (char *) getShortPath(gisdbase).ascii() );#else G__setenv( (char *)"GISDBASE", (char *) gisdbase.ascii() );#endif G__setenv( (char *)"LOCATION_NAME", (char *) location.ascii() ); G__setenv( (char *)"MAPSET", (char *) mapset.ascii() ); // Add all available mapsets to search path char **ms = G_available_mapsets(); for ( int i = 0; ms[i]; i++ ) G_add_mapset_to_search_path ( ms[i] );}int QgsGrass::initialized = 0;bool QgsGrass::active = 0;QgsGrass::GERROR QgsGrass::error = QgsGrass::OK;QString QgsGrass::error_message;QString QgsGrass::defaultGisdbase;QString QgsGrass::defaultLocation;QString QgsGrass::defaultMapset;QString QgsGrass::mMapsetLock;QString QgsGrass::mGisrc;QString QgsGrass::mTmp;jmp_buf QgsGrass::mFatalErrorEnv;int QgsGrass::error_routine ( char *msg, int fatal){ return error_routine((const char*) msg, fatal);}int QgsGrass::error_routine ( const char *msg, int fatal){ std::cerr << "error_routine (fatal = " << fatal << "): " << msg << std::endl; error_message = msg; if ( fatal ) { error = FATAL; // we have to do a long jump here, otherwise GRASS >= 6.3 will kill our process longjmp(mFatalErrorEnv, 1); } else error = WARNING; return 1;}void GRASS_EXPORT QgsGrass::resetError ( void ){ error = OK;}int GRASS_EXPORT QgsGrass::getError ( void ){ return error;}QString GRASS_EXPORT QgsGrass::getErrorMessage ( void ){ return error_message;}jmp_buf GRASS_EXPORT &QgsGrass::fatalErrorEnv(){ return mFatalErrorEnv;}QString GRASS_EXPORT QgsGrass::openMapset ( QString gisdbase, QString location, QString mapset ){#ifdef QGISDEBUG std::cerr << "QgsGrass::openMapset" << std::endl; std::cerr << "gisdbase = " << gisdbase.local8Bit().data() << std::endl; std::cerr << "location = " << location.local8Bit().data() << std::endl; std::cerr << "mapset = " << mapset.local8Bit().data() << std::endl;#endif QString mapsetPath = gisdbase + "/" + location + "/" + mapset; // Check if the mapset is in use QString gisBase = getenv("GISBASE"); if ( gisBase.isNull() ) return QObject::tr("GISBASE is not set."); QFileInfo fi( mapsetPath+ "/WIND" ); if ( !fi.exists() ) { return mapsetPath + QObject::tr(" is not a GRASS mapset."); } QString lock = mapsetPath + "/.gislock"; QFile lockFile ( lock ); Q3Process *process = new Q3Process(); process->addArgument ( gisBase + "/etc/lock" ); // lock program process->addArgument ( lock ); // lock file // TODO: getpid() probably is not portable#ifndef _MSC_VER int pid = getpid();#else int pid = GetCurrentProcessId();#endif#ifdef QGISDEBUG std::cerr << "pid = " << pid << std::endl;#endif process->addArgument ( QString::number(pid) ); if ( !process->start() ) { return QObject::tr("Cannot start ") + gisBase + "/etc/lock"; } // TODO better wait while ( process->isRunning () ) { }#ifndef WIN32 int status = process->exitStatus ();#ifdef QGISDEBUG std::cerr << "status = " << status << std::endl;#endif#endif delete process; // TODO WIN32 (lock.exe does not work properly?)#ifndef WIN32 if ( status > 0 ) return QObject::tr("Mapset is already in use.");#endif // 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() ) { lockFile.remove(); return QObject::tr("Temporary directory ") + mTmp + QObject::tr(" exist but is not writable"); } } else if ( !dir.mkdir( mTmp ) ) { lockFile.remove(); return QObject::tr("Cannot create temporary directory ") + mTmp; } // Create GISRC file QString globalGisrc = QDir::home().path() + "/.grassrc6"; mGisrc = mTmp + "/gisrc";#ifdef QGISDEBUG std::cerr << "globalGisrc = " << globalGisrc.local8Bit().data() << std::endl; std::cerr << "mGisrc = " << mGisrc.local8Bit().data() << std::endl;#endif QFile out ( mGisrc ); if ( !out.open( QIODevice::WriteOnly ) ) { lockFile.remove(); return QObject::tr("Cannot create ") + mGisrc; } QTextStream stream ( &out ); QFile in ( globalGisrc ); QString line; 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; } stream << line; } in.close(); } line = "GISDBASE: " + gisdbase + "\n"; stream << line; line = "LOCATION_NAME: " + location + "\n"; stream << line; line = "MAPSET: " + mapset + "\n"; stream << line; out.close(); // Set GISRC enviroment variable
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -