Skip to content

Commit

Permalink
[GRASS][FEATURE] vector import via browser drag and drop
Browse files Browse the repository at this point in the history
blazek committed May 18, 2015
1 parent e474e09 commit 7091d3b
Showing 10 changed files with 881 additions and 91 deletions.
24 changes: 23 additions & 1 deletion src/providers/grass/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -172,6 +172,24 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION)
)
ENDIF (GRASS_MAJOR_VERSION LESS 7 )

#
# grass vector import module
#
ADD_EXECUTABLE(qgis.v.in${GRASS_BUILD_VERSION} ../qgis.v.in.cpp)
SET_TARGET_PROPERTIES(qgis.v.in${GRASS_BUILD_VERSION} PROPERTIES
COMPILE_FLAGS "-DGRASS_BASE=\\\"${GRASS_PREFIX}\\\" \"-DGRASS_LIB_EXPORT=${DLLEXPORT}\" \"-DGRASS_EXPORT=${DLLIMPORT}\""
)
TARGET_LINK_LIBRARIES(qgis.v.in${GRASS_BUILD_VERSION}
qgisgrass${GRASS_BUILD_VERSION}
${GRASS_LIBRARY${GRASS_BUILD_VERSION}_gis}
${GRASS_LIBRARY${GRASS_BUILD_VERSION}_datetime}
${GRASS_LIBRARY${GRASS_BUILD_VERSION}_vect}
${GRASS_LIBRARY${GRASS_BUILD_VERSION}_dbmibase}
${GRASS_LIBRARY${GRASS_BUILD_VERSION}_dbmiclient}
${GDAL_LIBRARY}
qgis_core
)

########################################################
# Install

@@ -188,7 +206,11 @@ MACRO(ADD_GRASSLIB GRASS_BUILD_VERSION)
RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
LIBRARY DESTINATION ${QGIS_PLUGIN_DIR})

