# -*- coding: utf-8 -*-
"""
/***************************************************************************
Cadastre - Dialog classes
A QGIS plugin
This plugins helps users to import the french land registry ('cadastre')
into a database. It is meant to ease the use of the data in QGIs
by providing search tools and appropriate layer symbology.
-------------------
begin : 2013-06-11
copyright : (C) 2013 by 3liz
email : info@3liz.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. *
* *
***************************************************************************/
"""
import csv
import os.path
import operator
import re
import tempfile
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.gui import QgsGenericProjectionSelector
import unicodedata
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/forms")
from db_manager.db_plugins.plugin import DBPlugin, Schema, Table
from db_manager.db_plugins import createDbPlugin
from db_manager.db_plugins.postgis.connector import PostGisDBConnector
import subprocess
from functools import partial
# --------------------------------------------------------
# import - Import data from EDIGEO and MAJIC files
# --------------------------------------------------------
class cadastre_common():
def __init__(self, dialog):
self.dialog = dialog
# plugin directory path
self.plugin_dir = os.path.dirname(os.path.abspath(__file__))
# default auth id for layers
self.defaultAuthId = 'EPSG:2154'
@staticmethod
def hasSpatialiteSupport():
'''
Check whether or not
spatialite support is ok
'''
try:
from db_manager.db_plugins.spatialite.connector import SpatiaLiteDBConnector
return True
except ImportError:
return False
pass
def updateLog(self, msg):
'''
Update the log
'''
t = self.dialog.txtLog
t.ensureCursorVisible()
prefix = ''
suffix = ''
t.append( '%s %s %s' % (prefix, msg, suffix) )
c = t.textCursor()
c.movePosition(QTextCursor.End, QTextCursor.MoveAnchor)
t.setTextCursor(c)
qApp.processEvents()
@staticmethod
def openFile(filename):
'''
Opens a file with default system app
'''
if sys.platform == "win32":
os.startfile(filename)
else:
opener ="open" if sys.platform == "darwin" else "xdg-open"
subprocess.call([opener, filename])
def updateProgressBar(self):
'''
Update the progress bar
'''
if self.dialog.go:
self.dialog.step+=1
self.dialog.pbProcess.setValue(int(self.dialog.step * 100/self.dialog.totalSteps))
qApp.processEvents()
def updateConnectionList(self):
'''
Update the combo box containing the database connection list
'''
QApplication.setOverrideCursor(Qt.WaitCursor)
dbType = unicode(self.dialog.liDbType.currentText()).lower()
self.dialog.liDbConnection.clear()
if self.dialog.liDbType.currentIndex() != 0:
self.dialog.dbType = dbType
# instance of db_manager plugin class
dbpluginclass = createDbPlugin( dbType )
self.dialog.dbpluginclass = dbpluginclass
# fill the connections combobox
self.dialog.connectionDbList = []
for c in dbpluginclass.connections():
self.dialog.liDbConnection.addItem( unicode(c.connectionName()))
self.dialog.connectionDbList.append(unicode(c.connectionName()))
# Show/Hide database specific pannel
if hasattr(self.dialog, 'databaseSpecificOptions'):
if dbType == 'postgis':
self.dialog.databaseSpecificOptions.setCurrentIndex(0)
else:
self.dialog.databaseSpecificOptions.setCurrentIndex(1)
self.toggleSchemaList(False)
else:
if hasattr(self.dialog, "inDbCreateSchema"):
self.dialog.databaseSpecificOptions.setTabEnabled(0, False)
self.dialog.databaseSpecificOptions.setTabEnabled(1, False)
QApplication.restoreOverrideCursor()
def toggleSchemaList(self, t):
'''
Toggle Schema list and inputs
'''
self.dialog.liDbSchema.setEnabled(t)
if hasattr(self.dialog, "inDbCreateSchema"):
self.dialog.inDbCreateSchema.setEnabled(t)
self.dialog.btDbCreateSchema.setEnabled(t)
self.dialog.databaseSpecificOptions.setTabEnabled(0, t)
self.dialog.databaseSpecificOptions.setTabEnabled(1, not t)
self.dialog.btCreateNewSpatialiteDb.setEnabled(not t)
def updateSchemaList(self):
'''
Update the combo box containing the schema list if relevant
'''
self.dialog.liDbSchema.clear()
QApplication.setOverrideCursor(Qt.WaitCursor)
connectionName = unicode(self.dialog.liDbConnection.currentText())
self.dialog.connectionName = connectionName
dbType = unicode(self.dialog.liDbType.currentText()).lower()
# Deactivate schema fields
self.toggleSchemaList(False)
connection = None
if connectionName:
# Get schema list
dbpluginclass = createDbPlugin( dbType, connectionName )
self.dialog.dbpluginclass = dbpluginclass
try:
connection = dbpluginclass.connect()
except BaseError as e:
DlgDbError.showError(e, self.dialog)
self.dialog.go = False
self.updateLog(e.msg)
QApplication.restoreOverrideCursor()
return
except:
self.dialog.go = False
msg = u"Impossible de récupérer les schémas de la base. Vérifier les informations de connexion."
self.updateLog(msg)
QApplication.restoreOverrideCursor()
return
finally:
QApplication.restoreOverrideCursor()
if connection:
self.dialog.connection = connection
db = dbpluginclass.database()
if db:
self.dialog.db = db
self.dialog.schemaList = []
if dbType == 'postgis':
# Activate schema fields
self.toggleSchemaList(True)
for s in db.schemas():
self.dialog.liDbSchema.addItem( unicode(s.name))
self.dialog.schemaList.append(unicode(s.name))
else:
self.toggleSchemaList(False)
else:
self.toggleSchemaList(False)
QApplication.restoreOverrideCursor()
def checkDatabaseForExistingStructure(self):
'''
Search among a database / schema
if there are alreaday Cadastre structure tables
in it
'''
hasStructure = False
hasData = False
hasMajicData = False
hasMajicDataProp = False
hasMajicDataParcelle = False
hasMajicDataVoie = False
searchTable = u'geo_commune'
majicTableParcelle = u'parcelle'
majicTableProp = u'proprietaire'
majicTableVoie = u'voie'
if self.dialog.db:
if self.dialog.dbType == 'postgis':
schemaSearch = [s for s in self.dialog.db.schemas() if s.name == self.dialog.schema]
schemaInst = schemaSearch[0]
getSearchTable = [a for a in self.dialog.db.tables(schemaInst) if a.name == searchTable]
if self.dialog.dbType == 'spatialite':
getSearchTable = [a for a in self.dialog.db.tables() if a.name == searchTable]
if getSearchTable:
hasStructure = True
# Check for data in it
sql = 'SELECT * FROM "%s" LIMIT 1' % searchTable
if self.dialog.dbType == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.dialog.schema)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.dialog.db.connector, sql)
if ok and rowCount >= 1:
hasData = True
# Check for Majic data in it
sql = 'SELECT * FROM "%s" LIMIT 1' % majicTableParcelle
if self.dialog.dbType == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.dialog.schema)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.dialog.db.connector, sql)
if ok and rowCount >= 1:
hasMajicData = True
hasMajicDataParcelle = True
# Check for Majic data in it
sql = 'SELECT * FROM "%s" LIMIT 1' % majicTableProp
if self.dialog.dbType == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.dialog.schema)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.dialog.db.connector, sql)
if ok and rowCount >= 1:
hasMajicData = True
hasMajicDataProp = True
# Check for Majic data in it
sql = 'SELECT * FROM "%s" LIMIT 1' % majicTableVoie
if self.dialog.dbType == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.dialog.schema)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.dialog.db.connector, sql)
if ok and rowCount >= 1:
hasMajicData = True
hasMajicDataVoie = True
# Set global properties
self.dialog.hasStructure = hasStructure
self.dialog.hasData = hasData
self.dialog.hasMajicData = hasMajicData
self.dialog.hasMajicDataParcelle = hasMajicDataParcelle
self.dialog.hasMajicDataProp = hasMajicDataProp
self.dialog.hasMajicData = hasMajicDataVoie
def checkDatabaseForExistingTable(self, tableName, schemaName=''):
'''
Check if the given table
exists in the database
'''
tableExists = False
if not self.dialog.db:
return False
if self.dialog.dbType == 'postgis':
sql = "SELECT * FROM information_schema.tables WHERE table_schema = '%s' AND table_name = '%s'" % (schemaName, tableName)
if self.dialog.dbType == 'spatialite':
sql = "SELECT name FROM sqlite_master WHERE type='table' AND name='%s'" % tableName
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.dialog.db.connector, sql)
if ok and rowCount >= 1:
tableExists = True
return tableExists
@staticmethod
def getLayerFromLegendByTableProps(tableName, geomCol='geom', sql=''):
'''
Get the layer from QGIS legend
corresponding to a database
table name (postgis or sqlite)
'''
layer = None
lr = QgsMapLayerRegistry.instance()
for lid,l in lr.mapLayers().items():
if not hasattr(l, 'providerType'):
continue
if hasattr(l, 'type') and l.type() != 0:
continue
if not l.providerType() in (u'postgres', u'spatialite'):
continue
connectionParams = cadastre_common.getConnectionParameterFromDbLayer(l)
import re
reg = r'(\.| )?(%s)' % tableName
if connectionParams and \
( \
connectionParams['table'] == tableName or \
( re.findall(reg, '%s' % connectionParams['table']) and re.findall(reg, '%s' % connectionParams['table'])[0] ) \
) and \
connectionParams['geocol'] == geomCol:
#and connectionParams['sql'] == sql:
return l
return layer
@staticmethod
def getConnectionParameterFromDbLayer(layer):
'''
Get connection parameters
from the layer datasource
'''
connectionParams = None
# Get params via regex
uri = layer.dataProvider().dataSourceUri()
reg = "(?:service='([^ ]+)' )?(?:dbname='([^ ]+)' )?(?:host=([^ ]+) )?(?:port=([0-9]+) )?(?:user='([^ ]+)' )?(?:password='([^ ]+)' )?(?:sslmode=([^ ]+) )?(?:key='([^ ]+)' )?(?:estimatedmetadata=([^ ]+) )?(?:srid=([0-9]+) )?(?:type=([a-zA-Z]+) )?(?:table=\"(.+)\" \()?(?:([^ ]+)\) )?(?:sql=(.*))?"
result = re.findall(r'%s' % reg, uri)
if not result:
return None
res = result[0]
if not res:
return None
service = res[0]
dbname = res[1]
host = res[2]
port = res[3]
user = res[4]
password = res[5]
sslmode = res[6]
key = res[7]
estimatedmetadata = res[8]
srid = res[9]
gtype = res[10]
table = res[11]
geocol = res[12]
sql = res[13]
schema = ''
if ' FROM ' not in table:
if re.search('"\."', table):
table = '"' + table + '"'
sp = table.replace('"', '').split('.')
schema = sp[0]
table = sp[1]
else:
reg = r'\* FROM ([^\)]+)?(\))?'
f = re.findall(r'%s' % reg, table)
if f and f[0]:
sp = f[0][0].replace('"', '').split('.')
if len(sp) > 1:
schema = sp[0].replace('\\', '')
table = sp[1]
else:
table = sp[0]
else:
return None
if layer.providerType() == u'postgres':
dbType = 'postgis'
else:
dbType = 'spatialite'
connectionParams = {
'service' : service,
'dbname' : dbname,
'host' : host,
'port': port,
'user' : user,
'password': password,
'sslmode' : sslmode,
'key': key,
'estimatedmetadata' : estimatedmetadata,
'srid' : srid,
'type': gtype,
'schema': schema,
'table' : table,
'geocol' : geocol,
'sql' : sql,
'dbType': dbType
}
return connectionParams
@staticmethod
def setSearchPath(sql, schema):
'''
Set the search_path parameters if postgis database
'''
prefix = u'SET search_path = "%s", public, pg_catalog;' % schema
if re.search('^BEGIN;', sql):
sql = sql.replace('BEGIN;', 'BEGIN;%s' % prefix)
else:
sql = prefix + sql
return sql
@staticmethod
def fetchDataFromSqlQuery(connector, sql, schema=None):
'''
Execute a SQL query and
return [header, data, rowCount]
NB: commit qgis/QGIS@14ab5eb changes QGIS DBmanager behaviour
'''
data = []
header = []
rowCount = 0
c = None
ok = True
try:
c = connector._execute(None,unicode(sql).encode('utf-8'))
data = []
header = connector._get_cursor_columns(c)
if header == None:
header = []
if len(header) > 0:
data = connector._fetchall(c)
rowCount = c.rowcount
if rowCount == -1:
rowCount = len(data)
except UnicodeDecodeError as e:
try:
c = connector._execute(None,unicode(sql))
data = []
header = connector._get_cursor_columns(c)
if header == None:
header = []
if len(header) > 0:
data = connector._fetchall(c)
rowCount = c.rowcount
if rowCount == -1:
rowCount = len(data)
except BaseError as e:
ok = False
error_message = e.msg
except BaseError as e:
ok = False
error_message = e.msg
finally:
if c:
c.close()
del c
# Log errors
if not ok:
print error_message
QgsMessageLog.logMessage( "cadastre debug - error while fetching data from database" )
return
return [header, data, rowCount, ok]
@staticmethod
def getConnectorFromUri(connectionParams):
'''
Set connector property
for the given database type
and parameters
'''
connector = None
uri = QgsDataSourceURI()
if connectionParams['dbType'] == 'postgis':
if connectionParams['host']:
uri.setConnection(
connectionParams['host'],
connectionParams['port'],
connectionParams['dbname'],
connectionParams['user'],
connectionParams['password']
)
if connectionParams['service']:
uri.setConnection(
connectionParams['service'],
connectionParams['dbname'],
connectionParams['user'],
connectionParams['password']
)
connector = PostGisDBConnector(uri)
if connectionParams['dbType'] == 'spatialite':
uri.setConnection('', '', connectionParams['dbname'], '', '')
if cadastre_common.hasSpatialiteSupport():
from db_manager.db_plugins.spatialite.connector import SpatiaLiteDBConnector
connector = SpatiaLiteDBConnector(uri)
return connector
def normalizeString(self, s):
'''
Removes all accents from
the given string and
replace e dans l'o
'''
p = re.compile( '(œ)')
s = p.sub('oe', s)
if isinstance(s,str):
s = unicode(s,"utf8","replace")
s=unicodedata.normalize('NFD',s)
s = s.encode('ascii','ignore')
s = s.upper().strip(' \t\n')
r = re.compile(r"[^ -~]")
s = r.sub(' ', s)
s = s.replace("'", " ")
return s
@staticmethod
def postgisToSpatialite(sql, targetSrid='2154'):
'''
Convert postgis SQL statement
into spatialite compatible
statements
'''
# delete some incompatible options
# replace other by spatialite syntax
replaceDict = [
# delete
{'in': r'with\(oids=.+\)', 'out': ''},
{'in': r'comment on [^;]+;', 'out': ''},
{'in': r'alter table ([^;]+) add primary key( )+\(([^;]+)\);',
'out': r'create index idx_\1_\3 on \1 (\3);'},
{'in': r'alter table ([^;]+) add constraint [^;]+ primary key( )+\(([^;]+)\);',
'out': r'create index idx_\1_\3 on \1 (\3);'},
{'in': r'alter table [^;]+drop column[^;]+;', 'out': ''},
{'in': r'alter table [^;]+drop constraint[^;]+;', 'out': ''},
#~ {'in': r'^analyse [^;]+;', 'out': ''},
# replace
{'in': r'truncate (bati|fanr|lloc|nbat|pdll|prop)',
'out': r'drop table \1;create table \1 (tmp text)'},
{'in': r'truncate ', 'out': 'delete from '},
{'in': r'distinct on *\([a-z, ]+\)', 'out': 'distinct'},
{'in': r'serial', 'out': 'INTEGER PRIMARY KEY AUTOINCREMENT'},
{'in': r'string_agg', 'out': 'group_concat'},
{'in': r'current_schema::text, ', 'out': ''},
{'in': r'substring', 'out': 'SUBSTR'},
{'in': r"(to_char\()([^']+) *, *'[09]+' *\)", 'out': r"CAST(\2 AS TEXT)"},
{'in': r"(to_number\()([^']+) *, *'[09]+' *\)", 'out': r"CAST(\2 AS float)"},
{'in': r"(to_date\()([^']+) *, *'DDMMYYYY' *\)",
'out': r"date(substr(\2, 5, 4) || '-' || substr(\2, 3, 2) || '-' || substr(\2, 1, 2))"},
{'in': r"(to_date\()([^']+) *, *'DD/MM/YYYY' *\)",
'out': r"date(substr(\2, 7, 4) || '-' || substr(\2, 4, 2) || '-' || substr(\2, 1, 2))"},
{'in': r"(to_date\()([^']+) *, *'YYYYMMDD' *\)",
'out': r"date(substr(\2, 1, 4) || '-' || substr(\2, 5, 2) || '-' || substr(\2, 7, 2))"},
{'in': r"(to_char\()([^']+) *, *'dd/mm/YYYY' *\)",
'out': r"strftime('%d/%m/%Y', \2)"},
{'in': r"ST_MakeValid\(geom\)",
'out': r"CASE WHEN ST_IsValid(geom) THEN geom ELSE ST_Buffer(geom,0) END"},
{'in': r"ST_MakeValid\(p\.geom\)",
'out': r"CASE WHEN ST_IsValid(p.geom) THEN p.geom ELSE ST_Buffer(p.geom,0) END"}
]
for a in replaceDict:
r = re.compile(a['in'], re.IGNORECASE|re.MULTILINE)
sql = r.sub(a['out'], sql)
#self.updateLog(sql)
# index spatiaux
r = re.compile(r'(create index [^;]+ ON )([^;]+)( USING +)(gist +)?\(([^;]+)\);', re.IGNORECASE|re.MULTILINE)
sql = r.sub(r"SELECT createSpatialIndex('\2', '\5');", sql)
# replace postgresql "update from" statement
r = re.compile(r'(update [^;=]+)(=)([^;=]+ FROM [^;]+)(;)', re.IGNORECASE|re.MULTILINE)
sql = r.sub(r'\1=(SELECT \3);', sql)
#self.updateLog(sql)
return sql
@staticmethod
def postgisToSpatialiteLocal10(sql, dataYear):
# majic formatage : replace multiple column update for loca10
r = re.compile(r'update local10 set[^;]+;', re.IGNORECASE|re.MULTILINE)
res = r.findall(sql)
replaceBy = ''
for statement in res:
replaceBy = '''
CREATE TABLE ll AS
SELECT DISTINCT l.invar, l.ccopre , l.ccosec, l.dnupla, l.ccoriv, l.ccovoi, l.dnvoiri, l10.annee || l10.invar AS local00, REPLACE(l10.annee||l10.ccodep || l10.ccodir || l10.ccocom || l.ccopre || l.ccosec || l.dnupla,' ', '0') AS parcelle, REPLACE(l10.annee || l10.ccodep || l10.ccodir || l10.ccocom || l.ccovoi,' ', '0') AS voie
FROM local00 l
INNER JOIN local10 AS l10 ON l.invar = l10.invar AND l.annee = l10.annee
WHERE l10.annee='?';
CREATE INDEX idx_ll_invar ON ll (invar);
UPDATE local10 SET ccopre = (SELECT DISTINCT ll.ccopre FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET ccosec = (SELECT DISTINCT ll.ccosec FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET dnupla = (SELECT DISTINCT ll.dnupla FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET ccoriv = (SELECT DISTINCT ll.ccoriv FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET ccovoi = (SELECT DISTINCT ll.ccovoi FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET dnvoiri = (SELECT DISTINCT ll.dnvoiri FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET local00 = (SELECT DISTINCT ll.local00 FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET parcelle = (SELECT DISTINCT ll.parcelle FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
UPDATE local10 SET voie = (SELECT DISTINCT ll.voie FROM ll WHERE ll.invar = local10.invar)
WHERE local10.annee = '?';
DROP TABLE ll;
'''
replaceBy = replaceBy.replace('?', dataYear)
sql = sql.replace(statement, replaceBy)
#self.updateLog(sql)
return sql
def createNewSpatialiteDatabase(self):
'''
Choose a file path to save
create the sqlite database with
spatial tools and create QGIS connection
'''
# Let the user choose new file path
ipath = QFileDialog.getSaveFileName (
None,
u"Choisir l'emplacement du nouveau fichier",
str(os.path.expanduser("~").encode('utf-8')).strip(' \t'),
"Sqlite database (*.sqlite)"
)
if not ipath:
self.updateLog(u"Aucune base de données créée (annulation)")
return None
# Delete file if exists (question already asked above)
if os.path.exists(unicode(ipath)):
os.remove(unicode(ipath))
# Create the spatialite database
try:
from pyspatialite import dbapi2 as db
# Create a connection (which will create the file automatically)
conn=db.connect(unicode(ipath))
c=conn.cursor()
# Get spatialite version
cursor = conn.execute('SELECT spatialite_version()')
rep = cursor.fetchall()
# v = [int(a) for a in rep[0][0].split('.')]
v = [int(re.findall(r'\d+', a)[0]) for a in rep[0][0].split('.')]
# pretty complicated, but it avoids a bug with some versions like 4.3.0a
vv = v[0] * 100000 + v[1] * 1000 + v[2] * 10
# Add spatialite support
if vv >= 401000:
# 4.1 and above
sql = "SELECT initspatialmetadata(1)"
else:
# Under 4.1
sql = "SELECT initspatialmetadata()"
c.execute(sql)
except:
self.updateLog(u"Échec lors de la création du fichier Spatialite !")
return None
finally:
conn.close()
del conn
# Create QGIS connexion
baseKey = "/SpatiaLite/connections/"
settings = QSettings()
myName = os.path.basename(ipath);
baseKey+= myName;
myFi = QFileInfo(ipath)
settings.setValue( baseKey + "/sqlitepath", myFi.canonicalFilePath());
# Update connections combo box and set new db selected
self.updateConnectionList()
listDic = { self.dialog.connectionDbList[i]:i for i in range(0, len(self.dialog.connectionDbList)) }
self.dialog.liDbConnection.setCurrentIndex(listDic[myName])
@staticmethod
def getCompteCommunalFromParcelleId(parcelleId, connectionParams, connector):
comptecommunal = None
sql = "SELECT comptecommunal FROM parcelle WHERE parcelle = '%s'" % parcelleId
if connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, connectionParams['schema'])
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector, sql)
if ok:
for line in data:
comptecommunal = line[0]
return comptecommunal
@staticmethod
def getProprietaireComptesCommunaux(comptecommunal, connectionParams, connector):
'''
Get the list of "comptecommunal" for all cities
for a owner given one single comptecommunal
'''
cc = comptecommunal
sql = " SELECT trim(ddenom) AS k, MyStringAgg(comptecommunal, ',') AS cc, dnuper"
sql+= " FROM proprietaire p"
sql+= " WHERE 2>1"
sql+= " AND ddenom IN (SELECT ddenom FROM proprietaire WHERE comptecommunal = %s)" % connector.quoteString( comptecommunal )
sql+= " GROUP BY dnuper, ddenom, dlign4"
sql+= " ORDER BY ddenom"
if connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, connectionParams['schema'])
sql = sql.replace('MyStringAgg', 'string_agg')
if connectionParams['dbType'] == 'spatialite':
sql = sql.replace('MyStringAgg', 'group_concat')
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector,sql)
if ok:
for line in data:
cc = line[1].split(',')
return cc
from cadastre_import_form import *
from cadastre_import import *
class cadastre_import_dialog(QDialog, Ui_cadastre_import_form):
def __init__(self, iface):
QDialog.__init__(self)
self.iface = iface
self.setupUi(self)
self.connectionDbList = []
# common cadastre methods
from cadastre_dialogs import cadastre_common
self.qc = cadastre_common(self)
# first disable database specifi tabs
self.databaseSpecificOptions.setTabEnabled(0, False)
self.databaseSpecificOptions.setTabEnabled(1, False)
# spatialite support
self.hasSpatialiteSupport = cadastre_common.hasSpatialiteSupport()
if not self.hasSpatialiteSupport:
self.liDbType.removeItem(2)
self.databaseSpecificOptions.setTabEnabled(1, False)
self.btCreateNewSpatialiteDb.setEnabled(False)
# Signals/Slot Connections
self.liDbType.currentIndexChanged[str].connect(self.qc.updateConnectionList)
self.liDbConnection.currentIndexChanged[str].connect(self.qc.updateSchemaList)
self.btDbCreateSchema.clicked.connect(self.createSchema)
self.btCreateNewSpatialiteDb.clicked.connect(self.qc.createNewSpatialiteDatabase)
self.btProcessImport.clicked.connect(self.processImport)
self.rejected.connect(self.onClose)
self.buttonBox.rejected.connect(self.onClose)
# path buttons selectors
# paths needed to be chosen by user
self.pathSelectors = {
"edigeoSourceDir" : {
"button" : self.btEdigeoSourceDir,
"input" : self.inEdigeoSourceDir
},
"majicSourceDir" : {
"button" : self.btMajicSourceDir,
"input" : self.inMajicSourceDir
}
}
for key, item in self.pathSelectors.items():
control = item['button']
slot = partial(self.chooseDataPath, key)
control.clicked.connect(slot)
# projection selector
self.projSelectors = {
"edigeoSourceProj" : {
"button" : self.btEdigeoSourceProj,
"input" : self.inEdigeoSourceProj,
"sentence" : "Choisir la projection des fichiers Edigeo"
},
"edigeoTargetProj" : {
"button" : self.btEdigeoTargetProj,
"input" : self.inEdigeoTargetProj,
"sentence" : "Choisir la projection de destination"
}
}
for key, item in self.projSelectors.items():
control = item['button']
slot = partial(self.chooseProjection, key)
control.clicked.connect(slot)
# Set initial values
self.doMajicImport = False
self.doEdigeoImport = False
self.dataVersion = None
self.dataYear = None
self.dbType = None
self.dbpluginclass = None
self.connectionName = None
self.connection = None
self.db = None
self.schema = None
self.schemaList = None
self.hasStructure = None
self.hasData = None
self.hasMajicData = None
self.hasMajicDataParcelle = None
self.hasMajicDataVoie = None
self.hasMajicDataProp = None
self.edigeoSourceProj = None
self.edigeoTargetProj = None
self.edigeoDepartement = None
self.edigeoDirection = None
self.edigeoLot = None
self.majicSourceDir = None
self.edigeoSourceDir = None
self.edigeoMakeValid = False
# set input values from settings
self.sList = {
'dataVersion': {
'widget': self.inDataVersion,
'wType': 'spinbox',
'property': self.dataVersion
},
'dataYear': {
'widget': self.inDataYear,
'wType': 'spinbox',
'property': self.dataYear
} ,
'schema': {
'widget': None
} ,
'majicSourceDir': {
'widget': self.inMajicSourceDir,
'wType': 'text',
'property': self.majicSourceDir
},
'edigeoSourceDir': {
'widget': self.inEdigeoSourceDir,
'wType': 'text',
'property': self.edigeoSourceDir
},
'edigeoDepartement': {
'widget': self.inEdigeoDepartement,
'wType': 'text',
'property': self.edigeoDepartement
},
'edigeoDirection': {
'widget': self.inEdigeoDirection,
'wType': 'spinbox',
'property': self.edigeoDirection
},
'edigeoLot': {
'widget': self.inEdigeoLot,
'wType': 'text',
'property': self.edigeoLot
},
'edigeoSourceProj': {
'widget': self.inEdigeoSourceProj,
'wType': 'text',
'property': self.edigeoSourceProj
},
'edigeoTargetProj': {
'widget': self.inEdigeoTargetProj,
'wType': 'text',
'property': self.edigeoTargetProj
}
}
self.getValuesFromSettings()
def onClose(self):
'''
Close dialog
'''
if self.db:
self.db.connector.__del__()
# Store settings
msg = self.checkImportInputData()
if not msg:
self.storeSettings()
self.close()
def chooseDataPath(self, key):
'''
Ask the user to select a folder
and write down the path to appropriate field
'''
ipath = QFileDialog.getExistingDirectory(
None,
u"Choisir le répertoire contenant les fichiers",
str(self.pathSelectors[key]['input'].text().encode('utf-8')).strip(' \t')
)
if os.path.exists(unicode(ipath)):
self.pathSelectors[key]['input'].setText(unicode(ipath))
def getValuesFromSettings(self):
'''
get values from QGIS settings
and set input fields appropriately
'''
s = QSettings()
for k,v in self.sList.items():
value = s.value("cadastre/%s" % k, '', type=str)
if value and value != 'None' and v['widget']:
if v['wType'] == 'text':
v['widget'].setText(value)
if v['wType'] == 'spinbox':
v['widget'].setValue(int(value))
if v['wType'] == 'combobox':
listDic = {v['list'][i]:i for i in range(0, len(v['list']))}
v['widget'].setCurrentIndex(listDic[value])
def createSchema(self):
QApplication.setOverrideCursor(Qt.WaitCursor)
try:
if self.db == None:
QMessageBox.warning(
self,
QApplication.translate("DBManagerPlugin", "Sorry"),
QApplication.translate("DBManagerPlugin", "No database selected or you are not connected to it.")
)
return
schema = self.inDbCreateSchema.text()
finally:
QApplication.restoreOverrideCursor()
if schema:
try:
self.db.createSchema(schema)
except BaseError as e:
DlgDbError.showError(e, self)
self.qc.updateLog(e.msg)
return
finally:
self.qc.updateSchemaList()
listDic = { self.schemaList[i]:i for i in range(0, len(self.schemaList)) }
self.liDbSchema.setCurrentIndex(listDic[schema])
self.inDbCreateSchema.clear()
QApplication.restoreOverrideCursor()
def chooseProjection(self, key):
'''
Let the user choose a SCR
'''
header = u"Choisir la projection"
sentence = self.projSelectors[key]['sentence']
projSelector = QgsGenericProjectionSelector(self)
projSelector.setMessage( "
%s
%s" % (header.encode('UTF8'), sentence.encode('UTF8')) )
projSelector.setSelectedAuthId(self.qc.defaultAuthId)
if projSelector.exec_():
self.crs = QgsCoordinateReferenceSystem( projSelector.selectedCrsId(), QgsCoordinateReferenceSystem.InternalCrsId )
if len(projSelector.selectedAuthId()) == 0:
QMessageBox.information(
self,
self.tr("Cadastre"),
self.tr(u"Aucun système de coordonnée de référence valide n'a été sélectionné")
)
return
else:
self.projSelectors[key]['input'].clear()
self.projSelectors[key]['input'].setText(self.crs.authid() + " - " + self.crs.description())
else:
return
def checkImportInputData(self):
'''
Check the user defined inpu data
'''
self.dataVersion = unicode(self.inDataVersion.text())
self.dataYear = unicode(self.inDataYear.text())
self.schema = unicode(self.liDbSchema.currentText())
self.majicSourceDir = unicode(self.inMajicSourceDir.text()).strip(' \t')
self.edigeoSourceDir = unicode(self.inEdigeoSourceDir.text()).strip(' \t')
self.edigeoDepartement = unicode(self.inEdigeoDepartement.text()).strip(' \t')
self.edigeoDirection = unicode(self.inEdigeoDirection.text()).strip(' \t')
self.edigeoLot = unicode(self.inEdigeoLot.text()).strip(' \t')
self.edigeoSourceProj = unicode(self.inEdigeoSourceProj.text().split( " - " )[ 0 ])
self.edigeoTargetProj = unicode(self.inEdigeoTargetProj.text().split( " - " )[ 0 ])
# defined properties
self.doMajicImport = os.path.exists(self.majicSourceDir)
self.doEdigeoImport = os.path.exists(self.edigeoSourceDir)
if self.cbMakeValid.isChecked():
self.edigeoMakeValid = True
msg = ''
if not self.db:
msg+= u'Veuillez sélectionner une base de données\n'
if not self.doMajicImport and not self.doEdigeoImport:
msg+= u'Veuillez sélectionner le chemin vers les fichiers à importer !\n'
if self.edigeoSourceDir and not self.doEdigeoImport:
msg+= u"Le chemin spécifié pour les fichiers EDIGEO n'existe pas\n"
if self.majicSourceDir and not self.doMajicImport:
msg+= u"Le chemin spécifié pour les fichiers MAJIC n'existe pas\n"
if self.doEdigeoImport and not self.edigeoSourceProj:
msg+= u'La projection source doit être renseignée !\n'
if self.doEdigeoImport and not self.edigeoTargetProj:
msg+= u'La projection cible doit être renseignée !\n'
if len(self.edigeoDepartement) != 2 :
msg+= u'Le département ne doit pas être vide !\n'
if not self.edigeoDirection:
msg+= u'La direction doit être un entier (0 par défaut) !\n'
if not self.edigeoLot:
msg+= u'Merci de renseigner un lot pour cet import (code commune, date d\'import, etc.)\n'
self.qc.updateLog(msg.replace('\n','
'))
return msg
def processImport(self):
'''
Lancement du processus d'import
'''
msg = self.checkImportInputData()
if msg:
QMessageBox.critical(self, u"Cadastre", msg)
return
# Store settings
self.storeSettings()
# cadastreImport instance
qi = cadastreImport(self)
# Check if structure already exists in the database/schema
self.qc.checkDatabaseForExistingStructure()
# Run Script for creating tables
if not self.hasStructure:
qi.installCadastreStructure()
else:
# Run update script which add some missing tables when needed
qi.updateCadastreStructure()
# Run MAJIC import
if self.doMajicImport:
qi.importMajic()
# Run Edigeo import
if self.doEdigeoImport:
qi.importEdigeo()
qi.endImport()
def storeSettings(self):
'''
Store cadastre settings in QGIS
'''
# store chosen data in QGIS settings
s = QSettings()
s.setValue("cadastre/dataVersion", str(self.dataVersion))
s.setValue("cadastre/dataYear", int(self.dataYear))
s.setValue("cadastre/majicSourceDir", self.majicSourceDir)
s.setValue("cadastre/edigeoSourceDir", self.edigeoSourceDir)
s.setValue("cadastre/edigeoDepartement", str(self.edigeoDepartement))
s.setValue("cadastre/edigeoDirection", int(self.edigeoDirection))
s.setValue("cadastre/edigeoLot", str(self.edigeoLot))
s.setValue("cadastre/edigeoSourceProj", str(self.edigeoSourceProj))
s.setValue("cadastre/edigeoTargetProj", str(self.edigeoTargetProj))
# --------------------------------------------------------
# load - Load data from database
# --------------------------------------------------------
from cadastre_load_form import *
from cadastre_loading import *
class cadastre_load_dialog(QDialog, Ui_cadastre_load_form):
def __init__(self, iface, cadastre_search_dialog):
QDialog.__init__(self)
self.iface = iface
self.setupUi(self)
self.mc = self.iface.mapCanvas()
self.cadastre_search_dialog = cadastre_search_dialog
# common cadastre methods
from cadastre_dialogs import cadastre_common
self.qc = cadastre_common(self)
self.ql = cadastreLoading(self)
# spatialite support
self.hasSpatialiteSupport = cadastre_common.hasSpatialiteSupport()
if not self.hasSpatialiteSupport:
self.liDbType.removeItem(2)
# Set initial values
self.go = True
self.step = 0
self.totalSteps = 0
self.dbType = None
self.dbpluginclass = None
self.connectionName = None
self.connection = None
self.db = None
self.schema = None
self.schemaList = None
self.hasStructure = None
# Get style list
self.getStyleList()
# Signals/Slot Connections
self.liDbType.currentIndexChanged[str].connect(self.qc.updateConnectionList)
self.liDbConnection.currentIndexChanged[str].connect(self.qc.updateSchemaList)
self.btProcessLoading.clicked.connect(self.onProcessLoadingClicked)
self.ql.cadastreLoadingFinished.connect(self.onLoadingEnd)
self.rejected.connect(self.onClose)
self.buttonBox.rejected.connect(self.onClose)
def onClose(self):
'''
Close dialog
'''
if self.db:
self.db.connector.__del__()
self.close()
def getStyleList(self):
'''
Get the list of style directories
inside the plugin dir
and add combobox item
'''
spath = os.path.join(self.qc.plugin_dir, "styles/")
dirs = os.listdir(spath)
dirs = [a for a in dirs if os.path.isdir(os.path.join(spath, a))]
dirs.sort()
cb = self.liTheme
cb.clear()
for d in dirs:
cb.addItem('%s' % d, d)
def onProcessLoadingClicked(self):
'''
Activate the loading of layers
from database tables
when user clicked on button
'''
if self.connection:
if self.db:
self.ql.processLoading()
def onLoadingEnd(self):
'''
Actions to trigger
when all the layers
have been loaded
'''
self.cadastre_search_dialog.checkMajicContent()
self.cadastre_search_dialog.clearComboboxes()
self.cadastre_search_dialog.setupSearchCombobox('commune', None, 'sql')
#self.cadastre_search_dialog.setupSearchCombobox('section', None, 'sql')
# ---------------------------------------------------------
# search - search for data among database ans export
# ---------------------------------------------------------
from cadastre_search_form import *
from cadastre_export import *
class cadastre_search_dialog(QDockWidget, Ui_cadastre_search_form):
def __init__(self, iface):
QDockWidget.__init__(self)
self.iface = iface
self.setupUi(self)
self.iface.addDockWidget(Qt.RightDockWidgetArea, self)
# common cadastre methods
from cadastre_dialogs import cadastre_common
self.qc = cadastre_common(self)
# database properties
self.connectionParams = None
self.connector = None
self.dbType = None
self.schema = None
self.mc = self.iface.mapCanvas()
self.communeLayer = None
self.communeFeatures = None
self.communeRequest = None
self.selectedCommuneFeature = None
self.sectionLayer = None
self.sectionFeatures = None
self.sectionRequest = None
self.sectionCommuneFeature = None
aLayer = cadastre_common.getLayerFromLegendByTableProps('geo_commune')
if aLayer:
self.connectionParams = cadastre_common.getConnectionParameterFromDbLayer(aLayer)
self.connector = cadastre_common.getConnectorFromUri( self.connectionParams )
# signals/slots
self.searchComboBoxes = {
'commune': {
'widget': self.liCommune,
'labelAttribute': 'tex2',
'table': 'geo_commune', 'geomCol': 'geom', 'sql': '',
'layer': None,
'request': None,
'attributes': ['ogc_fid','tex2','idu','geo_commune','geom', 'lot'],
'orderBy': ['tex2'],
'features': None,
'chosenFeature': None,
'resetWidget': self.btResetCommune,
'child': {
'key': 'section',
'fkey': 'geo_commune',
'getIfNoFeature': True
}
},
'section': {
'widget': self.liSection,
'labelAttribute': 'idu',
'table': 'geo_section', 'geomCol': 'geom', 'sql': '',
'layer': None,
'request': None,
'attributes': ['ogc_fid','tex','idu','geo_commune','geo_section','geom','lot'],
'orderBy': ['geo_section'],
'features': None,
'chosenFeature': None,
'resetWidget': self.btResetSection,
'child': {
'key': 'parcelle',
'fkey': 'geo_section',
'getIfNoFeature': False
}
},
'parcelle': {
'widget': self.liParcelle,
'labelAttribute': 'idu',
'table': 'parcelle_info', 'geomCol': 'geom', 'sql': '',
'layer': None,
'request': None,
'attributes': ['ogc_fid','tex','idu','geo_section','geom', 'comptecommunal', 'geo_parcelle'],
'orderBy': ['geo_parcelle'],
'features': None,
'chosenFeature': None,
'connector': None,
'resetWidget': self.btResetParcelle
},
'proprietaire': {
'widget': self.liProprietaire,
'labelAttribute': 'idu',
'table': 'parcelle_info',
'layer': None,
'request': None,
'attributes': ['comptecommunal','idu','dnupro','geom'],
'orderBy': ['ddenom'],
'features': None,
'id': None,
'chosenFeature': None,
'connector': None,
'search': {
'button' : self.btSearchProprietaire,
'child': 'parcelle_proprietaire',
'minlen': 3
}
},
'parcelle_proprietaire': {
'widget': self.liParcelleProprietaire,
'labelAttribute': 'idu',
'table': 'parcelle_info', 'geomCol': 'geom', 'sql': '',
'layer': None,
'request': None,
'attributes': ['ogc_fid','tex','idu','comptecommunal','geom', 'geo_parcelle'],
'orderBy': ['geo_parcelle'],
'features': None,
'chosenFeature': None,
'connector': None,
'resetWidget': self.btResetParcelleProprietaire
},
'adresse': {
'widget': self.liAdresse,
'labelAttribute': 'voie',
'table': 'parcelle_info',
'layer': None,
'request': None,
'attributes': ['ogc_fid','voie','idu','geom'],
'orderBy': ['voie'],
'features': None,
'chosenFeature': None,
'connector': None,
'search': {
'button' : self.btSearchAdresse,
'child': 'parcelle_adresse',
'minlen': 3
}
},
'parcelle_adresse': {
'widget': self.liParcelleAdresse,
'labelAttribute': 'idu',
'table': 'parcelle_info', 'geomCol': 'geom', 'sql': '',
'layer': None,
'request': None,
'attributes': ['ogc_fid','tex','idu','voie','geom', 'comptecommunal', 'geo_parcelle'],
'orderBy': ['geo_parcelle'],
'features': None,
'chosenFeature': None,
'connector': None,
'resetWidget': self.btResetParcelleAdresse
}
}
# Detect that the user has hidden/showed the dock
self.visibilityChanged.connect(self.onVisibilityChange)
# center/zoom/selection buttons
self.zoomButtons = {
'lieu':{
'buttons':{
'centre': self.btCentrerLieu,
'zoom': self.btZoomerLieu,
'select': self.btSelectionnerLieu
},
'comboboxes': ['commune', 'section', 'parcelle']
},
'adresse':{
'buttons':{
'centre': self.btCentrerAdresse,
'zoom': self.btZoomerAdresse,
'select': self.btSelectionnerAdresse
},
'comboboxes': ['adresse', 'parcelle_adresse']
},
'proprietaire':{
'buttons':{
'centre': self.btCentrerProprietaire,
'zoom': self.btZoomerProprietaire,
'select': self.btSelectionnerProprietaire
},
'comboboxes': ['proprietaire', 'parcelle_proprietaire']
}
}
zoomButtonsFunctions = {
'centre': self.setCenterToChosenItem,
'zoom': self.setZoomToChosenItem,
'select': self.setSelectionToChosenItem
}
for key, item in self.zoomButtons.items():
for k, button in item['buttons'].items():
control = button
slot = partial(zoomButtonsFunctions[k], key)
control.clicked.connect(slot)
# Manuel search button and combo (proprietaire, adresse)
for key, item in self.searchComboBoxes.items():
# manual search widgets
if item.has_key('search'):
# search button
control = item['search']['button']
slot = partial(self.searchItem, key)
control.clicked.connect(slot)
# connect Enter key pressed event
item['widget'].lineEdit().returnPressed.connect(slot)
# when a search result is chosen in combobox
control = item['widget']
slot = partial(self.onSearchItemChoose, key)
control.currentIndexChanged[str].connect(slot)
else:
control = item['widget']
# when the user edits the combobox content
slot = partial(self.onNonSearchItemEdit, key)
control.editTextChanged[str].connect(slot)
# when the user chooses in the list
slot = partial(self.onNonSearchItemChoose, key)
control.currentIndexChanged[str].connect(slot)
# when the user reset the entered value
control = item['resetWidget']
slot = partial(self.onNonSearchItemReset, key)
control.clicked.connect(slot)
# export buttons
self.btExportProprietaire.clicked.connect(self.exportProprietaire)
self.exportParcelleButtons = {
'parcelle': self.btExportParcelle,
'parcelle_adresse': self.btExportParcelleAdresse,
'parcelle_proprietaire': self.btExportParcelleProprietaire
}
for key, item in self.exportParcelleButtons.items():
control = item
slot = partial(self.exportParcelle, key)
control.clicked.connect(slot)
# setup some gui items
self.setupSearchCombobox('commune', None, 'sql')
#self.setupSearchCombobox('section', None, 'sql')
# Check majic content
self.hasMajicDataProp = False
self.hasMajicDataVoie = False
self.hasMajicDataParcelle = False
self.checkMajicContent()
# signals
def clearComboboxes(self):
'''
Clear comboboxes content
'''
self.txtLog.clear()
for key, item in self.searchComboBoxes.items():
# manual search widgets
if item.has_key('widget'):
item['widget'].clear()
def checkMajicContent(self):
'''
Check if database contains
any MAJIC data
'''
self.hasMajicDataProp = False
self.hasMajicDataVoie = False
self.hasMajicDataParcelle = False
from cadastre_dialogs import cadastre_common
aLayer = cadastre_common.getLayerFromLegendByTableProps('geo_commune')
if aLayer:
self.connectionParams = cadastre_common.getConnectionParameterFromDbLayer(aLayer)
# Get connection parameters
if self.connectionParams:
# Get Connection params
connector = cadastre_common.getConnectorFromUri(self.connectionParams)
if connector:
# Get data from table proprietaire
sql = 'SELECT * FROM "proprietaire" LIMIT 1'
if self.connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector, sql)
if ok and rowCount >= 1:
self.hasMajicDataProp = True
# Get data from table voie
sql = 'SELECT * FROM "voie" LIMIT 1'
if self.connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector, sql)
if ok and rowCount >= 1:
self.hasMajicDataVoie = True
# Get data from table parcelle
sql = 'SELECT * FROM "parcelle" LIMIT 1'
if self.connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector, sql)
if ok and rowCount >= 1:
self.hasMajicDataParcelle = True
connector.__del__()
self.grpAdresse.setEnabled(self.hasMajicDataVoie and self.hasMajicDataParcelle)
self.grpProprietaire.setEnabled(self.hasMajicDataProp)
self.btExportParcelle.setEnabled(self.hasMajicDataProp)
if not self.hasMajicDataParcelle or not self.hasMajicDataVoie:
self.qc.updateLog(u"Pas de données MAJIC non bâties et/ou fantoir -> désactivation de la recherche d'adresse")
if not self.hasMajicDataProp:
self.qc.updateLog(u"Pas de données MAJIC propriétaires -> désactivation de la recherche de propriétaires")
def setupSearchCombobox(self, combo, filterExpression=None, queryMode='qgis'):
'''
Fil given combobox with data
from sql query or QGIS layer query
And add autocompletion
'''
layer = None
features = None
# Get widget
searchCombo = self.searchComboBoxes[combo]
cb = searchCombo['widget']
cb.clear()
# Get corresponding QGIS layer
itemList = []
table = searchCombo['table']
layer = cadastre_common.getLayerFromLegendByTableProps(
table,
searchCombo['geomCol'],
searchCombo['sql']
)
self.searchComboBoxes[combo]['layer'] = layer
if layer:
# Get all features
keepattributes = self.searchComboBoxes[combo]['attributes']
request = QgsFeatureRequest().setSubsetOfAttributes(
keepattributes,
layer.pendingFields()
)
self.searchComboBoxes[combo]['request'] = request
labelAttribute = self.searchComboBoxes[combo]['labelAttribute']
# Get features
if queryMode == 'sql':
features = self.getFeaturesFromSqlQuery(
layer,
filterExpression,
keepattributes,
self.searchComboBoxes[combo]['orderBy']
)
else:
features = layer.getFeatures(request)
self.searchComboBoxes[combo]['features'] = features
# Loop through features
# optionnaly filter by QgsExpression
qe = None
if filterExpression and queryMode == 'qgis':
qe = QgsExpression(filterExpression)
if queryMode == 'sql':
emptyLabel = u'%s item(s)' % len(features)
else:
emptyLabel = ''
cb.addItem('%s' % emptyLabel, '')
for feat in features:
keep = True
if qe:
if not qe.evaluate(feat):
keep = False
if keep:
if feat and feat[labelAttribute]:
itemList.append(feat[labelAttribute])
cb.addItem(feat[labelAttribute], feat)
# style cb to adjust list width to max length content
pView = cb.view()
pView.setMinimumWidth(pView.sizeHintForColumn(0))
# Activate autocompletion
completer = QCompleter(itemList, self)
completer.setCompletionMode(QCompleter.PopupCompletion)
completer.setMaxVisibleItems(30)
completer.setCaseSensitivity(Qt.CaseInsensitive)
#~ completer.popup().setStyleSheet("background-color: lightblue")
cb.setEditable(True)
cb.setCompleter(completer)
else:
#~ self.qc.updateLog(u'Veuillez charger des données cadastrales dans QGIS pour pouvoir effectuer une recherche')
self.searchComboBoxes[combo]['layer'] = None
self.searchComboBoxes[combo]['request'] = None
self.searchComboBoxes[combo]['features'] = None
self.searchComboBoxes[combo]['chosenFeature'] = None
return [layer, features]
def getFeaturesFromSqlQuery(self, layer, filterExpression=None, attributes='*', orderBy=None):
'''
Get data from a db table,
optionnally filtered by given expression
and get corresponding QgsFeature objects
'''
QApplication.setOverrideCursor(Qt.WaitCursor)
# Get connection parameters
connectionParams = cadastre_common.getConnectionParameterFromDbLayer(layer)
if not connectionParams:
QApplication.restoreOverrideCursor()
return None
# set properties
self.dbType = connectionParams['dbType']
self.schema = connectionParams['schema']
# Use db_manager tool to run the query
connector = cadastre_common.getConnectorFromUri(connectionParams)
# SQL
sql = ' SELECT %s' % ', '.join(attributes)
# Replace geo_parcelle by parcelle_info if necessary
table = connectionParams['table']
if table == 'geo_parcelle':
table = 'parcelle_info'
f = '"%s"' % table
sql+= ' FROM %s' % f
sql+= " WHERE 2>1"
if filterExpression:
sql+= " AND %s" % filterExpression
if orderBy:
sql+= ' ORDER BY %s' % ', '.join(orderBy)
if self.dbType == 'postgis':
sql = cadastre_common.setSearchPath(sql, connectionParams['schema'])
# Get data
#self.qc.updateLog(sql)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector, sql)
# Get features
features = []
if rowCount > 0:
fids = [str(a[0]) for a in data]
exp = ' "%s" IN ( %s ) ' % (
attributes[0],
','.join(fids)
)
request = QgsFeatureRequest().setSubsetOfAttributes(attributes, layer.pendingFields()).setFilterExpression(exp)
if orderBy:
request.addOrderBy(orderBy[0])
for feat in layer.getFeatures(request):
features.append(feat)
connector.__del__()
QApplication.restoreOverrideCursor()
return features
def getFeatureFromComboboxValue(self, combo):
'''
Get the feature corresponding to
the chosen combobox value
'''
QApplication.setOverrideCursor(Qt.WaitCursor)
# Get widget
searchCombo = self.searchComboBoxes[combo]
cb = searchCombo['widget']
# Reinit
self.searchComboBoxes[combo]['chosenFeature'] = None
feature = cb.itemData(cb.currentIndex())
if feature:
self.searchComboBoxes[combo]['chosenFeature'] = feature
QApplication.restoreOverrideCursor()
def searchItem(self, key):
'''
Query database to get item (adresse, proprietaire)
corresponding to given name
'''
# TODO : utiliser la recherche plein texte pour postgis et spatialite
# pour la recherche des propriétaires -> plus efficace et permissif
QApplication.setOverrideCursor(Qt.WaitCursor)
# Get value
combo = self.searchComboBoxes[key]['widget']
searchValue = unicode(combo.currentText())
# Abort if searchValue length too small
minlen = self.searchComboBoxes[key]['search']['minlen']
if len(self.qc.normalizeString(searchValue)) < minlen:
self.qc.updateLog(u"%s caractères minimum requis pour la recherche !" % minlen)
QApplication.restoreOverrideCursor()
return None
# Get database connection parameters from a qgis layer
dbtable = self.searchComboBoxes[key]['table']
layer = cadastre_common.getLayerFromLegendByTableProps( dbtable.replace('v_', '') )
if not layer:
QApplication.restoreOverrideCursor()
return None
connectionParams = cadastre_common.getConnectionParameterFromDbLayer(layer)
if not connectionParams:
QApplication.restoreOverrideCursor()
return None
# Use db_manager tool to run the query
connector = cadastre_common.getConnectorFromUri(connectionParams)
self.connector = connector
# Format searchValue
# get rid of contextual info
sp = searchValue.split('|')
if len(sp) > 1:
searchValue = sp[1]
# get rid of double spaces
r = re.compile(r'[ ,]+', re.IGNORECASE)
searchValue = r.sub(' ', searchValue).strip(' \t\n')
if key == 'adresse':
# get rid of stopwords
stopwords = ['ALLEE', 'AQUEDUC', 'ARCEAUX', 'AVENUE', 'AVENUES', 'BOULEVARD', 'CARREFOUR', 'CARRER', 'CHEMIN', 'CHEMINS', 'CHEMIN RURAL', 'CLOS', 'COUR', 'COURS', 'DESCENTE', 'ENCLOS', 'ESCALIER', 'ESPACE', 'ESPLANADE', 'GRAND RUE', 'IMPASSE', 'MAIL', 'MONTEE', 'PARVIS', 'PASSAGE', 'PASSERELLE', 'PLACE', 'PLAN', 'PONT', 'QUAI', 'ROND-POINT', 'ROUTE', 'RUE', 'RUISSEAU', 'SENTE', 'SENTIER', 'SQUARE', 'TERRASSE', 'TRABOULE', 'TRAVERSE', 'TRAVERSEE', 'TRAVERSIER', 'TUNNEL', 'VOIE', 'VOIE COMMUNALE', 'VIADUC', 'ZONE',
'ACH', 'ALL', 'ANGL', 'ART', 'AV', 'AVE', 'BD', 'BV', 'CAMP', 'CAR', 'CC', 'CD', 'CH', 'CHE', 'CHEM', 'CHS ', 'CHV', 'CITE', 'CLOS', 'COTE', 'COUR', 'CPG', 'CR', 'CRS', 'CRX', 'D', 'DIG', 'DOM', 'ECL', 'ESC', 'ESP', 'FG', 'FOS', 'FRM', 'GARE', 'GPL', 'GR', 'HAM', 'HLE', 'HLM ', 'IMP', 'JTE ', 'LOT', 'MAIL', 'MAIS', 'N', 'PARC', 'PAS', 'PCH', 'PL', 'PLE ', 'PONT', 'PORT', 'PROM', 'PRV', 'PTA', 'PTE', 'PTR', 'PTTE', 'QUA', 'QUAI', 'REM', 'RES', 'RIVE', 'RLE', 'ROC', 'RPE ', 'RPT ', 'RTE ', 'RUE', 'RULT', 'SEN', 'SQ', 'TOUR', 'TSSE', 'VAL', 'VC', 'VEN', 'VLA', 'VOIE', 'VOIR', 'VOY', 'ZONE'
]
sp = searchValue.split(' ')
if len(sp)>0 and self.qc.normalizeString(sp[0]) in stopwords:
searchValue = ' '.join(sp[1:])
if len(self.qc.normalizeString(searchValue)) < minlen:
self.qc.updateLog(u"%s caractères minimum requis pour la recherche !" % minlen)
QApplication.restoreOverrideCursor()
return None
sqlSearchValue = self.qc.normalizeString(searchValue)
# Build SQL query
if key == 'adresse':
sql = ' SELECT DISTINCT v.voie, c.libcom, v.natvoi, v.libvoi'
sql+= ' FROM voie v'
sql+= ' INNER JOIN commune c ON c.ccodep = v.ccodep AND c.ccocom = v.ccocom'
sql+= " WHERE libvoi LIKE %s" % self.connector.quoteString('%'+sqlSearchValue+'%')
sql+= ' ORDER BY c.libcom, v.natvoi, v.libvoi'
if key == 'proprietaire':
sql = " SELECT trim(ddenom) AS k, MyStringAgg(comptecommunal, ',') AS cc, dnuper" #, c.ccocom"
sql+= ' FROM proprietaire p'
#~ sql+= ' INNER JOIN commune c ON c.ccocom = p.ccocom'
sql+= " WHERE ddenom LIKE %s" % self.connector.quoteString(sqlSearchValue+'%')
sql+= ' GROUP BY dnuper, ddenom, dlign4' #, c.ccocom'
sql+= ' ORDER BY ddenom' #, c.ccocom'
self.dbType = connectionParams['dbType']
if self.dbType == 'postgis':
sql = cadastre_common.setSearchPath(sql, connectionParams['schema'])
sql = sql.replace('MyStringAgg', 'string_agg')
else:
sql = sql.replace('MyStringAgg', 'group_concat')
#self.qc.updateLog(sql)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(connector,sql)
# Fill combobox
self.qc.updateLog(u"%s résultats correpondent à '%s'" % (rowCount, searchValue))
cb = self.searchComboBoxes[key]['widget']
cb.clear()
cb.addItem(u'%s item(s)' % rowCount , '')
itemList = []
for line in data:
if key == 'adresse':
label = '%s | %s %s' % (
line[1].strip(),
line[2].strip(),
line[3].strip()
)
val = {'voie' : line[0]}
if key == 'proprietaire':
#~ label = '%s - %s | %s' % (line[3], line[2], line[0].strip())
label = '%s | %s' % (line[2], line[0].strip())
val = {
'cc' : ["'%s'" % a for a in line[1].split(',')],
'dnuper' : line[2]
}
cb.addItem(label, val)
pView = cb.view()
pView.setMinimumWidth(pView.sizeHintForColumn(0))
# Restore cursor
QApplication.restoreOverrideCursor()
def onSearchItemChoose(self, key):
'''
Select parcelles corresponding
to chosen item in combo box
(adresse, proprietaire)
'''
QApplication.setOverrideCursor(Qt.WaitCursor)
# Get value
combo = self.searchComboBoxes[key]['widget']
value = combo.itemData(combo.currentIndex())
if not value:
QApplication.restoreOverrideCursor()
return None
# Set filter expression
if key == 'adresse':
filterExpression = "voie = '%s'" % value['voie']
if key == 'proprietaire':
filterExpression = "comptecommunal IN (%s)" % ', '.join(value['cc'])
# Get data for child and fill combobox
ckey = self.searchComboBoxes[key]['search']['child']
[layer, features] = self.setupSearchCombobox(
ckey,
filterExpression,
'sql'
)
# Set properties
self.searchComboBoxes[key]['layer'] = layer
self.searchComboBoxes[key]['features'] = features
self.searchComboBoxes[key]['chosenFeature'] = features
# Set proprietaire id
if key == 'proprietaire':
self.searchComboBoxes[key]['id'] = value['cc']
if features:
self.qc.updateLog(
u"%s parcelle(s) trouvée(s) pour '%s'" % (
len(features),
combo.currentText()
)
)
QApplication.restoreOverrideCursor()
def onNonSearchItemChoose(self, key):
'''
Get feature from chosen item in combobox
and optionnaly fill its child combobox
'''
# get feature from the chosen value
self.getFeatureFromComboboxValue(key)
# optionnaly also update child combobox
item = self.searchComboBoxes[key]
if item.has_key('child'):
feature = item['chosenFeature']
ckey = item['child']['key']
fkey = item['child']['fkey']
if feature:
filterExpression = "%s = '%s' AND lot = '%s'" % (fkey, feature[fkey], feature['lot'])
self.setupSearchCombobox(ckey, filterExpression, 'sql')
else:
if item['child']['getIfNoFeature']:
self.setupSearchCombobox(ckey, None, 'sql')
def onNonSearchItemEdit(self, key):
'''
Empty previous stored feature
for the combobox every time
the user edit its content
'''
self.searchComboBoxes[key]['chosenFeature'] = None
def onNonSearchItemReset(self, key):
'''
Unchoose item in combobox
which also trigger onNonSearchItemChoose above
'''
self.searchComboBoxes[key]['widget'].setCurrentIndex(0)
def setZoomToChosenSearchCombobox(self, combo):
'''
Zoom to the feature(s)
selected in the give combobox
'''
# Get widget
searchCombo = self.searchComboBoxes[combo]
cb = searchCombo['widget']
# Zoom
if searchCombo['chosenFeature']:
if isinstance(searchCombo['chosenFeature'], list):
# buid virtual geom
f = searchCombo['chosenFeature'][0]
extent = f.geometry().boundingBox()
for feat in searchCombo['chosenFeature']:
extent.combineExtentWith(feat.geometry().boundingBox())
else:
extent = searchCombo['chosenFeature'].geometry().boundingBox()
# reproject extent if needed
if self.mc.hasCrsTransformEnabled():
crsDest = self.mc.mapRenderer().destinationCrs()
layer = searchCombo['layer']
crsSrc = layer.crs()
xform = QgsCoordinateTransform(crsSrc, crsDest)
extent = xform.transform(extent)
self.mc.setExtent(extent)
self.mc.refresh()
def setCenterToChosenSearchCombobox(self, combo):
'''
Center to the feature(s)
chosen in the corresponding combobox
'''
# Get widget
searchCombo = self.searchComboBoxes[combo]
cb = searchCombo['widget']
# Center
if searchCombo['chosenFeature']:
# first get scale
scale = self.mc.scale()
# then zoom to geometry extent
if isinstance(searchCombo['chosenFeature'], list):
# buid virtual geom
f = searchCombo['chosenFeature'][0]
extent = f.geometry().boundingBox()
for feat in searchCombo['chosenFeature']:
extent.combineExtentWith(feat.geometry().boundingBox())
else:
extent = searchCombo['chosenFeature'].geometry().boundingBox()
# reproject extent if needed
if self.mc.hasCrsTransformEnabled():
crsDest = self.mc.mapRenderer().destinationCrs()
layer = searchCombo['layer']
crsSrc = layer.crs()
xform = QgsCoordinateTransform(crsSrc, crsDest)
extent = xform.transform(extent)
self.mc.setExtent(extent)
# the set the scale back
self.mc.zoomScale(scale)
self.mc.refresh()
def setSelectionToChosenSearchCombobox(self, combo):
'''
Select the feature(s)
corresponding to the chosen item
'''
# Get widget
searchCombo = self.searchComboBoxes[combo]
cb = searchCombo['widget']
# Select
if searchCombo['chosenFeature'] and searchCombo['layer']:
searchCombo['layer'].removeSelection()
if isinstance(searchCombo['chosenFeature'], list):
i = [feat.id() for feat in searchCombo['chosenFeature']]
else:
i = searchCombo['chosenFeature'].id()
searchCombo['layer'].select(i)
def setCenterToChosenItem(self, key):
'''
Set map center corresponding
to the chosen feature(s) for the
last not null item in the list
'''
w = None
for item in self.zoomButtons[key]['comboboxes']:
if self.searchComboBoxes[item]['chosenFeature'] \
and self.searchComboBoxes[item]['layer']:
w = item
if w:
self.setCenterToChosenSearchCombobox(w)
def setZoomToChosenItem(self, key):
'''
Zoom to the chosen feature(s) for the
last not null item in the list
'''
w = None
for item in self.zoomButtons[key]['comboboxes']:
if self.searchComboBoxes[item]['chosenFeature'] \
and self.searchComboBoxes[item]['layer']:
w = item
if w:
self.setZoomToChosenSearchCombobox(w)
def setSelectionToChosenItem(self, key):
'''
Select the feature(s) for the
last non null item in the list
'''
w = None
for item in self.zoomButtons[key]['comboboxes']:
if self.searchComboBoxes[item]['chosenFeature'] \
and self.searchComboBoxes[item]['layer']:
w = item
if w:
self.setSelectionToChosenSearchCombobox(w)
def exportProprietaire(self):
'''
Export the selected proprietaire
as PDF using the template composer
filled with appropriate data
'''
if not self.connector:
return
# Search proprietaire by dnuper
cc = self.searchComboBoxes['proprietaire']['id']
if cc:
layer = self.searchComboBoxes['proprietaire']['layer']
qex = cadastreExport(layer, 'proprietaire', cc)
qex.exportAsPDF()
else:
self.qc.updateLog(u'Aucune donnée trouvée pour ce propriétaire !')
def exportParcelle(self, key):
'''
Export the selected parcelle
as PDF using the template composer
filled with appropriate data
'''
if not self.connector:
return
feat = self.searchComboBoxes[key]['chosenFeature']
layer = self.searchComboBoxes[key]['layer']
if feat:
comptecommunal = cadastre_common.getCompteCommunalFromParcelleId( feat['geo_parcelle'], self.connectionParams, self.connector)
qex = cadastreExport(layer, 'parcelle', comptecommunal, feat['geo_parcelle'])
qex.exportAsPDF()
else:
self.qc.updateLog(u'Aucune parcelle sélectionnée !')
def onVisibilityChange(self, visible):
'''
Fill commune combobox when the dock
becomes visible
'''
if visible:
print "visible"
#~ self.setupSearchCombobox('commune', None, 'sql')
else:
self.txtLog.clear()
# --------------------------------------------------------
# Option - Let the user configure options
# --------------------------------------------------------
from cadastre_option_form import *
class cadastre_option_dialog(QDialog, Ui_cadastre_option_form):
def __init__(self, iface):
QDialog.__init__(self)
self.iface = iface
self.setupUi(self)
self.plugin_dir = os.path.dirname(__file__)
# Signals/Slot Connections
self.rejected.connect(self.onReject)
self.buttonBox.rejected.connect(self.onReject)
self.buttonBox.accepted.connect(self.onAccept)
# interface change buttons
self.interfaceSelectors = {
"Cadastre" : {
"button" : self.btInterfaceCadastre
},
"QGIS" : {
"button" : self.btInterfaceQgis
}
}
from functools import partial
for key, item in self.interfaceSelectors.items():
control = item['button']
slot = partial(self.applyInterface, key)
control.clicked.connect(slot)
# path buttons selectors
# paths needed to be chosen by user
self.pathSelectors = {
"tempDir" : {
"button" : self.btTempDir,
"input" : self.inTempDir,
"type": "dir"
},
"composerTemplateFile" : {
"button" : self.btComposerTemplateFile,
"input" : self.inComposerTemplateFile,
"type": "file"
}
}
from functools import partial
for key, item in self.pathSelectors.items():
control = item['button']
slot = partial(self.chooseDataPath, key)
control.clicked.connect(slot)
# Set initial widget values
self.getValuesFromSettings()
def chooseDataPath(self, key):
'''
Ask the user to select a folder
and write down the path to appropriate field
'''
if self.pathSelectors[key]['type'] == 'dir':
ipath = QFileDialog.getExistingDirectory(
None,
u"Choisir le répertoire contenant les fichiers",
str(self.pathSelectors[key]['input'].text().encode('utf-8')).strip(' \t')
)
else:
ipath = QFileDialog.getOpenFileName(
None,
u"Choisir le modèle de composeur utilisé pour l'export",
str(self.pathSelectors[key]['input'].text().encode('utf-8')).strip(' \t'),
u"Composeur (*.qpt)"
)
if os.path.exists(unicode(ipath)):
self.pathSelectors[key]['input'].setText(unicode(ipath))
def getValuesFromSettings(self):
'''
Get majic file names and other options
from settings and set corresponding inputs
'''
s = QSettings()
batiFileName = s.value("cadastre/batiFileName", 'REVBATI.800', type=str)
if batiFileName:
self.inMajicBati.setText(batiFileName)
fantoirFileName = s.value("cadastre/fantoirFileName", 'TOPFANR.800', type=str)
if fantoirFileName:
self.inMajicFantoir.setText(fantoirFileName)
lotlocalFileName = s.value("cadastre/lotlocalFileName", 'REVD166.800', type=str)
if lotlocalFileName:
self.inMajicLotlocal.setText(lotlocalFileName)
nbatiFileName = s.value("cadastre/nbatiFileName", 'REVNBAT.800', type=str)
if nbatiFileName:
self.inMajicNbati.setText(nbatiFileName)
pdlFileName = s.value("cadastre/pdlFileName", 'REVFPDL.800', type=str)
if pdlFileName:
self.inMajicPdl.setText(pdlFileName)
propFileName = s.value("cadastre/propFileName", 'REVPROP.800', type=str)
if propFileName:
self.inMajicProp.setText(propFileName)
tempDir = s.value("cadastre/tempDir", '%s' % tempfile.gettempdir(), type=str)
if tempDir:
self.inTempDir.setText(tempDir)
maxInsertRows = s.value("cadastre/maxInsertRows", 100000, type=int)
if maxInsertRows:
self.inMaxInsertRows.setValue(maxInsertRows)
spatialiteTempStore = s.value("cadastre/spatialiteTempStore", 'MEMORY', type=str)
if spatialiteTempStore and hasattr(self, 'inSpatialiteTempStore'):
if spatialiteTempStore == 'MEMORY':
self.inSpatialiteTempStore.setCurrentIndex(0)
else:
self.inSpatialiteTempStore.setCurrentIndex(1)
composerTemplateFile = s.value(
"cadastre/composerTemplateFile",
'%s/composers/paysage_a4.qpt' % self.plugin_dir,
type=str
)
if composerTemplateFile:
self.inComposerTemplateFile.setText(composerTemplateFile)
def applyInterface(self, key):
'''
Help the user to select
and apply personalized interface
'''
item = self.interfaceSelectors[key]
iniPath = os.path.join(
self.plugin_dir,
'interface/'
)
interfaceInfo = u'''
Pour appliquer l'interface %s
- Menu Préférences > Personnalisation
- Bouton Charger depuis le fichier (icône dossier ouvert)
- Sélectionner le fichier %s.ini situé dans le dossier : %s
- Appliquer et fermer la fenêtre
- Redémarer QGIS
''' % (key, key.lower(), iniPath)
QMessageBox.information(
self,
u"Cadastre - Personnalisation",
interfaceInfo
)
def onAccept(self):
'''
Save options when pressing OK button
'''
# Save Majic file names
s = QSettings()
s.setValue("cadastre/batiFileName", self.inMajicBati.text().strip(' \t\n\r'))
s.setValue("cadastre/fantoirFileName", self.inMajicFantoir.text().strip(' \t\n\r'))
s.setValue("cadastre/lotlocalFileName", self.inMajicLotlocal.text().strip(' \t\n\r'))
s.setValue("cadastre/nbatiFileName", self.inMajicNbati.text().strip(' \t\n\r'))
s.setValue("cadastre/pdlFileName", self.inMajicPdl.text().strip(' \t\n\r'))
s.setValue("cadastre/propFileName", self.inMajicProp.text().strip(' \t\n\r'))
# Save temp dir
s.setValue("cadastre/tempDir", self.inTempDir.text().strip(' \t\n\r'))
# Save composer template dir
s.setValue("cadastre/composerTemplateFile", self.inComposerTemplateFile.text().strip(' \t\n\r'))
# Save performance tuning
s.setValue("cadastre/maxInsertRows", int(self.inMaxInsertRows.value()))
s.setValue("cadastre/spatialiteTempStore", self.inSpatialiteTempStore.currentText().upper())
self.accept()
def onReject(self):
'''
Run some actions when
the user closes the dialog
'''
string = "cadastre option dialog closed"
self.close()
# --------------------------------------------------------
# About - Let the user display the about dialog
# --------------------------------------------------------
from cadastre_about_form import *
class cadastre_about_dialog(QDialog, Ui_cadastre_about_form):
def __init__(self, iface):
QDialog.__init__(self)
self.iface = iface
self.setupUi(self)
# Signals/Slot Connections
self.rejected.connect(self.onReject)
self.buttonBox.rejected.connect(self.onReject)
self.buttonBox.accepted.connect(self.onAccept)
def onAccept(self):
'''
Save options when pressing OK button
'''
self.accept()
def onReject(self):
'''
Run some actions when
the user closes the dialog
'''
self.close()
# --------------------------------------------------------
# Parcelle - Show parcelle information
# --------------------------------------------------------
from cadastre_parcelle_form import *
from cadastre_export import *
class cadastre_parcelle_dialog(QDialog, Ui_cadastre_parcelle_form):
def __init__(self, iface, layer, feature, cadastre_search_dialog):
QDialog.__init__(self)
self.iface = iface
self.feature = feature
self.layer = layer
self.mc = iface.mapCanvas()
self.setupUi(self)
self.cadastre_search_dialog = cadastre_search_dialog
# common cadastre methods
from cadastre_dialogs import cadastre_common
self.qc = cadastre_common(self)
# Get connection parameters
connectionParams = cadastre_common.getConnectionParameterFromDbLayer(layer)
if not connectionParams:
return
self.connectionParams = connectionParams
self.dbType = connectionParams['dbType']
self.schema = connectionParams['schema']
connector = cadastre_common.getConnectorFromUri(connectionParams)
self.connector = connector
# Signals/Slot Connections
self.rejected.connect(self.onReject)
self.buttonBox.rejected.connect(self.onReject)
self.buttonBox.accepted.connect(self.onAccept)
# Export buttons
exportButtons = {
'parcelle' : self.btExportParcelle,
'proprietaire': self.btExportProprietaire
}
for key, item in exportButtons.items():
control = item
slot = partial(self.exportAsPDF, key)
control.clicked.connect(slot)
# Parcelle action button
self.btCentrer.clicked.connect(self.centerToParcelle)
self.btZoomer.clicked.connect(self.zoomToParcelle)
self.btSelectionner.clicked.connect(self.selectParcelle)
# Select parcelle from proprietaire action
self.btParcellesProprietaire.clicked.connect(self.selectParcellesProprietaire)
# Check majic content
self.hasMajicDataProp = False
self.checkMajicContent()
# Set dialog content
self.setParcelleContent()
self.setProprietairesContent()
def checkMajicContent(self):
'''
Check if database contains
any MAJIC data
'''
self.hasMajicDataProp = False
sql = 'SELECT * FROM "proprietaire" LIMIT 1'
if self.connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.connector, sql)
if ok and rowCount >= 1:
self.hasMajicDataProp = True
def setParcelleContent(self):
'''
Get data for the selected
parcelle and set the dialog
text content
'''
if self.hasMajicDataProp:
# Get parcelle info
sql = '''
SELECT
c.libcom AS nomcommune, c.ccocom AS codecommune, p.dcntpa AS contenance,
CASE
WHEN v.libvoi IS NOT NULL THEN trim(ltrim(p.dnvoiri, '0') || ' ' || trim(v.natvoi) || ' ' || v.libvoi)
ELSE ltrim(p.cconvo, '0') || p.dvoilib
END AS adresse,
CASE
WHEN p.gurbpa = 'U' THEN 'Oui'
ELSE 'Non'
END AS urbain,
ccosec || dnupla
FROM parcelle p
LEFT OUTER JOIN commune c ON p.ccocom = c.ccocom AND c.ccodep = p.ccodep
LEFT OUTER JOIN voie v ON v.voie = p.voie
WHERE 2>1
AND parcelle = '%s'
LIMIT 1
''' % self.feature['geo_parcelle']
else:
self.parcelleInfo.setText(u'Les données MAJIC n\'ont pas été trouvées dans la base de données')
sql ='''
SELECT c.tex2 AS nomcommune, c.idu AS codecommune, '' AS contenance,
'' AS adresse,
'' AS urbain,
p.idu
FROM geo_parcelle p
INNER JOIN geo_commune c
ON ST_Intersects(p.geom, c.geom)
WHERE geo_parcelle = '%s'
''' % self.feature['geo_parcelle']
if self.connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.connector, sql)
html = ''
if ok:
for line in data:
html+= u'%s
' % line[5]
html+= u'Commune :'
if line[0] and line[1]:
html+= ' %s (%s)
' % (line[0], line[1])
else:
html+= u' Pas de données Fantoir dans la base !
'
html+= u'Surface géographique : %s m²
' % int(self.feature.geometry().area())
html+= u'Contenance : %s m²
' % line[2]
html+= u'Adresse : %s
' % line[3]
html+= u'Urbaine : %s
' % line[4]
self.parcelleInfo.setText('%s' % html)
def setProprietairesContent(self):
'''
Get proprietaires data
and set the dialog content
'''
# Check for MAJIC DATA
if not self.hasMajicDataProp:
self.proprietairesInfo.setText(u'Les données MAJIC de propriétaires n\'ont pas été trouvées dans la base de données')
return
# Get proprietaire info
sql = u'''
SELECT coalesce(ccodro_lib, '') || ' - ' || p.dnuper || ' - ' || trim(coalesce(p.dqualp, '')) || ' ' || trim(coalesce(p.ddenom, '')) || ' - ' ||trim(coalesce(p.dlign3, '')) || ' / ' || ltrim(trim(coalesce(p.dlign4, '')), '0') || trim(coalesce(p.dlign5, '')) || ' ' || trim(coalesce(p.dlign6, '')) ||
CASE
WHEN jdatnss IS NOT NULL
THEN ' - Né(e) le ' || coalesce(to_char(jdatnss, 'dd/mm/YYYY'), '') || ' à ' || coalesce(p.dldnss, '')
ELSE ''
END
FROM proprietaire p
LEFT JOIN ccodro ON ccodro.ccodro = p.ccodro
WHERE 2>1
AND comptecommunal = (SELECT comptecommunal FROM parcelle WHERE parcelle = '%s')
''' % self.feature['geo_parcelle']
if self.connectionParams['dbType'] == 'postgis':
sql = cadastre_common.setSearchPath(sql, self.connectionParams['schema'])
if self.connectionParams['dbType'] == 'spatialite':
sql = cadastre_common.postgisToSpatialite(sql)
[header, data, rowCount, ok] = cadastre_common.fetchDataFromSqlQuery(self.connector, sql)
html = ''
if ok:
for line in data:
html+= u'%s
' % line[0]
self.proprietairesInfo.setText('%s' % html)
def exportAsPDF(self, key):
'''
Export the parcelle or proprietaire
information as a PDF file
'''
if not self.connector:
return
if not self.hasMajicDataProp:
self.proprietairesInfo.setText(u'Pas de données de propriétaires dans la base')
return
if self.feature:
comptecommunal = cadastre_common.getCompteCommunalFromParcelleId(
self.feature['geo_parcelle'],
self.connectionParams,
self.connector
)
if comptecommunal:
if key == 'proprietaire' and self.cbExportAllCities.isChecked():
comptecommunal = cadastre_common.getProprietaireComptesCommunaux(
comptecommunal,
self.connectionParams,
self.connector
)
if self.layer:
qe = cadastreExport(
self.layer,
key,
comptecommunal,
self.feature['geo_parcelle']
)
qe.exportAsPDF()
def centerToParcelle(self):
'''
Centre to parcelle feature
'''
if self.feature:
# first get scale
scale = self.mc.scale()
extent = self.feature.geometry().boundingBox()
# reproject extent if needed
if self.mc.hasCrsTransformEnabled():
crsDest = self.mc.mapRenderer().destinationCrs()
layer = self.layer
crsSrc = layer.crs()
xform = QgsCoordinateTransform(crsSrc, crsDest)
extent = xform.transform(extent)
self.mc.setExtent(extent)
# the set the scale back
self.mc.zoomScale(scale)
self.mc.refresh()
def zoomToParcelle(self):
'''
Zoom to parcelle feature
'''
if self.feature:
extent = self.feature.geometry().boundingBox()
# reproject extent if needed
if self.mc.hasCrsTransformEnabled():
crsDest = self.mc.mapRenderer().destinationCrs()
layer = self.layer
crsSrc = layer.crs()
xform = QgsCoordinateTransform(crsSrc, crsDest)
extent = xform.transform(extent)
self.mc.setExtent(extent)
self.mc.refresh()
def selectParcelle(self):
'''
Zoom to parcelle feature
'''
if self.layer and self.feature:
self.layer.removeSelection()
self.layer.select(self.feature.id())
def selectParcellesProprietaire(self):
'''
Select all parcelles from this parcelle proprietaire.
Use search class tools.
Needs refactoring
'''
if not self.hasMajicDataProp:
self.proprietairesInfo.setText(u'Pas de données de propriétaires dans la base')
return
qs = self.cadastre_search_dialog
key = 'proprietaire'
comptecommunal = cadastre_common.getCompteCommunalFromParcelleId( self.feature['geo_parcelle'], self.connectionParams, self.connector )
if not comptecommunal:
print "Aucune parcelle trouvée pour ce propriétaire"
value = comptecommunal
filterExpression = "comptecommunal IN ('%s')" % value
# Get data for child and fill combobox
ckey = qs.searchComboBoxes[key]['search']['child']
[layer, features] = qs.setupSearchCombobox(
ckey,
filterExpression,
'sql'
)
# Set properties
qs.searchComboBoxes[key]['layer'] = layer
qs.searchComboBoxes[key]['features'] = features
qs.searchComboBoxes[key]['chosenFeature'] = features
# Select all parcelles from proprietaire
qs.setSelectionToChosenSearchCombobox('proprietaire')
def onAccept(self):
'''
Save options when pressing OK button
'''
self.accept()
def onReject(self):
'''
Run some actions when
the user closes the dialog
'''
self.connector.__del__()
self.close()
# --------------------------------------------------------
# Messages - Displays a message to the user
# --------------------------------------------------------
from cadastre_message_form import *
class cadastre_message_dialog(QDialog, Ui_cadastre_message_form):
def __init__(self, iface, message):
QDialog.__init__(self)
self.iface = iface
self.setupUi(self)
self.teMessage.setText(message)
# Signals/Slot Connections
self.rejected.connect(self.onReject)
self.buttonBox.rejected.connect(self.onReject)
self.buttonBox.accepted.connect(self.onAccept)
def onAccept(self):
'''
Save options when pressing OK button
'''
self.accept()
def onReject(self):
'''
Run some actions when
the user closes the dialog
'''
self.close()