1 /***************************************************************************
2 qgsgrassimport.cpp - Import to GRASS mapset
3 -------------------
4 begin : May, 2015
5 copyright : (C) 2015 Radim Blazek
6 email : radim.blazek@gmail.com
7 ***************************************************************************/
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include <QByteArray>
18 #include <QtConcurrentRun>
19
20 #include "qgscoordinatereferencesystem.h"
21 #include "qgscoordinatetransform.h"
22 #include "qgsfeature.h"
23 #include "qgsfeatureiterator.h"
24 #include "qgsgeometry.h"
25 #include "qgsproviderregistry.h"
26 #include "qgsrasterdataprovider.h"
27 #include "qgsrasteriterator.h"
28
29 #include "qgsgrassimport.h"
30
31 extern "C"
32 {
33 #include <grass/version.h>
34 #include <grass/gis.h>
35 #include <grass/raster.h>
36 #include <grass/imagery.h>
37 }
38
39 //------------------------------ QgsGrassImport ------------------------------------
instance()40 QgsGrassImportIcon *QgsGrassImportIcon::instance()
41 {
42 static QgsGrassImportIcon *sInstance = new QgsGrassImportIcon();
43 return sInstance;
44 }
45
QgsGrassImportIcon()46 QgsGrassImportIcon::QgsGrassImportIcon()
47 : QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconImport.gif" ) ) )
48 {
49 }
50
51 //------------------------------ QgsGrassImportProcess ------------------------------------
QgsGrassImportProgress(QProcess * process,QObject * parent)52 QgsGrassImportProgress::QgsGrassImportProgress( QProcess *process, QObject *parent )
53 : QObject( parent )
54 , mProcess( process )
55 , mProgressMin( 0 )
56 , mProgressMax( 0 )
57 , mProgressValue( 0 )
58 {
59 connect( mProcess, &QProcess::readyReadStandardError, this, &QgsGrassImportProgress::onReadyReadStandardError );
60 }
61
setProcess(QProcess * process)62 void QgsGrassImportProgress::setProcess( QProcess *process )
63 {
64 mProcess = process;
65 connect( mProcess, &QProcess::readyReadStandardError, this, &QgsGrassImportProgress::onReadyReadStandardError );
66 }
67
onReadyReadStandardError()68 void QgsGrassImportProgress::onReadyReadStandardError()
69 {
70 if ( mProcess )
71 {
72 // TODO: parse better progress output
73 QString output = QString::fromLocal8Bit( mProcess->readAllStandardError() );
74 Q_FOREACH ( const QString &line, output.split( "\n" ) )
75 {
76 QgsDebugMsg( "line = '" + line + "'" );
77 QString text, html;
78 int value;
79 QgsGrass::ModuleOutput type = QgsGrass::parseModuleOutput( line, text, html, value );
80 if ( type == QgsGrass::OutputPercent )
81 {
82 mProgressMin = 0;
83 mProgressMax = 100;
84 mProgressValue = value;
85 emit progressChanged( html, mProgressHtml, mProgressMin, mProgressMax, mProgressValue );
86 }
87 else if ( type == QgsGrass::OutputProgress )
88 {
89 html = tr( "Progress: %1" ).arg( value );
90 append( html );
91 }
92 else if ( type == QgsGrass::OutputMessage || type == QgsGrass::OutputWarning || type == QgsGrass::OutputError )
93 {
94 append( html );
95 }
96 }
97 }
98 }
99
append(const QString & html)100 void QgsGrassImportProgress::append( const QString &html )
101 {
102 QgsDebugMsg( "html = " + html );
103 if ( !mProgressHtml.isEmpty() )
104 {
105 mProgressHtml += QLatin1String( "<br>" );
106 }
107 mProgressHtml += html;
108 emit progressChanged( html, mProgressHtml, mProgressMin, mProgressMax, mProgressValue );
109 }
110
setRange(int min,int max)111 void QgsGrassImportProgress::setRange( int min, int max )
112 {
113 mProgressMin = min;
114 mProgressMax = max;
115 mProgressValue = min;
116 emit progressChanged( QString(), mProgressHtml, mProgressMin, mProgressMax, mProgressValue );
117 }
118
setValue(int value)119 void QgsGrassImportProgress::setValue( int value )
120 {
121 mProgressValue = value;
122 emit progressChanged( QString(), mProgressHtml, mProgressMin, mProgressMax, mProgressValue );
123 }
124
125 //------------------------------ QgsGrassImport ------------------------------------
QgsGrassImport(const QgsGrassObject & grassObject)126 QgsGrassImport::QgsGrassImport( const QgsGrassObject &grassObject )
127 : mGrassObject( grassObject )
128 , mCanceled( false )
129 {
130 // QMovie used by QgsAnimatedIcon is using QTimer which cannot be start from another thread
131 // (it works on Linux however) so we cannot start it connecting from QgsGrassImportItem and
132 // connect it also here (QgsGrassImport is constructed on the main thread) to a slot doing nothing.
133 QgsGrassImportIcon::instance()->connectFrameChanged( this, &QgsGrassImport::frameChanged );
134 }
135
~QgsGrassImport()136 QgsGrassImport::~QgsGrassImport()
137 {
138 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
139 {
140 QgsDebugMsg( "mFutureWatcher not finished -> waitForFinished()" );
141 mFutureWatcher->waitForFinished();
142 }
143 QgsGrassImportIcon::instance()->disconnectFrameChanged( this, &QgsGrassImport::frameChanged );
144 }
145
setError(const QString & error)146 void QgsGrassImport::setError( const QString &error )
147 {
148 QgsDebugMsg( "error: " + error );
149 mError = error;
150 }
151
error()152 QString QgsGrassImport::error()
153 {
154 return mError;
155 }
156
importInThread()157 void QgsGrassImport::importInThread()
158 {
159 mFutureWatcher = new QFutureWatcher<bool>( this );
160 connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsGrassImport::onFinished );
161 mFutureWatcher->setFuture( QtConcurrent::run( run, this ) );
162 }
163
run(QgsGrassImport * imp)164 bool QgsGrassImport::run( QgsGrassImport *imp )
165 {
166 imp->import();
167 return true;
168 }
169
onFinished()170 void QgsGrassImport::onFinished()
171 {
172 emit finished( this );
173 }
174
names() const175 QStringList QgsGrassImport::names() const
176 {
177 QStringList list;
178 list << mGrassObject.name();
179 return list;
180 }
181
isCanceled() const182 bool QgsGrassImport::isCanceled() const
183 {
184 return mCanceled;
185 }
186
cancel()187 void QgsGrassImport::cancel()
188 {
189 mCanceled = true;
190 }
191
192 //------------------------------ QgsGrassRasterImport ------------------------------------
QgsGrassRasterImport(QgsRasterPipe * pipe,const QgsGrassObject & grassObject,const QgsRectangle & extent,int xSize,int ySize)193 QgsGrassRasterImport::QgsGrassRasterImport( QgsRasterPipe *pipe, const QgsGrassObject &grassObject,
194 const QgsRectangle &extent, int xSize, int ySize )
195 : QgsGrassImport( grassObject )
196 , mPipe( pipe )
197 , mExtent( extent )
198 , mXSize( xSize )
199 , mYSize( ySize )
200 {
201 }
202
~QgsGrassRasterImport()203 QgsGrassRasterImport::~QgsGrassRasterImport()
204 {
205 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
206 {
207 QgsDebugMsg( "mFutureWatcher not finished -> waitForFinished()" );
208 mFutureWatcher->waitForFinished();
209 }
210 delete mPipe;
211 }
212
import()213 bool QgsGrassRasterImport::import()
214 {
215 if ( !mPipe )
216 {
217 setError( QStringLiteral( "Pipe is null." ) );
218 return false;
219 }
220
221 QgsRasterDataProvider *provider = mPipe->provider();
222 if ( !provider )
223 {
224 setError( QStringLiteral( "Pipe has no provider." ) );
225 return false;
226 }
227
228 if ( !provider->isValid() )
229 {
230 setError( QStringLiteral( "Provider is not valid." ) );
231 return false;
232 }
233
234
235 struct Cell_head defaultWindow;
236 if ( !QgsGrass::defaultRegion( mGrassObject.gisdbase(), mGrassObject.location(), &defaultWindow ) )
237 {
238 setError( QStringLiteral( "Cannot get default window" ) );
239 return false;
240 }
241
242 int redBand = 0;
243 int greenBand = 0;
244 int blueBand = 0;
245 for ( int band = 1; band <= provider->bandCount(); band++ )
246 {
247 QgsDebugMsg( QString( "band = %1" ).arg( band ) );
248 int colorInterpretation = provider->colorInterpretation( band );
249 if ( colorInterpretation == QgsRaster::RedBand )
250 {
251 redBand = band;
252 }
253 else if ( colorInterpretation == QgsRaster::GreenBand )
254 {
255 greenBand = band;
256 }
257 else if ( colorInterpretation == QgsRaster::BlueBand )
258 {
259 blueBand = band;
260 }
261
262 Qgis::DataType qgis_out_type = Qgis::UnknownDataType;
263 #ifdef QGISDEBUG
264 RASTER_MAP_TYPE data_type = -1;
265 #endif
266 switch ( provider->dataType( band ) )
267 {
268 case Qgis::Byte:
269 case Qgis::UInt16:
270 case Qgis::Int16:
271 case Qgis::UInt32:
272 case Qgis::Int32:
273 qgis_out_type = Qgis::Int32;
274 break;
275 case Qgis::Float32:
276 qgis_out_type = Qgis::Float32;
277 break;
278 case Qgis::Float64:
279 qgis_out_type = Qgis::Float64;
280 break;
281 case Qgis::ARGB32:
282 case Qgis::ARGB32_Premultiplied:
283 qgis_out_type = Qgis::Int32; // split to multiple bands?
284 break;
285 case Qgis::CInt16:
286 case Qgis::CInt32:
287 case Qgis::CFloat32:
288 case Qgis::CFloat64:
289 case Qgis::UnknownDataType:
290 setError( tr( "Data type %1 not supported" ).arg( provider->dataType( band ) ) );
291 return false;
292 }
293
294 QgsDebugMsg( QString( "data_type = %1" ).arg( data_type ) );
295
296 QString module = QgsGrass::qgisGrassModulePath() + "/qgis.r.in";
297 QStringList arguments;
298 QString name = mGrassObject.name();
299 if ( provider->bandCount() > 1 )
300 {
301 // raster.<band> to keep in sync with r.in.gdal
302 name += QStringLiteral( ".%1" ).arg( band );
303 }
304 arguments.append( "output=" + name ); // get list of all output names
305 QTemporaryFile gisrcFile;
306 try
307 {
308 mProcess = QgsGrass::startModule( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset(), module, arguments, gisrcFile );
309 }
310 catch ( QgsGrass::Exception &e )
311 {
312 setError( e.what() );
313 return false;
314 }
315 if ( !mProgress )
316 {
317 mProgress = new QgsGrassImportProgress( mProcess, this );
318 }
319 else
320 {
321 mProgress->setProcess( mProcess );
322 }
323 mProgress->append( tr( "Writing band %1/%2" ).arg( band ).arg( provider->bandCount() ) );
324
325 QDataStream outStream( mProcess );
326
327 outStream << ( qint32 ) defaultWindow.proj;
328 outStream << ( qint32 ) defaultWindow.zone;
329 outStream << mExtent << ( qint32 )mXSize << ( qint32 )mYSize;
330 outStream << ( qint32 )qgis_out_type;
331
332 // calculate reasonable block size (5MB)
333 int maximumTileHeight = 5000000 / mXSize;
334 maximumTileHeight = std::max( 1, maximumTileHeight );
335 // smaller if reprojecting so that it can be canceled quickly
336 if ( mPipe->projector() )
337 {
338 maximumTileHeight = std::max( 1, 100000 / mXSize );
339 }
340
341 QgsRasterIterator iter( mPipe->last() );
342 iter.setMaximumTileWidth( mXSize );
343 iter.setMaximumTileHeight( maximumTileHeight );
344
345 iter.startRasterRead( band, mXSize, mYSize, mExtent );
346
347 int iterLeft = 0;
348 int iterTop = 0;
349 int iterCols = 0;
350 int iterRows = 0;
351 QgsRasterBlock *block = nullptr;
352 mProcess->setReadChannel( QProcess::StandardOutput );
353 mProgress->setRange( 0, mYSize - 1 );
354 while ( iter.readNextRasterPart( band, iterCols, iterRows, &block, iterLeft, iterTop ) )
355 {
356 for ( int row = 0; row < iterRows; row++ )
357 {
358 mProgress->setValue( iterTop + row );
359
360 if ( !block->convert( qgis_out_type ) )
361 {
362 setError( tr( "Cannot convert block (%1) to data type %2" ).arg( block->toString() ).arg( qgis_out_type ) );
363 delete block;
364 return false;
365 }
366 // prepare null values
367 double noDataValue;
368 if ( block->hasNoDataValue() )
369 {
370 noDataValue = block->noDataValue();
371 }
372 else
373 {
374 switch ( qgis_out_type )
375 {
376 case Qgis::Int32:
377 noDataValue = -2147483648.0;
378 break;
379 case Qgis::Float32:
380 noDataValue = std::numeric_limits<float>::max() * -1.0;
381 break;
382 case Qgis::Float64:
383 noDataValue = std::numeric_limits<double>::max() * -1.0;
384 break;
385 default: // should not happen
386 noDataValue = std::numeric_limits<double>::max() * -1.0;
387 }
388 for ( qgssize i = 0; i < ( qgssize )block->width()*block->height(); i++ )
389 {
390 if ( block->isNoData( i ) )
391 {
392 block->setValue( i, noDataValue );
393 }
394 }
395 }
396
397 char *data = block->bits( row, 0 );
398 int size = iterCols * block->dataTypeSize();
399 QByteArray byteArray = QByteArray::fromRawData( data, size ); // does not copy data and does not take ownership
400 if ( isCanceled() )
401 {
402 outStream << true; // cancel module
403 break;
404 }
405 outStream << false; // not canceled
406 outStream << noDataValue;
407
408 outStream << byteArray;
409
410 // Without waitForBytesWritten() it does not finish OK on Windows (process timeout)
411 mProcess->waitForBytesWritten( -1 );
412
413 #ifndef Q_OS_WIN
414 // wait until the row is written to allow quick cancel (don't send data to buffer)
415 mProcess->waitForReadyRead();
416 bool result;
417 outStream >> result;
418 #endif
419 }
420 delete block;
421 if ( isCanceled() )
422 {
423 outStream << true; // cancel module
424 break;
425 }
426 }
427
428 // TODO: send something back from module and read it here to close map correctly in module
429
430 mProcess->closeWriteChannel();
431 // TODO: best timeout?
432 mProcess->waitForFinished( 30000 );
433
434 QString stderrString = mProcess->readAllStandardError().constData();
435
436 #ifdef QGISDEBUG
437 QString stdoutString = mProcess->readAllStandardOutput().constData();
438 QString processResult = QStringLiteral( "exitStatus=%1, exitCode=%2, error=%3, errorString=%4 stdout=%5, stderr=%6" )
439 .arg( mProcess->exitStatus() ).arg( mProcess->exitCode() )
440 .arg( mProcess->error() ).arg( mProcess->errorString(),
441 stdoutString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ), stderrString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ) );
442 QgsDebugMsg( "processResult: " + processResult );
443 #endif
444
445 if ( mProcess->exitStatus() != QProcess::NormalExit )
446 {
447 setError( mProcess->errorString() );
448 mProcess = nullptr;
449 return false;
450 }
451
452 if ( mProcess->exitCode() != 0 )
453 {
454 setError( stderrString );
455 delete mProcess;
456 mProcess = nullptr;
457 return false;
458 }
459
460 delete mProcess;
461 mProcess = nullptr;
462 }
463 QgsDebugMsg( QString( "redBand = %1 greenBand = %2 blueBand = %3" ).arg( redBand ).arg( greenBand ).arg( blueBand ) );
464 if ( redBand > 0 && greenBand > 0 && blueBand > 0 )
465 {
466 // TODO: check if the group exists
467 // I_find_group()
468 QString name = mGrassObject.name();
469
470 G_TRY
471 {
472 QgsGrass::setMapset( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset() );
473 struct Ref ref;
474 I_get_group_ref( name.toUtf8().constData(), &ref );
475 QString redName = name + QStringLiteral( ".%1" ).arg( redBand );
476 QString greenName = name + QStringLiteral( ".%1" ).arg( greenBand );
477 QString blueName = name + QStringLiteral( ".%1" ).arg( blueBand );
478 I_add_file_to_group_ref( redName.toUtf8().constData(), mGrassObject.mapset().toUtf8().constData(), &ref );
479 I_add_file_to_group_ref( greenName.toUtf8().constData(), mGrassObject.mapset().toUtf8().constData(), &ref );
480 I_add_file_to_group_ref( blueName.toUtf8().constData(), mGrassObject.mapset().toUtf8().constData(), &ref );
481 I_put_group_ref( name.toUtf8().constData(), &ref );
482 }
483 G_CATCH( QgsGrass::Exception & e )
484 {
485 QgsDebugMsg( QString( "Cannot create group: %1" ).arg( e.what() ) );
486 }
487 }
488 return true;
489 }
490
srcDescription() const491 QString QgsGrassRasterImport::srcDescription() const
492 {
493 if ( !mPipe || !mPipe->provider() )
494 {
495 return QString();
496 }
497 return mPipe->provider()->dataSourceUri();
498 }
499
extensions(QgsRasterDataProvider * provider)500 QStringList QgsGrassRasterImport::extensions( QgsRasterDataProvider *provider )
501 {
502 QStringList list;
503 if ( provider && provider->bandCount() > 1 )
504 {
505 int bands = provider->bandCount();
506 list.reserve( bands );
507 for ( int band = 1; band <= bands; ++band )
508 {
509 list << QStringLiteral( ".%1" ).arg( band );
510 }
511 }
512 return list;
513 }
514
names() const515 QStringList QgsGrassRasterImport::names() const
516 {
517 QStringList list;
518 if ( mPipe && mPipe->provider() )
519 {
520 Q_FOREACH ( const QString &ext, extensions( mPipe->provider() ) )
521 {
522 list << mGrassObject.name() + ext;
523 }
524 }
525 if ( list.isEmpty() )
526 {
527 list << mGrassObject.name();
528 }
529 return list;
530 }
531
532 //------------------------------ QgsGrassVectorImport ------------------------------------
QgsGrassVectorImport(QgsVectorDataProvider * provider,const QgsGrassObject & grassObject)533 QgsGrassVectorImport::QgsGrassVectorImport( QgsVectorDataProvider *provider, const QgsGrassObject &grassObject )
534 : QgsGrassImport( grassObject )
535 , mProvider( provider )
536 {
537 }
538
~QgsGrassVectorImport()539 QgsGrassVectorImport::~QgsGrassVectorImport()
540 {
541 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
542 {
543 QgsDebugMsg( "mFutureWatcher not finished -> waitForFinished()" );
544 mFutureWatcher->waitForFinished();
545 }
546 delete mProvider;
547 }
548
import()549 bool QgsGrassVectorImport::import()
550 {
551
552 if ( !mProvider )
553 {
554 setError( QStringLiteral( "Provider is null." ) );
555 return false;
556 }
557
558 if ( !mProvider->isValid() )
559 {
560 setError( QStringLiteral( "Provider is not valid." ) );
561 return false;
562 }
563
564 QgsCoordinateReferenceSystem providerCrs = mProvider->crs();
565 QgsCoordinateReferenceSystem mapsetCrs = QgsGrass::crsDirect( mGrassObject.gisdbase(), mGrassObject.location() );
566 QgsDebugMsg( "providerCrs = " + providerCrs.toWkt() );
567 QgsDebugMsg( "mapsetCrs = " + mapsetCrs.toWkt() );
568 QgsCoordinateTransform coordinateTransform;
569 bool doTransform = false;
570 if ( providerCrs.isValid() && mapsetCrs.isValid() && providerCrs != mapsetCrs )
571 {
572 coordinateTransform.setSourceCrs( providerCrs );
573 coordinateTransform.setDestinationCrs( mapsetCrs );
574 doTransform = true;
575 }
576
577 QString module = QgsGrass::qgisGrassModulePath() + "/qgis.v.in";
578 QStringList arguments;
579 QString name = mGrassObject.name();
580 arguments.append( "output=" + name );
581 QTemporaryFile gisrcFile;
582 try
583 {
584 mProcess = QgsGrass::startModule( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset(), module, arguments, gisrcFile );
585 }
586 catch ( QgsGrass::Exception &e )
587 {
588 setError( e.what() );
589 return false;
590 }
591 mProgress = new QgsGrassImportProgress( mProcess, this );
592
593 QDataStream outStream( mProcess );
594 mProcess->setReadChannel( QProcess::StandardOutput );
595
596 QgsWkbTypes::Type wkbType = mProvider->wkbType();
597 bool isPolygon = QgsWkbTypes::singleType( QgsWkbTypes::flatType( wkbType ) ) == QgsWkbTypes::Polygon;
598 outStream << ( qint32 )wkbType;
599
600 outStream << mProvider->fields();
601
602 // FID may be 0, but cat should be >= 1 -> check if fid 0 exists
603 qint32 fidToCatPlus = 0;
604 QgsFeature feature;
605 QgsFeatureIterator iterator = mProvider->getFeatures( QgsFeatureRequest().setFilterFid( 0 ) );
606 if ( iterator.nextFeature( feature ) )
607 {
608 fidToCatPlus = 1;
609 }
610 iterator.close();
611 outStream << fidToCatPlus;
612
613 qint32 featureCount = mProvider->featureCount();
614 outStream << featureCount;
615
616 mProgress->append( tr( "Writing features" ) );
617 for ( int i = 0; i < ( isPolygon ? 2 : 1 ); i++ ) // two cycles with polygons
618 {
619 iterator = mProvider->getFeatures();
620 // rewind does not work
621 #if 0
622 if ( i > 0 ) // second run for polygons
623 {
624 iterator.rewind();
625 }
626 #endif
627 QgsDebugMsg( "send features" );
628 // Better to get real progress from module (without buffer)
629 #if 0
630 mProgress->setRange( 1, featureCount );
631 #endif
632 int count = 0;
633 while ( iterator.nextFeature( feature ) )
634 {
635 if ( !feature.isValid() )
636 {
637 continue;
638 }
639 if ( doTransform && feature.hasGeometry() )
640 {
641 QgsGeometry g = feature.geometry();
642 g.transform( coordinateTransform );
643 feature.setGeometry( g );
644 }
645 if ( isCanceled() )
646 {
647 outStream << true; // cancel module
648 break;
649 }
650 outStream << false; // not canceled
651 outStream << feature;
652
653 // Without waitForBytesWritten() it does not finish OK on Windows (data lost)
654 mProcess->waitForBytesWritten( -1 );
655
656 #ifndef Q_OS_WIN
657 // wait until the feature is written to allow quick cancel (don't send data to buffer)
658
659 // Feedback disabled because it was sometimes hanging on Linux, for example, when importing polygons
660 // the features were written OK in the first run, but after cleaning of polygons, which takes some time
661 // it was hanging here for few seconds after each feature, but data did not arrive to the modulee anyway,
662 // QFSFileEnginePrivate::nativeRead() was hanging on fgetc()
663
664 // TODO: inspect what is happening in QProcess, if there is some buffer and how to disable it
665 #if 0
666 mProcess->waitForReadyRead();
667 bool result;
668 outStream >> result;
669 #endif
670 #endif
671 count++;
672 #if 0
673 if ( count % 100 == 0 )
674 {
675 mProgress->setValue( count );
676 }
677 #endif
678 // get some feedback for large datasets
679 if ( count % 10000 == 0 )
680 {
681 QgsDebugMsg( QString( "%1 features written" ).arg( count ) );
682 }
683 }
684
685 feature = QgsFeature(); // indicate end by invalid feature
686 outStream << false; // not canceled
687 outStream << feature;
688
689 mProcess->waitForBytesWritten( -1 );
690 QgsDebugMsg( "features sent" );
691 #ifndef Q_OS_WIN
692 #if 0
693 mProcess->waitForReadyRead();
694 bool result;
695 outStream >> result;
696 QgsDebugMsg( "got feedback" );
697 #endif
698 #endif
699 iterator.close();
700 }
701
702 // Close write channel before waiting for response to avoid stdin buffer problem on Windows
703 mProcess->closeWriteChannel();
704
705 QgsDebugMsg( "waitForReadyRead" );
706 bool result;
707 mProcess->waitForReadyRead();
708 outStream >> result;
709 QgsDebugMsg( QString( "result = %1" ).arg( result ) );
710
711 QgsDebugMsg( "waitForFinished" );
712 mProcess->waitForFinished( 30000 );
713
714 #ifdef QGISDEBUG
715 QString stdoutString = mProcess->readAllStandardOutput().constData();
716 #endif
717 QString stderrString = mProcess->readAllStandardError().constData();
718
719 #ifdef QGISDEBUG
720 QString processResult = QStringLiteral( "exitStatus=%1, exitCode=%2, error=%3, errorString=%4 stdout=%5, stderr=%6" )
721 .arg( mProcess->exitStatus() ).arg( mProcess->exitCode() )
722 .arg( mProcess->error() ).arg( mProcess->errorString(),
723 stdoutString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ), stderrString.replace( QLatin1String( "\n" ), QLatin1String( ", " ) ) );
724 QgsDebugMsg( "processResult: " + processResult );
725 #endif
726
727 if ( mProcess->exitStatus() != QProcess::NormalExit )
728 {
729 setError( mProcess->errorString() );
730 delete mProcess;
731 mProcess = nullptr;
732 return false;
733 }
734
735 if ( mProcess->exitCode() != 0 )
736 {
737 setError( stderrString );
738 delete mProcess;
739 mProcess = nullptr;
740 return false;
741 }
742
743 delete mProcess;
744 mProcess = nullptr;
745 return true;
746 }
747
srcDescription() const748 QString QgsGrassVectorImport::srcDescription() const
749 {
750 if ( !mProvider )
751 {
752 return QString();
753 }
754 return mProvider->dataSourceUri();
755 }
756
757 //------------------------------ QgsGrassCopy ------------------------------------
QgsGrassCopy(const QgsGrassObject & srcObject,const QgsGrassObject & destObject)758 QgsGrassCopy::QgsGrassCopy( const QgsGrassObject &srcObject, const QgsGrassObject &destObject )
759 : QgsGrassImport( destObject )
760 , mSrcObject( srcObject )
761 {
762 }
763
import()764 bool QgsGrassCopy::import()
765 {
766
767 try
768 {
769 QgsGrass::copyObject( mSrcObject, mGrassObject );
770 }
771 catch ( QgsGrass::Exception &e )
772 {
773 setError( e.what() );
774 return false;
775 }
776
777 return true;
778 }
779
780
srcDescription() const781 QString QgsGrassCopy::srcDescription() const
782 {
783 return mSrcObject.toString();
784 }
785
786 //------------------------------ QgsGrassExternal ------------------------------------
QgsGrassExternal(const QString & gdalSource,const QgsGrassObject & destObject)787 QgsGrassExternal::QgsGrassExternal( const QString &gdalSource, const QgsGrassObject &destObject )
788 : QgsGrassImport( destObject )
789 , mSource( gdalSource )
790 {
791 }
792
import()793 bool QgsGrassExternal::import()
794 {
795
796 try
797 {
798 QString cmd = QgsGrass::gisbase() + "/bin/r.external";
799 QStringList arguments;
800
801 if ( QFileInfo::exists( mSource ) )
802 {
803 arguments << "input=" + mSource;
804 }
805 else
806 {
807 arguments << "source=" + mSource;
808 }
809 arguments << "output=" + mGrassObject.name();
810
811 // TODO: best timeout
812 int timeout = -1;
813 // throws QgsGrass::Exception
814 QgsGrass::runModule( mGrassObject.gisdbase(), mGrassObject.location(), mGrassObject.mapset(), cmd, arguments, timeout, false );
815 }
816 catch ( QgsGrass::Exception &e )
817 {
818 setError( e.what() );
819 return false;
820 }
821
822 return true;
823 }
824
srcDescription() const825 QString QgsGrassExternal::srcDescription() const
826 {
827 return mSource;
828 }
829