INSTALL(TARGETS qgis.d.rast${GRASS_BUILD_VERSION} qgis.g.info${GRASS_BUILD_VERSION} qgis.r.in${GRASS_BUILD_VERSION}
INSTALL(TARGETS
qgis.d.rast${GRASS_BUILD_VERSION}
qgis.g.info${GRASS_BUILD_VERSION}
qgis.r.in${GRASS_BUILD_VERSION}
qgis.v.in${GRASS_BUILD_VERSION}
RUNTIME DESTINATION ${QGIS_LIBEXEC_DIR}/grass/modules
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
13 changes: 1 addition & 12 deletions src/providers/grass/qgis.r.in.cpp
Original file line number Diff line number Diff line change
@@ -43,11 +43,6 @@ extern "C"
#include "qgsrasterblock.h"
#include "qgsgrass.h"

//#ifdef _MSC_VER
//#define INFINITY (DBL_MAX+DBL_MAX)
//#define NAN (INFINITY-INFINITY)
//#endif

#if GRASS_VERSION_MAJOR >= 7
#define G_allocate_raster_buf Rast_allocate_buf
#define G_close_cell Rast_close
@@ -68,15 +63,13 @@ extern "C"
int main( int argc, char **argv )
{
char *name;
struct GModule *module;
struct Option *map;
struct Cell_head window;
int cf;

G_gisinit( argv[0] );

module = G_define_module();
module->description = ( "Output raster map layers in a format suitable for display in QGIS" );
G_define_module();

map = G_define_standard_option( G_OPT_R_OUTPUT );

@@ -94,9 +87,6 @@ int main( int argc, char **argv )
qint32 rows, cols;
stdinStream >> extent >> cols >> rows;

//G_fatal_error("i = %d", i);
//G_fatal_error( extent.toString().toAscii().data() );

QString err = QgsGrass::setRegion( &window, extent, rows, cols );
if ( !err.isEmpty() )
{
@@ -107,7 +97,6 @@ int main( int argc, char **argv )

QGis::DataType qgis_type;
qint32 type;
//stdinStream >> grass_type;
stdinStream >> type;
qgis_type = ( QGis::DataType )type;

331 changes: 331 additions & 0 deletions src/providers/grass/qgis.v.in.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
/***************************************************************************
qgis.v.in.cpp
---------------------
begin : May 2015
copyright : (C) 2015 by Radim Blazek
email : radim dot blazek at gmail dot 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. *
* *
***************************************************************************/

extern "C"
{
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#ifdef WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include <grass/version.h>
#include <grass/gis.h>
#include <grass/dbmi.h>

#if GRASS_VERSION_MAJOR < 7
#include <grass/Vect.h>
#else
#include <grass/vector.h>
#endif
}

#include <QByteArray>
#include <QDataStream>
#include <QFile>
#include <QIODevice>

#include "qgsfeature.h"
#include "qgsgeometry.h"
#include "qgsrectangle.h"
#include "qgsrasterblock.h"
#include "qgsspatialindex.h"
#include "qgsgrass.h"

static struct line_pnts *line = Vect_new_line_struct();

void writePoint( struct Map_info* map, int type, QgsPoint point, struct line_cats *cats )
{
Vect_reset_line( line );
Vect_append_point( line, point.x(), point.y(), 0 );
Vect_write_line( map, type, line, cats );
}

void writePolyline( struct Map_info* map, int type, QgsPolyline polyline, struct line_cats *cats )
{
Vect_reset_line( line );
foreach ( QgsPoint point, polyline )
{
Vect_append_point( line, point.x(), point.y(), 0 );
}
Vect_write_line( map, type, line, cats );
}

int main( int argc, char **argv )
{
struct Option *mapOption;

G_gisinit( argv[0] );
G_define_module();
mapOption = G_define_standard_option( G_OPT_V_OUTPUT );

if ( G_parser( argc, argv ) )
exit( EXIT_FAILURE );

QFile stdinFile;
stdinFile.open( 0, QIODevice::ReadOnly );
QDataStream stdinStream( &stdinFile );

QFile stdoutFile;
stdoutFile.open( 0, QIODevice::ReadOnly );
QDataStream stdoutStream( &stdoutFile );

qint32 typeQint32;
stdinStream >> typeQint32;
QGis::WkbType wkbType = ( QGis::WkbType )typeQint32;
QGis::WkbType wkbFlatType = QGis::flatType( wkbType );
bool isPolygon = QGis::singleType( wkbFlatType ) == QGis::WKBPolygon;

struct Map_info finalMap, tmpMap;
Vect_open_new( &finalMap, mapOption->answer, 0 );
struct Map_info * map = &finalMap;
QDateTime now = QDateTime::currentDateTime();
QString tmpName = QString( "%1_tmp_%2" ).arg( mapOption->answer ).arg( now.toString( "yyyyMMddhhmmss" ) );
if ( isPolygon )
{
Vect_open_new( &tmpMap, tmpName.toUtf8().data(), 0 );
map = &tmpMap;
}

QgsFields srcFields;
stdinStream >> srcFields;
// TODO: find (in QgsGrassVectorImport) if there is unique 'id' or 'cat' field and use it as cat
int keyNum = 1;
QString key;
while ( true )
{
key = "cat" + ( keyNum == 1 ? "" : QString::number( keyNum ) );
if ( srcFields.indexFromName( key ) == -1 )
{
break;
}
keyNum++;
}

QgsFields fields;
fields.append( QgsField( key, QVariant::Int ) );
fields.extend( srcFields );

struct field_info *fieldInfo = Vect_default_field_info( &finalMap, 1, NULL, GV_1TABLE );
if ( Vect_map_add_dblink( &finalMap, 1, NULL, fieldInfo->table, key.toLatin1().data(),
fieldInfo->database, fieldInfo->driver ) != 0 )
{
G_fatal_error( "Cannot add link" );
}

dbDriver *driver = db_start_driver_open_database( fieldInfo->driver, fieldInfo->database );
if ( !driver )
{
G_fatal_error( "Cannot open database %s by driver %s", fieldInfo->database, fieldInfo->driver );
}
try
{
QgsGrass::createTable( driver, QString( fieldInfo->table ), fields );
}
catch ( QgsGrass::Exception &e )
{
G_fatal_error( "Cannot create table: %s", e.what() );
}

QgsFeature feature;
struct line_cats *cats = Vect_new_cats_struct();

qint32 featureCount = 0;
while ( true )
{
stdinStream >> feature;
if ( !feature.isValid() )
{
break;
}

QgsGeometry* geometry = feature.geometry();
if ( geometry )
{
// geometry type may be probably different from provider type (e.g. multi x single)
QGis::WkbType geometryType = QGis::flatType( geometry->wkbType() );
if ( !isPolygon )
{
Vect_reset_cats( cats );
Vect_cat_set( cats, 1, ( int )feature.id() );
}

if ( geometryType == QGis::WKBPoint )
{
QgsPoint point = geometry->asPoint();
writePoint( map, GV_POINT, point, cats );
}
else if ( geometryType == QGis::WKBMultiPoint )
{
QgsMultiPoint multiPoint = geometry->asMultiPoint();
foreach ( QgsPoint point, multiPoint )
{
writePoint( map, GV_POINT, point, cats );
}
}
else if ( geometryType == QGis::WKBLineString )
{
QgsPolyline polyline = geometry->asPolyline();
writePolyline( map, GV_LINE, polyline, cats );
}
else if ( geometryType == QGis::WKBMultiLineString )
{
QgsMultiPolyline multiPolyline = geometry->asMultiPolyline();
foreach ( QgsPolyline polyline, multiPolyline )
{
writePolyline( map, GV_LINE, polyline, cats );
}
}
else if ( geometryType == QGis::WKBPolygon )
{
QgsPolygon polygon = geometry->asPolygon();
foreach ( QgsPolyline polyline, polygon )
{
writePolyline( map, GV_BOUNDARY, polyline, cats );
}
}
else if ( geometryType == QGis::WKBMultiPolygon )
{
QgsMultiPolygon multiPolygon = geometry->asMultiPolygon();
foreach ( QgsPolygon polygon, multiPolygon )
{
foreach ( QgsPolyline polyline, polygon )
{
writePolyline( map, GV_BOUNDARY, polyline, cats );
}
}
}
else
{
G_fatal_error( "Geometry type not supported" );
}

QgsAttributes attributes = feature.attributes();
attributes.insert( 0, QVariant( feature.id() ) );
try
{
QgsGrass::insertRow( driver, QString( fieldInfo->table ), attributes );
}
catch ( QgsGrass::Exception &e )
{
G_fatal_error( "Cannot insert: %s", e.what() );
}
}
featureCount++;
}

if ( isPolygon )
{
double snapTreshold = 0;
Vect_build_partial( map, GV_BUILD_BASE );

if ( snapTreshold > 0 )
{
Vect_snap_lines( map, GV_BOUNDARY, snapTreshold, NULL );
}
Vect_break_polygons( map, GV_BOUNDARY, NULL );
Vect_remove_duplicates( map, GV_BOUNDARY | GV_CENTROID, NULL );
while ( true )
{
Vect_break_lines( map, GV_BOUNDARY, NULL );
Vect_remove_duplicates( map, GV_BOUNDARY, NULL );
if ( Vect_clean_small_angles_at_nodes( map, GV_BOUNDARY, NULL ) == 0 )
{
break;
}
}
Vect_merge_lines( map, GV_BOUNDARY, NULL, NULL );
#if GRASS_VERSION_MAJOR < 7
Vect_remove_bridges( map, NULL );
#else
int linesRemoved, bridgesRemoved;
Vect_remove_bridges( map, NULL, &linesRemoved, &bridgesRemoved );
#endif
Vect_build_partial( map, GV_BUILD_ATTACH_ISLES );

QMap<QgsFeatureId, QgsFeature> centroids;
QgsSpatialIndex spatialIndex;
int nAreas = Vect_get_num_areas( map );
for ( int area = 1; area <= nAreas; area++ )
{
double x, y;
if ( Vect_get_point_in_area( map, area, &x, &y ) < 0 )
{
// TODO: send warning
continue;
}
QgsPoint point( x, y );
QgsFeature feature( area );
feature.setGeometry( QgsGeometry::fromPoint( point ) );
feature.setValid( true );
centroids.insert( area, feature );
spatialIndex.insertFeature( feature );
}
// read once more to assign centroids to polygons
while ( true )
{
stdinStream >> feature;
if ( !feature.isValid() )
{
break;
}
if ( !feature.geometry() )
{
continue;
}

QList<QgsFeatureId> idList = spatialIndex.intersects( feature.geometry()->boundingBox() );
foreach ( QgsFeatureId id, idList )
{
QgsFeature& centroid = centroids[id];
if ( feature.geometry()->contains( centroid.geometry() ) )
{
centroid.attributes().append( feature.id() );
}
}
}

Vect_copy_map_lines( &tmpMap, &finalMap );
Vect_close( &tmpMap );
Vect_delete( tmpName.toUtf8().data() );

foreach ( QgsFeature centroid, centroids.values() )
{
QgsPoint point = centroid.geometry()->asPoint();

if ( centroid.attributes().size() > 0 )
{
Vect_reset_cats( cats );
foreach ( QVariant attribute, centroid.attributes() )
{
Vect_cat_set( cats, 1, attribute.toInt() );
}
writePoint( &finalMap, GV_CENTROID, point, cats );
}
}
}

db_close_database_shutdown_driver( driver );
Vect_build( &finalMap );
Vect_close( &finalMap );

stdoutStream << ( bool )true; // to keep caller waiting until finished
// TODO history

exit( EXIT_SUCCESS );
}
125 changes: 124 additions & 1 deletion src/providers/grass/qgsgrass.cpp
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
#include "qgslogger.h"
#include "qgsapplication.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsfield.h"
#include "qgsrectangle.h"
#include "qgsconfig.h"

@@ -1107,7 +1108,7 @@ bool GRASS_LIB_EXPORT QgsGrass::objectExists( const QgsGrassObject& grassObject
if ( grassObject.type() == QgsGrassObject::Raster )
path += "/cellhd";
else if ( grassObject.type() == QgsGrassObject::Vector )
path += "/vect";
path += "/vector";
else if ( grassObject.type() == QgsGrassObject::Region )
path += "/windows";

@@ -1791,6 +1792,128 @@ bool QgsGrass::deleteObjectDialog( const QgsGrassObject & object )
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes;
}

void QgsGrass::createTable( dbDriver *driver, const QString tableName, const QgsFields &fields )
{
if ( !driver ) // should not happen
{
throw QgsGrass::Exception( "driver is null" );
}

QStringList fieldsStringList;
for ( int i = 0; i < fields.size(); i++ )
{
QgsField field = fields.field( i );
QString name = field.name().toLower().replace( " ", "_" );
if ( name.at( 0 ).isDigit() )
{
name = "_" + name;
}
QString typeName;
switch ( field.type() )
{
case QVariant::Int:
case QVariant::LongLong:
case QVariant::Bool:
typeName = "integer";
break;
case QVariant::Double:
typeName = "double precision";
break;
// TODO: verify how is it with spatialite/dbf support for date, time, datetime, v.in.ogr is using all
case QVariant::Date:
typeName = "date";
break;
case QVariant::Time:
typeName = "time";
break;
case QVariant::DateTime:
typeName = "datetime";
break;
case QVariant::String:
typeName = QString( "varchar (%1)" ).arg( field.length() );
break;
default:
typeName = QString( "varchar (%1)" ).arg( field.length() > 0 ? field.length() : 255 );
}
fieldsStringList << name + " " + typeName;
}
QString sql = QString( "create table %1 (%2);" ).arg( tableName ).arg( fieldsStringList.join( ", " ) );

dbString dbstr;
db_init_string( &dbstr );
db_set_string( &dbstr, sql.toLatin1().data() );

int result = db_execute_immediate( driver, &dbstr );
db_free_string( &dbstr );
if ( result != DB_OK )
{
throw QgsGrass::Exception( QObject::tr( "Cannot create table" ) + ": " + QString::fromLatin1( db_get_error_msg() ) );
}
}

void QgsGrass::insertRow( dbDriver *driver, const QString tableName,
const QgsAttributes& attributes )
{
if ( !driver ) // should not happen
{
throw QgsGrass::Exception( "driver is null" );
}

QStringList valuesStringList;
foreach ( QVariant attribute, attributes )
{
QString valueString;

bool quote = true;
switch ( attribute.type() )
{
case QVariant::Int:
case QVariant::Double:
case QVariant::LongLong:
valueString = attribute.toString();
quote = false;
break;
// TODO: use rbool according to driver
case QVariant::Bool:
valueString = attribute.toBool() ? "1" : "0";
quote = false;
break;
case QVariant::Date:
valueString = attribute.toDate().toString( Qt::ISODate );
break;
case QVariant::Time:
valueString = attribute.toTime().toString( Qt::ISODate );
break;
case QVariant::DateTime:
valueString = attribute.toDateTime().toString( Qt::ISODate );
break;
default:
valueString = attribute.toString();
}
valueString.replace( "'", "''" );

if ( quote )
{
valueString = "'" + valueString + "'";
}

valuesStringList << valueString;
}
QString sql = QString( "insert into %1 values (%2);" ).arg( tableName ).arg( valuesStringList.join( ", " ) );

dbString dbstr;
db_init_string( &dbstr );
db_set_string( &dbstr, sql.toLatin1().data() );

int result = db_execute_immediate( driver, &dbstr );
db_free_string( &dbstr );
if ( result != DB_OK )
{
throw QgsGrass::Exception( QObject::tr( "Cannot insert, statement" ) + ": " + sql
+ QObject::tr( "error" ) + ": " + QString::fromLatin1( db_get_error_msg() ) );
}
}

// 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
10 changes: 10 additions & 0 deletions src/providers/grass/qgsgrass.h
Original file line number Diff line number Diff line change
@@ -26,11 +26,14 @@ extern "C"
#include <grass/version.h>
#include <grass/gis.h>
#include <grass/form.h>
#include <grass/dbmi.h>
}

#include <stdexcept>
#include "qgsapplication.h"
#include "qgsexception.h"
#include "qgsfeature.h"
#include "qgsfield.h"
#include <qgsrectangle.h>
#include <QProcess>
#include <QString>
@@ -349,6 +352,13 @@ class QgsGrass
*/
static GRASS_LIB_EXPORT bool deleteObjectDialog( const QgsGrassObject & object );

/** Create new table. Throws QgsGrass::Exception */
static GRASS_LIB_EXPORT void createTable( dbDriver *driver, const QString tableName, const QgsFields &fields );

/** Insert row to table. Throws QgsGrass::Exception */
static GRASS_LIB_EXPORT void insertRow( dbDriver *driver, const QString tableName,
const QgsAttributes& attributes );

//! Library version
static GRASS_LIB_EXPORT int versionMajor();
static GRASS_LIB_EXPORT int versionMinor();
184 changes: 182 additions & 2 deletions src/providers/grass/qgsgrassimport.cpp
Original file line number Diff line number Diff line change
@@ -13,9 +13,16 @@
* (at your option) any later version. *
* *
***************************************************************************/
#include <unistd.h>

#include <QByteArray>
#include <QtConcurrentRun>

#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include "qgsfeature.h"
#include "qgsfeatureiterator.h"
#include "qgsgeometry.h"
#include "qgsproviderregistry.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasteriterator.h"
@@ -50,7 +57,6 @@ QString QgsGrassImport::error()
QgsGrassRasterImport::QgsGrassRasterImport( QgsRasterPipe* pipe, const QgsGrassObject& grassObject,
const QgsRectangle &extent, int xSize, int ySize )
: QgsGrassImport( grassObject )
//, mProvider( provider )
, mPipe( pipe )
, mExtent( extent )
, mXSize( xSize )
@@ -66,7 +72,6 @@ QgsGrassRasterImport::~QgsGrassRasterImport()
QgsDebugMsg( "mFutureWatcher not finished -> waitForFinished()" );
mFutureWatcher->waitForFinished();
}
//delete mProvider;
delete mPipe;
}

@@ -200,6 +205,8 @@ bool QgsGrassRasterImport::import()
delete block;
}

