PageRenderTime 84ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/src/app/qgsnewspatialitelayerdialog.cpp

https://github.com/alexckp/qgis
C++ | 376 lines | 285 code | 58 blank | 33 comment | 43 complexity | 8e869339ed8b8a825cfe7a4995d22fc7 MD5 | raw file
  1. /***************************************************************************
  2. qgsnewspatialitelayerdialog.cpp
  3. Creates a new Spatialite layer. This dialog borrows heavily from
  4. qgsnewvectorlayerdialog.cpp
  5. -------------------
  6. begin : 2010-03-18
  7. copyright : (C) 2010 by Gary Sherman
  8. email : gsherman@mrcc.com
  9. ***************************************************************************/
  10. /***************************************************************************
  11. * *
  12. * This program is free software; you can redistribute it and/or modify *
  13. * it under the terms of the GNU General Public License as published by *
  14. * the Free Software Foundation; either version 2 of the License, or *
  15. * (at your option) any later version. *
  16. * *
  17. ***************************************************************************/
  18. /* $Id$ */
  19. #include "qgsnewspatialitelayerdialog.h"
  20. #include "qgsspatialitesridsdialog.h"
  21. #include "qgsapplication.h"
  22. #include "qgisapp.h" // <- for theme icons
  23. #include <qgsvectorlayer.h>
  24. #include <qgsmaplayerregistry.h>
  25. #include "qgslogger.h"
  26. #include <QPushButton>
  27. #include <QSettings>
  28. #include <QLineEdit>
  29. #include <QMessageBox>
  30. #include <QFileDialog>
  31. #include <spatialite.h>
  32. QgsNewSpatialiteLayerDialog::QgsNewSpatialiteLayerDialog( QWidget *parent, Qt::WFlags fl )
  33. : QDialog( parent, fl )
  34. {
  35. setupUi( this );
  36. mAddAttributeButton->setIcon( QgisApp::getThemeIcon( "/mActionNewAttribute.png" ) );
  37. mRemoveAttributeButton->setIcon( QgisApp::getThemeIcon( "/mActionDeleteAttribute.png" ) );
  38. mTypeBox->addItem( tr( "Text data" ), "text" );
  39. mTypeBox->addItem( tr( "Whole number" ), "integer" );
  40. mTypeBox->addItem( tr( "Decimal number" ), "real" );
  41. mPointRadioButton->setChecked( true );
  42. // Populate the database list from the stored connections
  43. QSettings settings;
  44. settings.beginGroup( "/SpatiaLite/connections" );
  45. QStringList keys = settings.childGroups();
  46. QStringList::Iterator it = keys.begin();
  47. mDatabaseComboBox->clear();
  48. while ( it != keys.end() )
  49. {
  50. // retrieving the SQLite DB name and full path
  51. QString text = settings.value( *it + "/sqlitepath", "###unknown###" ).toString();
  52. mDatabaseComboBox->addItem( text );
  53. ++it;
  54. }
  55. settings.endGroup();
  56. buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
  57. buttonBox->button( QDialogButtonBox::Apply )->setEnabled( false );
  58. connect( buttonBox->button( QDialogButtonBox::Apply ), SIGNAL( clicked() ), this, SLOT( apply() ) );
  59. buttonBox->button( QDialogButtonBox::Ok )->setDefault( true );
  60. // Set the SRID box to a default of WGS84
  61. leSRID->setText( "4326" );
  62. pbnFindSRID->setEnabled( mDatabaseComboBox->count() );
  63. }
  64. QgsNewSpatialiteLayerDialog::~QgsNewSpatialiteLayerDialog()
  65. {
  66. }
  67. void QgsNewSpatialiteLayerDialog::on_mTypeBox_currentIndexChanged( int index )
  68. {
  69. // This isn't used since widths are irrelevant in sqlite3
  70. switch ( index )
  71. {
  72. case 0: // Text data
  73. case 1: // Whole number
  74. case 2: // Decimal number
  75. default:
  76. break;
  77. }
  78. }
  79. void QgsNewSpatialiteLayerDialog::on_toolButtonNewDatabase_clicked()
  80. {
  81. QString fileName = QFileDialog::getSaveFileName( this, tr( "New SpatiaLite Database File" ),
  82. ".",
  83. tr( "SpatiaLite (*.sqlite *.db )" ) );
  84. if ( fileName.isEmpty() )
  85. return;
  86. mDatabaseComboBox->insertItem( 0, fileName );
  87. mDatabaseComboBox->setCurrentIndex( 0 );
  88. createDb();
  89. }
  90. QString QgsNewSpatialiteLayerDialog::selectedType() const
  91. {
  92. if ( mPointRadioButton->isChecked() )
  93. {
  94. return "POINT";
  95. }
  96. else if ( mLineRadioButton->isChecked() )
  97. {
  98. return "LINESTRING";
  99. }
  100. else if ( mPolygonRadioButton->isChecked() )
  101. {
  102. return "POLYGON";
  103. }
  104. else if ( mMultipointRadioButton->isChecked() )
  105. {
  106. return "MULTIPOINT";
  107. }
  108. else if ( mMultilineRadioButton->isChecked() )
  109. {
  110. return "MULTILINESTRING";
  111. }
  112. else if ( mMultipolygonRadioButton->isChecked() )
  113. {
  114. return "MULTIPOLYGON";
  115. }
  116. Q_ASSERT( "no type selected" == 0 );
  117. return "";
  118. }
  119. void QgsNewSpatialiteLayerDialog::on_leLayerName_textChanged( QString text )
  120. {
  121. bool created = leLayerName->text().length() > 0 && mAttributeView->topLevelItemCount() > 0 && createDb();
  122. buttonBox->button( QDialogButtonBox::Ok )->setEnabled( created );
  123. buttonBox->button( QDialogButtonBox::Apply )->setEnabled( created );
  124. }
  125. void QgsNewSpatialiteLayerDialog::on_mAddAttributeButton_clicked()
  126. {
  127. if ( mNameEdit->text().length() > 0 )
  128. {
  129. QString myName = mNameEdit->text();
  130. //use userrole to avoid translated type string
  131. QString myType = mTypeBox->itemData( mTypeBox->currentIndex(), Qt::UserRole ).toString();
  132. mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType ) );
  133. if ( mAttributeView->topLevelItemCount() > 0 && leLayerName->text().length() > 0 )
  134. {
  135. bool created = createDb();
  136. buttonBox->button( QDialogButtonBox::Ok )->setEnabled( created );
  137. buttonBox->button( QDialogButtonBox::Apply )->setEnabled( created );
  138. }
  139. mNameEdit->clear();
  140. }
  141. }
  142. void QgsNewSpatialiteLayerDialog::on_mRemoveAttributeButton_clicked()
  143. {
  144. delete mAttributeView->currentItem();
  145. if ( mAttributeView->topLevelItemCount() == 0 )
  146. {
  147. buttonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
  148. buttonBox->button( QDialogButtonBox::Apply )->setEnabled( false );
  149. }
  150. }
  151. void QgsNewSpatialiteLayerDialog::on_pbnFindSRID_clicked()
  152. {
  153. // set the SRID from a list returned from the selected Spatialite database
  154. QgsSpatialiteSridsDialog *sridDlg = new QgsSpatialiteSridsDialog( this );
  155. if ( sridDlg->load( mDatabaseComboBox->currentText() ) )
  156. {
  157. if ( sridDlg->exec() )
  158. {
  159. // set the srid field to the selection
  160. leSRID->setText( sridDlg->selectedSrid() );
  161. sridDlg->accept();
  162. }
  163. }
  164. }
  165. bool QgsNewSpatialiteLayerDialog::createDb()
  166. {
  167. QSettings settings;
  168. if ( mDatabaseComboBox->currentText().isEmpty() )
  169. return false;
  170. QFile newDb( mDatabaseComboBox->currentText() );
  171. if ( !newDb.exists() )
  172. {
  173. QgsDebugMsg( "creating a new db" );
  174. // copy the spatilite template to the user specified path
  175. QString spatialiteTemplate = QgsApplication::qgisSpatialiteDbTemplatePath();
  176. QFile spatialiteTemplateDb( spatialiteTemplate );
  177. QFileInfo fullPath = QFileInfo( mDatabaseComboBox->currentText() );
  178. QDir path = fullPath.dir();
  179. QgsDebugMsg( QString( "making this dir: %1" ).arg( path.absolutePath() ) );
  180. // Must be sure there is destination directory ~/.qgis
  181. QDir().mkpath( path.absolutePath( ) );
  182. QgsDebugMsg( QString( "Copying %1 to %2" ).arg( spatialiteTemplate ).arg( newDb.fileName() ) );
  183. //now copy the template db file into the chosen location
  184. if ( !spatialiteTemplateDb.copy( newDb.fileName() ) )
  185. {
  186. QMessageBox::warning( 0, tr( "SpatiaLite Database" ), tr( "Could not copy the template database to new location" ) );
  187. pbnFindSRID->setEnabled( false );
  188. return false;
  189. }
  190. }
  191. QFileInfo fi( newDb );
  192. if ( !fi.exists() )
  193. {
  194. pbnFindSRID->setEnabled( false );
  195. return false;
  196. }
  197. QString key = "/SpatiaLite/connections/" + fi.fileName() + "/sqlitepath";
  198. if ( !settings.contains( key ) )
  199. {
  200. settings.setValue( "/SpatiaLite/connections/selected", fi.fileName() );
  201. settings.setValue( key, fi.canonicalFilePath() );
  202. QMessageBox::information( 0, tr( "SpatiaLite Database" ), tr( "Registered new database!" ) );
  203. }
  204. pbnFindSRID->setEnabled( true );
  205. return true;
  206. }
  207. void QgsNewSpatialiteLayerDialog::on_buttonBox_accepted()
  208. {
  209. if ( apply() )
  210. accept();
  211. }
  212. void QgsNewSpatialiteLayerDialog::on_buttonBox_rejected()
  213. {
  214. reject();
  215. }
  216. bool QgsNewSpatialiteLayerDialog::apply()
  217. {
  218. // Build up the sql statement for creating the table
  219. QString sql = QString( "create table %1(" ).arg( quotedIdentifier( leLayerName->text() ) );
  220. QString delim = "";
  221. if ( checkBoxPrimaryKey->isChecked() )
  222. {
  223. sql += "pkuid integer primary key autoincrement,";
  224. }
  225. QTreeWidgetItemIterator it( mAttributeView );
  226. while ( *it )
  227. {
  228. sql += delim + QString( "%1 %2" ).arg( quotedIdentifier(( *it )->text( 0 ) ) ).arg(( *it )->text( 1 ) );
  229. delim = ",";
  230. ++it;
  231. }
  232. // complete the create table statement
  233. sql += ")";
  234. QgsDebugMsg( QString( "Creating table in database %1" ).arg( mDatabaseComboBox->currentText() ) );
  235. QgsDebugMsg( sql ); // OK
  236. QString sqlAddGeom = QString( "select AddGeometryColumn(%1,%2,%3,%4,2)" )
  237. .arg( quotedValue( leLayerName->text() ) )
  238. .arg( quotedValue( leGeometryColumn->text() ) )
  239. .arg( leSRID->text().toInt() )
  240. .arg( quotedValue( selectedType() ) );
  241. QgsDebugMsg( sqlAddGeom ); // OK
  242. QString sqlCreateIndex = QString( "select CreateSpatialIndex(%1,%2)" )
  243. .arg( quotedValue( leLayerName->text() ) )
  244. .arg( quotedValue( leGeometryColumn->text() ) );
  245. QgsDebugMsg( sqlCreateIndex ); // OK
  246. spatialite_init( 0 );
  247. sqlite3 *db;
  248. int rc = sqlite3_open( mDatabaseComboBox->currentText().toUtf8(), &db );
  249. if ( rc != SQLITE_OK )
  250. {
  251. QMessageBox::warning( this,
  252. tr( "SpatiaLite Database" ),
  253. tr( "Unable to open the database: %1" ).arg( mDatabaseComboBox->currentText() ) );
  254. }
  255. else
  256. {
  257. char * errmsg;
  258. rc = sqlite3_exec( db, sql.toUtf8(), NULL, NULL, &errmsg );
  259. if ( rc != SQLITE_OK )
  260. {
  261. QMessageBox::warning( this,
  262. tr( "Error Creating SpatiaLite Table" ),
  263. tr( "Failed to create the SpatiaLite table %1. The database returned:\n%2" ).arg( leLayerName->text() ).arg( errmsg ) );
  264. sqlite3_free( errmsg );
  265. }
  266. else
  267. {
  268. // create the geometry column and the spatial index
  269. rc = sqlite3_exec( db, sqlAddGeom.toUtf8(), NULL, NULL, &errmsg );
  270. if ( rc != SQLITE_OK )
  271. {
  272. QMessageBox::warning( this,
  273. tr( "Error Creating Geometry Column" ),
  274. tr( "Failed to create the geometry column. The database returned:\n%1" ).arg( errmsg ) );
  275. sqlite3_free( errmsg );
  276. }
  277. else
  278. {
  279. // create the spatial index
  280. rc = sqlite3_exec( db, sqlCreateIndex.toUtf8(), NULL, NULL, &errmsg );
  281. if ( rc != SQLITE_OK )
  282. {
  283. QMessageBox::warning( this,
  284. tr( "Error Creating Spatial Index" ),
  285. tr( "Failed to create the spatial index. The database returned:\n%1" ).arg( errmsg ) );
  286. sqlite3_free( errmsg );
  287. }
  288. QgsVectorLayer *layer = new QgsVectorLayer( QString( "dbname='%1' table='%2'(%3) sql=" )
  289. .arg( mDatabaseComboBox->currentText() )
  290. .arg( leLayerName->text() )
  291. .arg( leGeometryColumn->text() ), leLayerName->text(), "spatialite" );
  292. if ( layer->isValid() )
  293. {
  294. // register this layer with the central layers registry
  295. if ( QgsMapLayerRegistry::instance()->addMapLayer( layer ) )
  296. return true;
  297. }
  298. else
  299. {
  300. QgsDebugMsg( leLayerName->text() + " is an invalid layer - not loaded" );
  301. QMessageBox::critical( this, tr( "Invalid Layer" ), tr( "%1 is an invalid layer and cannot be loaded." ).arg( leLayerName->text() ) );
  302. delete layer;
  303. }
  304. }
  305. }
  306. }
  307. return false;
  308. }
  309. QString QgsNewSpatialiteLayerDialog::quotedIdentifier( QString id )
  310. {
  311. id.replace( "\"", "\"\"" );
  312. return id.prepend( "\"" ).append( "\"" );
  313. }
  314. QString QgsNewSpatialiteLayerDialog::quotedValue( QString value )
  315. {
  316. value.replace( "'", "''" );
  317. return value.prepend( "'" ).append( "'" );
  318. }