QGIS/python/plugins/db_manager/dlg_import_vector.py
2013-09-01 01:02:50 +02:00

348 lines
12 KiB
Python

# -*- coding: utf-8 -*-
"""
/***************************************************************************
Name : DB Manager
Description : Database manager plugin for QGIS
Date : Oct 13, 2011
copyright : (C) 2011 by Giuseppe Sucameli
email : brush.tyler@gmail.com
The content of this file is based on
- PG_Manager by Martin Dobias (GPLv2 license)
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
"""
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import qgis.core
from qgis.utils import iface
from .ui.ui_DlgImportVector import Ui_DbManagerDlgImportVector as Ui_Dialog
class DlgImportVector(QDialog, Ui_Dialog):
HAS_INPUT_MODE, ASK_FOR_INPUT_MODE = range(2)
def __init__(self, inLayer, outDb, outUri, parent=None):
QDialog.__init__(self, parent)
self.inLayer = inLayer
self.db = outDb
self.outUri = outUri
self.setupUi(self)
self.default_pk = "id"
self.default_geom = "geom"
self.mode = self.ASK_FOR_INPUT_MODE if self.inLayer is None else self.HAS_INPUT_MODE
# used to delete the inlayer whether created inside this dialog
self.inLayerMustBeDestroyed = False
self.populateSchemas()
self.populateTables()
self.populateLayers()
self.populateEncodings()
# updates of UI
self.setupWorkingMode( self.mode )
self.connect(self.cboSchema, SIGNAL("currentIndexChanged(int)"), self.populateTables)
def setupWorkingMode(self, mode):
""" hide the widget to select a layer/file if the input layer is already set """
self.wdgInput.setVisible( mode == self.ASK_FOR_INPUT_MODE )
self.resize( 450, 350 )
self.cboTable.setEditText(self.outUri.table())
if mode == self.ASK_FOR_INPUT_MODE:
QObject.connect( self.btnChooseInputFile, SIGNAL("clicked()"), self.chooseInputFile )
#QObject.connect( self.cboInputLayer.lineEdit(), SIGNAL("editingFinished()"), self.updateInputLayer )
QObject.connect( self.cboInputLayer, SIGNAL("editTextChanged(const QString &)"), self.inputPathChanged )
#QObject.connect( self.cboInputLayer, SIGNAL("currentIndexChanged(int)"), self.updateInputLayer )
QObject.connect( self.btnUpdateInputLayer, SIGNAL("clicked()"), self.updateInputLayer )
else:
# set default values
pk = self.outUri.keyColumn()
self.editPrimaryKey.setText(pk if pk != "" else self.default_pk)
if self.inLayer.hasGeometryType():
geom = self.outUri.geometryColumn()
self.editGeomColumn.setText(geom if geom != "" else self.default_geom)
inCrs = self.inLayer.crs()
srid = inCrs.postgisSrid() if inCrs.isValid() else 4236
self.editSourceSrid.setText( "%s" % srid )
self.editTargetSrid.setText( "%s" % srid )
self.checkSupports()
def checkSupports(self):
""" update options available for the current input layer """
allowSpatial = self.db.connector.hasSpatialSupport()
hasGeomType = self.inLayer and self.inLayer.hasGeometryType()
isShapefile = self.inLayer and self.inLayer.providerType() == "ogr" and self.inLayer.storageType() == "ESRI Shapefile"
self.chkGeomColumn.setEnabled(allowSpatial and hasGeomType)
self.chkSourceSrid.setEnabled(allowSpatial and hasGeomType)
self.chkTargetSrid.setEnabled(allowSpatial and hasGeomType)
self.chkSinglePart.setEnabled(allowSpatial and hasGeomType and isShapefile)
self.chkSpatialIndex.setEnabled(allowSpatial and hasGeomType)
def populateLayers(self):
self.cboInputLayer.clear()
for index, layer in enumerate( iface.legendInterface().layers() ):
# TODO: add import raster support!
if layer.type() == qgis.core.QgsMapLayer.VectorLayer:
self.cboInputLayer.addItem( layer.name(), index )
def deleteInputLayer(self):
""" unset the input layer, then destroy it but only if it was created from this dialog """
if self.mode == self.ASK_FOR_INPUT_MODE and self.inLayer:
if self.inLayerMustBeDestroyed:
self.inLayer.deleteLater()
self.inLayer = None
self.inLayerMustBeDestroyed = False
return True
return False
def chooseInputFile(self):
vectorFormats = qgis.core.QgsProviderRegistry.instance().fileVectorFilters()
# get last used dir and format
settings = QSettings()
lastDir = settings.value("/db_manager/lastUsedDir", "")
lastVectorFormat = settings.value("/UI/lastVectorFileFilter", "")
# ask for a filename
(filename, lastVectorFormat) = QFileDialog.getOpenFileNameAndFilter(self, self.tr("Choose the file to import"), lastDir, vectorFormats, lastVectorFormat)
if filename == "":
return
# store the last used dir and format
settings.setValue("/db_manager/lastUsedDir", QFileInfo(filename).filePath())
settings.setValue("/UI/lastVectorFileFilter", lastVectorFormat)
self.cboInputLayer.setEditText( filename )
def inputPathChanged(self, path):
if self.cboInputLayer.currentIndex() < 0:
return
self.cboInputLayer.blockSignals(True)
self.cboInputLayer.setCurrentIndex( -1 )
self.cboInputLayer.setEditText( path )
self.cboInputLayer.blockSignals(False)
def updateInputLayer(self):
""" create the input layer and update available options """
if self.mode != self.ASK_FOR_INPUT_MODE:
return
self.deleteInputLayer()
index = self.cboInputLayer.currentIndex()
if index < 0:
filename = self.cboInputLayer.currentText()
if filename == "":
return False
layerName = QFileInfo(filename).completeBaseName()
layer = qgis.core.QgsVectorLayer(filename, layerName, "ogr")
if not layer.isValid() or layer.type() != qgis.core.QgsMapLayer.VectorLayer:
layer.deleteLater()
return False
self.inLayer = layer
self.inLayerMustBeDestroyed = True
else:
legendIndex = self.cboInputLayer.itemData( index )
self.inLayer = iface.legendInterface().layers()[ legendIndex ]
self.inLayerMustBeDestroyed = False
# update the output table name
self.cboTable.setEditText(self.inLayer.name())
self.checkSupports()
return True
def populateSchemas(self):
if not self.db:
return
self.cboSchema.clear()
schemas = self.db.schemas()
if schemas == None:
self.hideSchemas()
return
index = -1
for schema in schemas:
self.cboSchema.addItem(schema.name)
if schema.name == self.outUri.schema():
index = self.cboSchema.count()-1
self.cboSchema.setCurrentIndex(index)
def hideSchemas(self):
self.cboSchema.setEnabled(False)
def populateTables(self):
if not self.db:
return
currentText = self.cboTable.currentText()
schemas = self.db.schemas()
if schemas != None:
schema_name = self.cboSchema.currentText()
matching_schemas = filter(lambda x: x.name == schema_name, schemas)
tables = matching_schemas[0].tables() if len(matching_schemas) > 0 else []
else:
tables = self.db.tables()
self.cboTable.clear()
for table in tables:
self.cboTable.addItem(table.name)
self.cboTable.setEditText(currentText)
def populateEncodings(self):
encodings = ['ISO-8859-1', 'ISO-8859-2', 'UTF-8', 'CP1250']
for enc in encodings:
self.cboEncoding.addItem(enc)
self.cboEncoding.setCurrentIndex(2)
def accept(self):
if self.mode == self.ASK_FOR_INPUT_MODE:
# create the input layer (if not already done) and
# update available options w/o changing the tablename!
self.cboTable.blockSignals(True)
table = self.cboTable.currentText()
self.updateInputLayer()
self.cboTable.setEditText(table)
self.cboTable.blockSignals(False)
# sanity checks
if self.inLayer is None:
QMessageBox.information(self, self.tr("Import to database"), self.tr("Input layer missing or not valid"))
return
if self.cboTable.currentText() == "":
QMessageBox.information(self, self.tr("Import to database"), self.tr("Output table name is required"))
return
if self.chkSourceSrid.isEnabled() and self.chkSourceSrid.isChecked():
try:
sourceSrid = self.editSourceSrid.text()
except ValueError:
QMessageBox.information(self, self.tr("Import to database"), self.tr("Invalid source srid: must be an integer"))
return
if self.chkTargetSrid.isEnabled() and self.chkTargetSrid.isChecked():
try:
targetSrid = self.editTargetSrid.text()
except ValueError:
QMessageBox.information(self, self.tr("Import to database"), self.tr("Invalid target srid: must be an integer"))
return
# override cursor
QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
# store current input layer crs and encoding, so I can restore it
prevInCrs = self.inLayer.crs()
prevInEncoding = self.inLayer.dataProvider().encoding()
try:
schema = self.outUri.schema() if not self.cboSchema.isEnabled() else self.cboSchema.currentText()
table = self.cboTable.currentText()
# get pk and geom field names from the source layer or use the
# ones defined by the user
pk = self.outUri.keyColumn() if not self.chkPrimaryKey.isChecked() else self.editPrimaryKey.text()
if self.inLayer.hasGeometryType() and self.chkGeomColumn.isEnabled():
geom = self.outUri.geometryColumn() if not self.chkGeomColumn.isChecked() else self.editGeomColumn.text()
geom = geom if geom != "" else self.default_geom
else:
geom = ""
# get output params, update output URI
self.outUri.setDataSource( schema, table, geom, "", pk )
uri = self.outUri.uri()
providerName = self.db.dbplugin().providerName()
options = {}
if self.radCreate.isChecked() and self.chkDropTable.isChecked():
options['overwrite'] = True
elif self.radAppend.isChecked():
options['append'] = True
if self.chkSinglePart.isEnabled() and self.chkSinglePart.isChecked():
options['forceSinglePartGeometryType'] = True
outCrs = None
if self.chkTargetSrid.isEnabled() and self.chkTargetSrid.isChecked():
targetSrid = int(self.editTargetSrid.text())
outCrs = qgis.core.QgsCoordinateReferenceSystem(targetSrid)
# update input layer crs and encoding
if self.chkSourceSrid.isEnabled() and self.chkSourceSrid.isChecked():
sourceSrid = int(self.editSourceSrid.text())
inCrs = qgis.core.QgsCoordinateReferenceSystem(sourceSrid)
self.inLayer.setCrs( inCrs )
if self.chkEncoding.isEnabled() and self.chkEncoding.isChecked():
enc = self.cboEncoding.currentText()
self.inLayer.setProviderEncoding( enc )
# do the import!
ret, errMsg = qgis.core.QgsVectorLayerImport.importLayer( self.inLayer, uri, providerName, outCrs, False, False, options )
except Exception as e:
ret = -1
errMsg = unicode( e )
finally:
# restore input layer crs and encoding
self.inLayer.setCrs( prevInCrs )
self.inLayer.setProviderEncoding( prevInEncoding )
# restore cursor
QApplication.restoreOverrideCursor()
if ret != 0:
output = qgis.gui.QgsMessageViewer()
output.setTitle( self.tr("Import to database") )
output.setMessageAsPlainText( self.tr("Error %d\n%s") % (ret, errMsg) )
output.showMessage()
return
# create spatial index
if self.chkSpatialIndex.isEnabled() and self.chkSpatialIndex.isChecked():
self.db.connector.createSpatialIndex( (schema, table), geom )
QMessageBox.information(self, self.tr("Import to database"), self.tr("Import was successful."))
return QDialog.accept(self)
def closeEvent(self, event):
# destroy the input layer instance but only if it was created
# from this dialog!
self.deleteInputLayer()
QDialog.closeEvent(self, event)
if __name__ == '__main__':
import sys
a = QApplication(sys.argv)
dlg = DlgLoadData()
dlg.show()
sys.exit(a.exec_())