// TODO: send something back from module and read it here to close map correctly in module

process->closeWriteChannel();
process->waitForFinished( 5000 );

@@ -275,3 +282,176 @@ QStringList QgsGrassRasterImport::names() const
}
return list;
}

//------------------------------ QgsGrassVectorImport ------------------------------------
QgsGrassVectorImport::QgsGrassVectorImport( QgsVectorDataProvider* provider, const QgsGrassObject& grassObject )
: QgsGrassImport( grassObject )
, mProvider( provider )
, mFutureWatcher( 0 )
{
}

QgsGrassVectorImport::~QgsGrassVectorImport()
{
if ( mFutureWatcher && !mFutureWatcher->isFinished() )
{
QgsDebugMsg( "mFutureWatcher not finished -> waitForFinished()" );
mFutureWatcher->waitForFinished();
}
delete mProvider;
}

void QgsGrassVectorImport::importInThread()
{
QgsDebugMsg( "entered" );
mFutureWatcher = new QFutureWatcher<bool>( this );
connect( mFutureWatcher, SIGNAL( finished() ), SLOT( onFinished() ) );
mFutureWatcher->setFuture( QtConcurrent::run( run, this ) );
}

bool QgsGrassVectorImport::run( QgsGrassVectorImport *imp )
{
QgsDebugMsg( "entered" );
imp->import();
return true;
}

bool QgsGrassVectorImport::import()
{

QgsDebugMsg( "entered" );

if ( !mProvider )
{
setError( "Provider is null." );
return false;
}

if ( !mProvider->isValid() )
{
setError( "Provider is not valid." );
return false;
}

QgsCoordinateReferenceSystem providerCrs = mProvider->crs();
QgsCoordinateReferenceSystem mapsetCrs = QgsGrass::crsDirect( mGrassObject.gisdbase(), mGrassObject.location() );
QgsDebugMsg( "providerCrs = " + providerCrs.toWkt() );
QgsDebugMsg( "mapsetCrs = " + mapsetCrs.toWkt() );
QgsCoordinateTransform coordinateTransform;
bool doTransform = false;
if ( providerCrs.isValid() && mapsetCrs.isValid() && providerCrs != mapsetCrs )
{
coordinateTransform.setSourceCrs( providerCrs );
coordinateTransform.setDestCRS( mapsetCrs );
doTransform = true;
}

QString module = QgsGrass::qgisGrassModulePath() + "/qgis.v.in";
QStringList arguments;
QString name = mGrassObject.name();
arguments.append( "output=" + name );
QTemporaryFile gisrcFile;
QProcess* process = 0;
try
{
process = QgsGrass::startModule( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset(), module, arguments, gisrcFile );
}
catch ( QgsGrass::Exception &e )
{
setError( e.what() );
return false;
}

QDataStream outStream( process );

QGis::WkbType wkbType = mProvider->geometryType();
bool isPolygon = QGis::singleType( QGis::flatType( wkbType ) ) == QGis::WKBPolygon;
outStream << ( qint32 )wkbType;

outStream << mProvider->fields();

QgsFeatureIterator iterator = mProvider->getFeatures();
QgsFeature feature;
for ( int i = 0; i < ( isPolygon ? 2 : 1 ); i++ ) // two cycles with polygons
{
if ( i > 0 )
{
//iterator.rewind(); // rewind does not work
iterator = mProvider->getFeatures();
}
QgsDebugMsg( "send features" );
while ( iterator.nextFeature( feature ) )
{
if ( !feature.isValid() )
{
continue;
}
if ( doTransform && feature.geometry() )
{
feature.geometry()->transform( coordinateTransform );
}
outStream << feature;
}
feature = QgsFeature(); // indicate end by invalid feature
outStream << feature;
QgsDebugMsg( "features sent" );
}
iterator.close();

process->setReadChannel( QProcess::StandardOutput );

bool result;
outStream >> result;
QgsDebugMsg( QString( "result = %1" ).arg( result ) );

process->closeWriteChannel();
process->waitForFinished( 5000 );

QString stdoutString = process->readAllStandardOutput().data();
QString stderrString = process->readAllStandardError().data();

QString processResult = QString( "exitStatus=%1, exitCode=%2, errorCode=%3, error=%4 stdout=%5, stderr=%6" )
.arg( process->exitStatus() ).arg( process->exitCode() )
.arg( process->error() ).arg( process->errorString() )
.arg( stdoutString ).arg( stderrString );
QgsDebugMsg( "processResult: " + processResult );

if ( process->exitStatus() != QProcess::NormalExit )
{
setError( process->errorString() );
delete process;
return false;
}

if ( process->exitCode() != 0 )
{
setError( stderrString );
delete process;
return false;
}

delete process;
return true;
}

void QgsGrassVectorImport::onFinished()
{
QgsDebugMsg( "entered" );
emit finished( this );
}

QString QgsGrassVectorImport::uri() const
{
if ( !mProvider )
{
return "";
}
return mProvider->dataSourceUri();
}

QStringList QgsGrassVectorImport::names() const
{
QStringList list;
list << mGrassObject.name();
return list;
}
27 changes: 23 additions & 4 deletions src/providers/grass/qgsgrassimport.h
Original file line number Diff line number Diff line change
@@ -20,8 +20,8 @@
#include <QObject>

#include "qgslogger.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterpipe.h"
#include "qgsvectordataprovider.h"

#include "qgsgrass.h"

@@ -32,6 +32,7 @@ class GRASS_LIB_EXPORT QgsGrassImport : public QObject
QgsGrassImport( QgsGrassObject grassObject );
virtual ~QgsGrassImport() {}
QgsGrassObject grassObject() const { return mGrassObject; }
virtual void importInThread() = 0;
virtual QString uri() const = 0;
// get error if import failed
QString error();
@@ -52,12 +53,11 @@ class GRASS_LIB_EXPORT QgsGrassRasterImport : public QgsGrassImport
Q_OBJECT
public:
// takes pipe ownership
//QgsGrassRasterImport( QgsRasterDataProvider* provider, const QgsGrassObject& grassObject );
QgsGrassRasterImport( QgsRasterPipe* pipe, const QgsGrassObject& grassObject,
const QgsRectangle &extent, int xSize, int ySize );
~QgsGrassRasterImport();
bool import();
void importInThread();
void importInThread() override;
QString uri() const override;
// get list of extensions (for bands)
static QStringList extensions( QgsRasterDataProvider* provider );
@@ -67,12 +67,31 @@ class GRASS_LIB_EXPORT QgsGrassRasterImport : public QgsGrassImport
void onFinished();
private:
static bool run( QgsGrassRasterImport *imp );
//QgsRasterDataProvider* mProvider;
QgsRasterPipe* mPipe;
QgsRectangle mExtent;
int mXSize;
int mYSize;
QFutureWatcher<bool>* mFutureWatcher;
};

class QgsGrassVectorImport : public QgsGrassImport
{
Q_OBJECT
public:
// takes provider ownership
QgsGrassVectorImport( QgsVectorDataProvider* provider, const QgsGrassObject& grassObject );
~QgsGrassVectorImport();
bool import();
void importInThread() override;
QString uri() const override;
// get list of all output names
QStringList names() const override;
public slots:
void onFinished();
private:
static bool run( QgsGrassVectorImport *imp );
QgsVectorDataProvider* mProvider;
QFutureWatcher<bool>* mFutureWatcher;
};

#endif // QGSGRASSIMPORT_H
2 changes: 2 additions & 0 deletions src/providers/grass/qgsgrassprovider.cpp
Original file line number Diff line number Diff line change
@@ -1874,6 +1874,8 @@ QString QgsGrassProvider::createTable( int field, const QString &key, const QStr
{
QgsDebugMsg( QString( "field = %1" ).arg( field ) );

// TODO: use QgsGrass::createTable

// Read attributes
struct field_info *fi = Vect_get_field( mMap, field ); // should work also with field = 0
if ( fi != NULL )
160 changes: 101 additions & 59 deletions src/providers/grass/qgsgrassprovidermodule.cpp
Original file line number Diff line number Diff line change
@@ -196,60 +196,93 @@ bool QgsGrassMapsetItem::handleDrop( const QMimeData * data, Qt::DropAction )
QgsCoordinateReferenceSystem mapsetCrs = QgsGrass::crsDirect( mGisdbase, mLocation );

QStringList existingRasters = QgsGrass::rasters( mapsetObject.mapsetPath() );
QStringList existingVectors = QgsGrass::vectors( mapsetObject.mapsetPath() );
// add currently being imported
foreach ( QgsGrassImport* import, mImports )
{
if ( import && import->grassObject().type() == QgsGrassObject::Raster )
{
existingRasters.append( import->names() );
}
else if ( import && import->grassObject().type() == QgsGrassObject::Vector )
{
existingVectors.append( import->names() );
}
}
QgsDebugMsg( "existingRasters = " + existingRasters.join( "," ) );

QStringList errors;
QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data );
#ifdef WIN32
Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
#else
Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
#endif
foreach ( const QgsMimeDataUtils::Uri& u, lst )
{
if ( u.layerType != "raster" && u.layerType != "vector" )
{
errors.append( tr( "%1 layer type not supported" ).arg( u.name ) );
continue;
}
QgsRasterDataProvider* rasterProvider = 0;
QgsVectorDataProvider* vectorProvider = 0;
QgsDataProvider* provider = 0;
QStringList extensions;
QStringList existingNames;
if ( u.layerType == "raster" )
{
QgsRasterDataProvider* provider = qobject_cast<QgsRasterDataProvider*>( QgsProviderRegistry::instance()->provider( u.providerKey, u.uri ) );
if ( !provider )
{
errors.append( tr( "Cannot create provider %1 : %2" ).arg( u.providerKey ).arg( u.uri ) );
continue;
}
if ( !provider->isValid() )
rasterProvider = qobject_cast<QgsRasterDataProvider*>( QgsProviderRegistry::instance()->provider( u.providerKey, u.uri ) );
provider = rasterProvider;
existingNames = existingRasters;
}
else if ( u.layerType == "vector" )
{
vectorProvider = qobject_cast<QgsVectorDataProvider*>( QgsProviderRegistry::instance()->provider( u.providerKey, u.uri ) );
provider = vectorProvider;
existingNames = existingVectors;
}
QgsDebugMsg( "existingNames = " + existingNames.join( "," ) );

if ( !provider )
{
errors.append( tr( "Cannot create provider %1 : %2" ).arg( u.providerKey ).arg( u.uri ) );
continue;
}
if ( !provider->isValid() )
{
errors.append( tr( "Provider is not valid %1 : %2" ).arg( u.providerKey ).arg( u.uri ) );
delete provider;
continue;
}

if ( u.layerType == "raster" )
{
extensions = QgsGrassRasterImport::extensions( rasterProvider );
}

QString newName = u.name;
if ( QgsNewNameDialog::exists( u.name, extensions, existingNames, caseSensitivity ) )
{
QgsNewNameDialog dialog( u.name, u.name, extensions, existingNames, QRegExp(), caseSensitivity );
if ( dialog.exec() != QDialog::Accepted )
{
errors.append( tr( "Provider is not valid %1 : %2" ).arg( u.providerKey ).arg( u.uri ) );
delete provider;
continue;
}
#ifdef WIN32
Qt::CaseSensitivity cs = Qt::CaseInsensitive;
#else
Qt::CaseSensitivity cs = Qt::CaseSensitive;
#endif
QStringList extensions = QgsGrassRasterImport::extensions( provider );
QString newName = u.name;
if ( QgsNewNameDialog::exists( u.name, extensions, existingRasters, cs ) )
{
QgsNewNameDialog dialog( u.name, u.name, extensions, existingRasters, QRegExp(), cs );
if ( dialog.exec() != QDialog::Accepted )
{
delete provider;
continue;
}
newName = dialog.name();
}
newName = dialog.name();
}

QgsRectangle newExtent = provider->extent();
QgsGrassImport *import = 0;
if ( u.layerType == "raster" )
{
QgsRectangle newExtent = rasterProvider->extent();
int newXSize;
int newYSize;
bool useSrcRegion = true;
if ( provider->capabilities() & QgsRasterInterface::Size )
if ( rasterProvider->capabilities() & QgsRasterInterface::Size )
{
newXSize = provider->xSize();
newYSize = provider->ySize();
newXSize = rasterProvider->xSize();
newYSize = rasterProvider->ySize();
}
else
{
@@ -260,7 +293,7 @@ bool QgsGrassMapsetItem::handleDrop( const QMimeData * data, Qt::DropAction )
if ( !QgsGrass::defaultRegion( mGisdbase, mLocation, &window ) )
{
errors.append( tr( "Cannot get default location region." ) );
delete provider;
delete rasterProvider;
continue;
}
newXSize = window.cols;
@@ -271,9 +304,9 @@ bool QgsGrassMapsetItem::handleDrop( const QMimeData * data, Qt::DropAction )
}

QgsRasterPipe* pipe = new QgsRasterPipe();
pipe->set( provider );
pipe->set( rasterProvider );

QgsCoordinateReferenceSystem providerCrs = provider->crs();
QgsCoordinateReferenceSystem providerCrs = rasterProvider->crs();
QgsDebugMsg( "providerCrs = " + providerCrs.toWkt() );
QgsDebugMsg( "mapsetCrs = " + mapsetCrs.toWkt() );
if ( providerCrs.isValid() && mapsetCrs.isValid() && providerCrs != mapsetCrs )
@@ -282,7 +315,7 @@ bool QgsGrassMapsetItem::handleDrop( const QMimeData * data, Qt::DropAction )
projector->setCRS( providerCrs, mapsetCrs );
if ( useSrcRegion )
{
projector->destExtentSize( provider->extent(), provider->xSize(), provider->ySize(),
projector->destExtentSize( rasterProvider->extent(), rasterProvider->xSize(), rasterProvider->ySize(),
newExtent, newXSize, newYSize );
}

@@ -293,41 +326,50 @@ bool QgsGrassMapsetItem::handleDrop( const QMimeData * data, Qt::DropAction )

QString path = mPath + "/" + "raster" + "/" + u.name;
QgsGrassObject rasterObject( mGisdbase, mLocation, mName, newName, QgsGrassObject::Raster );
QgsGrassRasterImport *import = new QgsGrassRasterImport( pipe, rasterObject,
newExtent, newXSize, newYSize ); // takes pipe ownership
connect( import, SIGNAL( finished( QgsGrassImport* ) ), SLOT( onImportFinished( QgsGrassImport* ) ) );
import = new QgsGrassRasterImport( pipe, rasterObject, newExtent, newXSize, newYSize ); // takes pipe ownership
}
else if ( u.layerType == "vector" )
{
QString path = mPath + "/" + "raster" + "/" + u.name;
QgsGrassObject vectorObject( mGisdbase, mLocation, mName, newName, QgsGrassObject::Vector );
import = new QgsGrassVectorImport( vectorProvider, vectorObject ); // takes provider ownership
}

// delete existing files (confirmed before in dialog)
bool deleteOk = true;
foreach ( QString name, import->names() )
connect( import, SIGNAL( finished( QgsGrassImport* ) ), SLOT( onImportFinished( QgsGrassImport* ) ) );

// delete existing files (confirmed before in dialog)
bool deleteOk = true;
foreach ( QString name, import->names() )
{
QgsGrassObject obj( import->grassObject() );
obj.setName( name );
if ( QgsGrass::objectExists( obj ) )
{
QgsGrassObject obj( rasterObject );
obj.setName( name );
if ( QgsGrass::objectExists( obj ) )
QgsDebugMsg( name + " exists -> delete" );
if ( !QgsGrass::deleteObject( obj ) )
{
QgsDebugMsg( name + " exists -> delete" );
if ( !QgsGrass::deleteObject( obj ) )
{
errors.append( tr( "Cannot delete %1" ).arg( name ) );
deleteOk = false;
}
errors.append( tr( "Cannot delete %1" ).arg( name ) );
deleteOk = false;
}
}
if ( !deleteOk )
{
delete import;
continue;
}
}
if ( !deleteOk )
{
delete import;
continue;
}

import->importInThread();
mImports.append( import );
import->importInThread();
mImports.append( import );
if ( u.layerType == "raster" )
{
existingRasters.append( import->names() );
refresh(); // after each new item so that it is visible if dialog is opened on next item
}
else
else if ( u.layerType == "vector" )
{
errors.append( tr( "%1 layer type not supported" ).arg( u.name ) );
existingVectors.append( import->names() );
}
refresh(); // after each new item so that it is visible if dialog is opened on next item
}

if ( !errors.isEmpty() )
96 changes: 84 additions & 12 deletions tests/src/providers/grass/testqgsgrassprovider.cpp
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ class TestQgsGrassProvider: public QObject
void region();
void info();
void rasterImport();
void vectorImport();
private:
void reportRow( QString message );
void reportHeader( QString message );
@@ -73,6 +74,7 @@ class TestQgsGrassProvider: public QObject
bool compare( QStringList expected, QStringList got, bool& ok );
// compare with tolerance
bool compare( double expected, double got, bool& ok );
bool createTmpLocation( QString& tmpGisdbase, QString& tmpLocation, QString& tmpMapset );
QString mGisdbase;
QString mLocation;
QString mReport;
@@ -106,6 +108,8 @@ void TestQgsGrassProvider::initTestCase()
mReport += QString( "<h1>GRASS %1 provider tests</h1>\n" ).arg( GRASS_BUILD_VERSION );
mReport += "<p>" + mySettings + "</p>\n";

reportRow( "LD_LIBRARY_PATH: " + QString( getenv( "LD_LIBRARY_PATH" ) ) );

QgsGrass::init();

//create some objects that will be used in all tests...
@@ -407,29 +411,25 @@ void TestQgsGrassProvider::info()
verify( ok );
}

void TestQgsGrassProvider::rasterImport()
// create temporary output location
bool TestQgsGrassProvider::createTmpLocation( QString& tmpGisdbase, QString& tmpLocation, QString& tmpMapset )
{
reportHeader( "TestQgsGrassProvider::rasterImport" );
bool ok = true;

// create temporary output location
// use QTemporaryFile to generate name (QTemporaryDir since 5.0)
QTemporaryFile* tmpFile = new QTemporaryFile( QDir::tempPath() + "/qgis-grass-test" );
tmpFile->open();
QString tmpGisdbase = tmpFile->fileName();
tmpGisdbase = tmpFile->fileName();
delete tmpFile;
reportRow( "tmpGisdbase: " + tmpGisdbase );
QString tmpLocation = "test";
QString tmpMapset = "PERMANENT";
tmpLocation = "test";
tmpMapset = "PERMANENT";

QString tmpMapsetPath = tmpGisdbase + "/" + tmpLocation + "/" + tmpMapset;
reportRow( "tmpMapsetPath: " + tmpMapsetPath );
QDir tmpDir = QDir::temp();
if ( !tmpDir.mkpath( tmpMapsetPath ) )
{
reportRow( "cannot create " + tmpMapsetPath );
verify( false );
return;
return false;
}

QStringList cpFiles;
@@ -440,10 +440,27 @@ void TestQgsGrassProvider::rasterImport()
if ( !QFile::copy( templateMapsetPath + "/" + cpFile, tmpMapsetPath + "/" + cpFile ) )
{
reportRow( "cannot copy " + cpFile );
verify( false );
return;
return false;
}
}
return true;
}

void TestQgsGrassProvider::rasterImport()
{
reportHeader( "TestQgsGrassProvider::rasterImport" );
bool ok = true;

QString tmpGisdbase;
QString tmpLocation;
QString tmpMapset;

if ( !createTmpLocation( tmpGisdbase, tmpLocation, tmpMapset ) )
{
reportRow( "cannot create temporary location" );
verify( false );
return;
}

QStringList rasterFiles;
rasterFiles << "tenbytenraster.asc" << "landsat.tif" << "raster/band1_byte_ct_epsg4326.tif" << "raster/band1_int16_noct_epsg4326.tif";
@@ -501,5 +518,60 @@ void TestQgsGrassProvider::rasterImport()
verify( ok );
}

void TestQgsGrassProvider::vectorImport()
{
reportHeader( "TestQgsGrassProvider::vectorImport" );
bool ok = true;

QString tmpGisdbase;
QString tmpLocation;
QString tmpMapset;

if ( !createTmpLocation( tmpGisdbase, tmpLocation, tmpMapset ) )
{
reportRow( "cannot create temporary location" );
verify( false );
return;
}

QStringList files;
files << "points.shp" << "multipoint.shp" << "lines.shp" << "polys.shp";
files << "polys_overlapping.shp" << "bug5598.shp";

QgsCoordinateReferenceSystem mapsetCrs = QgsGrass::crsDirect( mGisdbase, mLocation );
foreach ( QString file, files )
{
QString uri = QString( TEST_DATA_DIR ) + "/" + file;
QString name = QFileInfo( uri ).baseName();
reportRow( "input vector: " + uri );
QgsVectorDataProvider* provider = qobject_cast<QgsVectorDataProvider*>( QgsProviderRegistry::instance()->provider( "ogr", uri ) );
if ( !provider )
{
reportRow( "Cannot create provider " + uri );
ok = false;
continue;
}
if ( !provider->isValid() )
{
reportRow( "Provider is not valid " + uri );
ok = false;
continue;
}

QgsGrassObject vectorObject( tmpGisdbase, tmpLocation, tmpMapset, name, QgsGrassObject::Vector );
QgsGrassVectorImport *import = new QgsGrassVectorImport( provider, vectorObject );
if ( !import->import() )
{
reportRow( "import failed: " + import->error() );
ok = false;
}
delete import;

QStringList layers = QgsGrass::vectorLayers( tmpGisdbase, tmpLocation, tmpMapset, name );
reportRow( "created layers: " + layers.join( "," ) );
}
verify( ok );
}

QTEST_MAIN( TestQgsGrassProvider )
#include "testqgsgrassprovider.moc"

0 comments on commit 7091d3b

Please sign in to comment.