From dd18bc886efcca5afa1a3e017e2a261edab02615 Mon Sep 17 00:00:00 2001
From: Robert Moerman
Date: Fri, 10 Jan 2014 14:39:55 +0200
Subject: [PATCH 1/7] Ported plugin to QGIS 2 api. Also changed the database
connection management.
---
PyQt4Dialogs.py | 1154 ++++++++++++++++++++++++--------------
__init__.py | 8 +-
constants.py | 52 ++
database.py | 21 +-
metadata.txt | 35 +-
plugin.py | 615 ++++++++++++++++++++
portqgis2_db_changes.sql | 41 ++
qgisToolbox.py | 31 +-
settings.py | 79 ---
sml_surveyor.py | 376 -------------
10 files changed, 1475 insertions(+), 937 deletions(-)
create mode 100644 constants.py
create mode 100644 plugin.py
create mode 100644 portqgis2_db_changes.sql
delete mode 100644 settings.py
delete mode 100644 sml_surveyor.py
diff --git a/PyQt4Dialogs.py b/PyQt4Dialogs.py
index b9a78a6..f78c879 100644
--- a/PyQt4Dialogs.py
+++ b/PyQt4Dialogs.py
@@ -24,40 +24,145 @@
from PyQt4Widgets import XQPushButton, XQDialogButtonBox
from database import *
-try:
- _fromUtf8 = QString.fromUtf8
-except AttributeError:
- _fromUtf8 = lambda s: s
+def _fromUtf8(s):
+ return s
# All dialogs using selector tool have a captured function
# All dialogs have a getReturn function
+class dlg_DatabaseConnection(QDialog):
+ """ This dialog enables the user to choose a database connection
+ defined through DB Manager
+ """
+
+ def __init__(self):
+ # initialize QDialog class
+ super(dlg_DatabaseConnection, self).__init__(None, Qt.WindowStaysOnTopHint)
+ # initialize ui
+ self.conn = None
+ self.setupUi()
+ self.populateDatabaseChoices()
+
+ def getDatabaseConnection(self):
+ return self.conn
+
+ def populateDatabaseChoices(self):
+ """ Populate database connection choices
+ """
+ settings = QSettings()
+ settings.beginGroup('PostgreSQL/connections')
+ self.cmbbx_conn.addItem(_fromUtf8(""))
+ self.cmbbx_conn.setItemText(0, QApplication.translate(
+ "dlg_DatabaseConnection",
+ "",
+ None
+ ))
+ for i,db in enumerate(settings.childGroups()):
+ self.cmbbx_conn.addItem(_fromUtf8(""))
+ self.cmbbx_conn.setItemText(i + 1, QApplication.translate(
+ "dlg_DatabaseConnection",
+ db,
+ None
+ ))
+
+ def testDatabaseConnection(self):
+ """ Test database connection has necessary tables
+ """
+ conn = str(self.cmbbx_conn.currentText())
+ if not bool(conn.replace(" ", "")):
+ QMessageBox.information(self, "Database Connection", "Please select a database connection")
+ else:
+ self.conn = conn
+ self.accept()
+
+ def setupUi(self):
+ """ Initialize ui
+ """
+ # define ui widgets
+ self.setObjectName(_fromUtf8("dlg_DatabaseConnection"))
+ self.resize(370, 200)
+ self.setCursor(QCursor(Qt.ArrowCursor))
+ self.setModal(True)
+ self.gridLayout = QGridLayout(self)
+ self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.verticalLayout = QVBoxLayout()
+ self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.lbl_instr = QLabel(self)
+ self.lbl_instr.setWordWrap(True)
+ self.lbl_instr.setObjectName(_fromUtf8("lbl_instr"))
+ self.verticalLayout.addWidget(self.lbl_instr)
+ self.formLayout = QFormLayout()
+ self.formLayout.setObjectName(_fromUtf8("formLayout"))
+ self.lbl_conn = QLabel(self)
+ self.lbl_conn.setObjectName(_fromUtf8("lbl_conn"))
+ self.formLayout.setWidget(0, QFormLayout.LabelRole, self.lbl_conn)
+ self.cmbbx_conn = QComboBox(self)
+ self.cmbbx_conn.setObjectName(_fromUtf8("cmbbx_conn"))
+ self.formLayout.setWidget(0, QFormLayout.FieldRole, self.cmbbx_conn)
+ self.verticalLayout.addLayout(self.formLayout)
+ self.lbl_aside = QLabel(self)
+ self.lbl_aside.setWordWrap(True)
+ self.lbl_aside.setObjectName(_fromUtf8("lbl_aside"))
+ self.verticalLayout.addWidget(self.lbl_aside)
+ self.btnbx_options = XQDialogButtonBox(self)
+ self.btnbx_options.setOrientation(Qt.Horizontal)
+ self.btnbx_options.setStandardButtons(XQDialogButtonBox.Cancel|XQDialogButtonBox.Ok)
+ self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
+ self.verticalLayout.addWidget(self.btnbx_options)
+ self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
+ # translate ui widgets' text
+ self.setWindowTitle(QApplication.translate(
+ "dlg_DatabaseConnection",
+ "Database Connection",
+ None,
+ QApplication.UnicodeUTF8
+ ))
+ self.lbl_conn.setText(QApplication.translate(
+ "dlg_DatabaseConnection",
+ "Connection: ",
+ None
+ ))
+ self.lbl_aside.setText(QApplication.translate(
+ "dlg_DatabaseConnection",
+ "*If your database connection cannot be found above then please define it through the PostGIS Connection Manager.",
+ None
+ ))
+ self.lbl_instr.setText(QApplication.translate(
+ "dlg_DatabaseConnection",
+ "A database connection has not yet been selected or is no longer valid. Please select a database connection.",
+ None
+ ))
+ # connect ui widgets
+ self.btnbx_options.accepted.connect(self.testDatabaseConnection)
+ self.btnbx_options.rejected.connect(self.reject)
+ QMetaObject.connectSlotsByName(self)
+
+
class dlg_Selector(QDialog):
- """ This dialog enables the selection of single features on a vector layer by means of the feature selector tool defined in qgisToolbox
+ """ This dialog enables the selection of single features on a
+ vector layer by means of the feature selector tool defined in
+ qgisToolbox
"""
- def __init__(self, db, iface, layer, query, obj={"NAME":"NONAME", "PURPOSE":"NOPURPOSE", "ACTION":"NOACTION"}, preserve = False, parent = None):
+ def __init__(self, db, iface, requiredLayer, mode, query, preserve = False, parent = None):
# initialize QDialog class
super(dlg_Selector, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
- self.setupUi(obj)
- # initialize instance variables
+ self.setupUi(requiredLayer, mode)
self.db = db
self.iface = iface
- self.layer = layer
+ self.layer = requiredLayer
+ self.mode = mode
self.query = query
- self.obj = obj
self.preserve = preserve
self.confirmed = False
self.featID = None
# initialize selector tool
- self.selector = featureSelector(iface, layer, True, self)
+ self.selector = featureSelector(iface, requiredLayer.layer, True, self)
# save qgis tool
self.tool = self.selector.parentTool
- def getReturn(self):
- """ Return intended variable(s) after the dialog has been accepted
- """
+ def getFeatureId(self):
return self.featID
def executeOption(self, button):
@@ -66,23 +171,31 @@ def executeOption(self, button):
if self.btnbx_options.standardButton(button) == QDialogButtonBox.Ok:
# check that a feature has been selected
if self.featID is None:
- QMessageBox.information(self, "No %s Selected" %(self.obj["NAME"].title(),), "Please select a %s." %(self.obj["NAME"].lower(),))
+ QMessageBox.information(
+ self,
+ "No %s Selected" %(self.layer.name.title(),),
+ "Please select a %s." %(self.layer.name.lower(),)
+ )
return
# check confirmation
if not self.confirmed:
- QMessageBox.information(self, "No Confirmation", "Please tick the confimation check box.")
+ QMessageBox.information(
+ self,
+ "No Confirmation",
+ "Please tick the confimation check box."
+ )
return
# reset qgis tool
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection if needed
- if not self.preserve: self.layer.removeSelection()
+ if not self.preserve: self.layer.layer.removeSelection()
# accept dialog
self.accept()
else:
# reset qgis tool
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection
- self.layer.removeSelection()
+ self.layer.layer.removeSelection()
# reject dialog
self.reject()
@@ -93,7 +206,7 @@ def captured(self, selected):
self.selector.disableCapturing()
# update dialog
self.featID = selected[0]
- self.lnedt_featID.setText(str(self.db.query(self.query["SELECT"], (self.featID,))[0][0]))
+ self.lnedt_featID.setText(str(self.db.query(self.query, (self.featID,))[0][0]))
self.pshbtn_re.setEnabled(True)
self.chkbx_confirm.setEnabled(True)
@@ -116,7 +229,7 @@ def confirm(self, state):
self.pshbtn_re.setEnabled(not bool(state))
self.confirmed = bool(state)
- def setupUi(self, obj):
+ def setupUi(self, layer, mode):
""" Initialize ui
"""
# define ui widgets
@@ -137,7 +250,7 @@ def setupUi(self, obj):
self.formLayout.setObjectName(_fromUtf8("formLayout"))
self.lbl_featID = QLabel(self.widget)
self.lbl_featID.setObjectName(_fromUtf8("lbl_featID"))
- self.formLayout.setWidget(0, QFormLayout.LabelRole, self.lbl_featID)
+ self.formLayout.setWidget(0, QFormLayout.LabelRole, self.lbl_featID)
self.lnedt_featID = QLineEdit(self.widget)
self.lnedt_featID.setEnabled(False)
self.lnedt_featID.setObjectName(_fromUtf8("lnedt_featID"))
@@ -159,32 +272,40 @@ def setupUi(self, obj):
self.verticalLayout.addWidget(self.btnbx_options)
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
# translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_Selector", "%s %s" %(obj["NAME"].title(), obj["PURPOSE"].title()), None, QApplication.UnicodeUTF8))
- self.lbl_featID.setText(QApplication.translate("dlg_Selector", "%s ID" %(obj["NAME"].title(),), None, QApplication.UnicodeUTF8))
- self.pshbtn_re.setText(QApplication.translate("dlg_Selector", "Re-select", None, QApplication.UnicodeUTF8))
- self.chkbx_confirm.setText(QApplication.translate("dlg_Selector", "I am sure I want to %s this %s" %(obj["ACTION"].lower(), obj["NAME"].lower()), None, QApplication.UnicodeUTF8))
+ self.setWindowTitle(QApplication.translate("dlg_Selector",
+ "%s %s" %(layer.name.title(), mode.actor.title()),
+ None,
+ QApplication.UnicodeUTF8))
+ self.lbl_featID.setText(QApplication.translate("dlg_Selector",
+ "%s ID" %(layer.name.title(),),
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_re.setText(QApplication.translate("dlg_Selector",
+ "Re-select",
+ None,
+ QApplication.UnicodeUTF8))
+ self.chkbx_confirm.setText(QApplication.translate("dlg_Selector",
+ "I am sure I want to %s this %s" %(mode.action.lower(), layer.name.lower()),
+ None,
+ QApplication.UnicodeUTF8))
# connect ui widgets
self.pshbtn_re.clicked.connect(self.reselect)
self.chkbx_confirm.stateChanged.connect(self.confirm)
self.btnbx_options.clicked.connect(self.executeOption)
QMetaObject.connectSlotsByName(self)
+
class dlg_Manager(QDialog):
""" This dialog enables the user to select an option with regards to managing a vector layer
"""
- def __init__(self, obj={"NAME":"NONAME",}, parent = None):
- # initialize QDialog class
+ def __init__(self, requiredLayer, parent=None):
super(dlg_Manager, self).__init__(parent, Qt.WindowStaysOnTopHint)
- # initialize ui
- self.setupUi(obj)
- # initialize instance variables
- self.obj = obj
+ self.setupUi(requiredLayer)
+ self.layer = requiredLayer
self.option = None
- def getReturn(self):
- """ Return intended variable(s) after the dialog has been accepted
- """
+ def getOption(self):
return self.option
def executeOption(self, button):
@@ -205,7 +326,7 @@ def executeOption(self, button):
# reject dialog
self.reject()
- def setupUi(self, obj):
+ def setupUi(self, layer):
""" Initialize ui
"""
# define ui widgets
@@ -240,66 +361,257 @@ def setupUi(self, obj):
self.vrtlyt.addWidget(self.btnbx_options)
self.mainlyt.addLayout(self.vrtlyt, 0, 0, 1, 1)
# translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_Manager", "%s Manager" %(obj["NAME"].title(),), None, QApplication.UnicodeUTF8))
- self.rdbtn_add.setText(QApplication.translate("dlg_Manager", "Create New %s" %(obj["NAME"].title(),), None, QApplication.UnicodeUTF8))
- self.rdbtn_edit.setText(QApplication.translate("dlg_Manager", "Edit Existing %s" %(obj["NAME"].title(),), None, QApplication.UnicodeUTF8))
- self.rdbtn_del.setText(QApplication.translate("dlg_Manager", "Delete Existing %s" %(obj["NAME"].title(),), None, QApplication.UnicodeUTF8))
+ self.setWindowTitle(QApplication.translate(
+ "dlg_Manager",
+ "%s Manager" %(layer.name.title(),),
+ None,
+ QApplication.UnicodeUTF8
+ ))
+ self.rdbtn_add.setText(QApplication.translate(
+ "dlg_Manager",
+ "Create New %s" %(layer.name.title(),),
+ None,
+ QApplication.UnicodeUTF8
+ ))
+ self.rdbtn_edit.setText(QApplication.translate(
+ "dlg_Manager",
+ "Edit Existing %s" %(layer.name.title(),),
+ None,
+ QApplication.UnicodeUTF8
+ ))
+ self.rdbtn_del.setText(QApplication.translate(
+ "dlg_Manager",
+ "Delete Existing %s" %(layer.name.title(),),
+ None,
+ QApplication.UnicodeUTF8
+ ))
+ # connect ui widgets
+ self.btnbx_options.clicked.connect(self.executeOption)
+ QMetaObject.connectSlotsByName(self)
+
+
+class dlg_FormBeacon(QDialog):
+ """ This dialog enables a user to define and modify a beacon
+ """
+
+ def __init__(self, db, query, fields, values=[], parent = None):
+ # initialize QDialog class
+ super(dlg_FormBeacon, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ # initialize ui
+ self.setupUi(fields)
+ # initialize instance variables
+ self.db = db
+ self.query = query
+ self.fields = fields
+ self.values_old = {}
+ self.values_new = {}
+ self.colours = {
+ "REQUIRED":"background-color: rgba(255, 107, 107, 150);",
+ "TYPE":"background-color: rgba(107, 107, 255, 150);",
+ "UNIQUE":"background-color: rgba(107, 255, 107, 150);"
+ }
+ # populate form if values are given
+ if bool(values):
+ self.populateForm(values)
+
+ def getValues(self):
+ """ Return intended variable(s) after the dialog has been accepted
+ """
+ return (self.values_old, self.values_new)
+
+ def populateForm(self, values):
+ """ Populate form with given values
+ """
+ for index,v in enumerate(values):
+ if v is not None: self.lnedts[index].setText(str(v))
+ self.values_old[self.fields[index].name] = v
+
+ def executeOption(self, button):
+ """ Perform validation and close the dialog
+ """
+ if self.btnbx_options.standardButton(button) == QDialogButtonBox.Save:
+ values_new = {}
+ # check required fields
+ valid = True
+ for lnedt in self.lnedts:
+ if bool(lnedt.property("REQUIRED")):
+ if str(lnedt.text()).strip() is "":
+ lnedt.setStyleSheet(self.colours["REQUIRED"])
+ valid = False
+ else: lnedt.setStyleSheet("")
+ if not valid:
+ QMessageBox.information(
+ self,
+ "Empty Required Fields",
+ "Please ensure that all required fields are completed."
+ )
+ return
+ # check correct field types
+ valid = True
+ for index,lnedt in enumerate(self.lnedts):
+ try:
+ if str(lnedt.text()).strip() is not "":
+ cast = self.fields[index].type
+ tmp = cast(str(lnedt.text()).strip())
+ values_new[self.fields[index].name] = tmp
+ lnedt.setStyleSheet("")
+ else:
+ values_new[self.fields[index].name] = None
+ except Exception as e:
+ lnedt.setStyleSheet(self.colours["TYPE"])
+ valid = False
+ if not valid:
+ QMessageBox.information(
+ self,
+ "Invalid Field Types",
+ "Please ensure that fields are completed with valid types."
+ )
+ return
+ # check unique fields
+ valid = True
+ for index,lnedt in enumerate(self.lnedts):
+ if str(lnedt.text()).strip() is "": continue
+ if bool(lnedt.property("UNIQUE")):
+ if self.fields[index].name in self.values_old.keys() and values_new[self.fields[index].name] == self.values_old[self.fields[index].name]:
+ lnedt.setStyleSheet("")
+ elif bool(int(self.db.query(self.query %(self.fields[index].name, "%s"), (values_new[self.fields[index].name],))[0][0])):
+ lnedt.setStyleSheet(self.colours["UNIQUE"])
+ valid = False
+ else: lnedt.setStyleSheet("")
+ if not valid:
+ QMessageBox.information(
+ self,
+ "Fields Not Unique",
+ "Please ensure that fields are given unique values."
+ )
+ return
+ # save values
+ self.values_new = values_new
+ # accept dialog
+ self.accept()
+ else:
+ # reject dialog
+ self.reject()
+
+ def setupUi(self, fields):
+ """ Initialize ui
+ """
+ # define ui widgets
+ self.setObjectName(_fromUtf8("dlg_FormBeacon"))
+ self.setCursor(QCursor(Qt.ArrowCursor))
+ self.setModal(True)
+ self.gridLayout = QGridLayout(self)
+ self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
+ self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.verticalLayout = QVBoxLayout()
+ self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.formLayout = QFormLayout()
+ self.formLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
+ self.formLayout.setObjectName(_fromUtf8("formLayout"))
+ self.lbls = []
+ self.lnedts = []
+ # define form fields dynamically from the database schema
+ for index, f in enumerate(fields):
+ lbl = QLabel(self)
+ lbl.setObjectName(_fromUtf8("lbl_%s" %(f.name,)))
+ self.formLayout.setWidget(index, QFormLayout.LabelRole, lbl)
+ self.lbls.append(lbl)
+ lnedt = QLineEdit(self)
+ lnedt.setProperty("REQUIRED", f.required)
+ lnedt.setProperty("UNIQUE", f.unique)
+ lnedt.setObjectName(_fromUtf8("lnedt_%s" %(f.name,)))
+ self.formLayout.setWidget(index, QFormLayout.FieldRole, lnedt)
+ self.lnedts.append(lnedt)
+ lbl.setText(QApplication.translate("dlg_FormBeacon",
+ ("*" if bool(self.lnedts[index].property("REQUIRED")) else "") + f.name.title(),
+ None,
+ QApplication.UnicodeUTF8))
+ lnedt.setProperty("TYPE", QApplication.translate("dlg_FormBeacon", str(f.type), None, QApplication.UnicodeUTF8))
+ self.verticalLayout.addLayout(self.formLayout)
+ self.line_1 = QFrame(self)
+ self.line_1.setFrameShape(QFrame.HLine)
+ self.line_1.setFrameShadow(QFrame.Sunken)
+ self.line_1.setObjectName(_fromUtf8("line_1"))
+ self.verticalLayout.addWidget(self.line_1)
+ self.label = QLabel(self)
+ self.label.setObjectName(_fromUtf8("label"))
+ self.verticalLayout.addWidget(self.label)
+ self.line_2 = QFrame(self)
+ self.line_2.setFrameShape(QFrame.HLine)
+ self.line_2.setFrameShadow(QFrame.Sunken)
+ self.line_2.setObjectName(_fromUtf8("line_2"))
+ self.verticalLayout.addWidget(self.line_2)
+ self.btnbx_options = QDialogButtonBox(self)
+ self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Save)
+ self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
+ self.btnbx_options.setCursor(QCursor(Qt.PointingHandCursor))
+ self.verticalLayout.addWidget(self.btnbx_options)
+ self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
+ # translate ui widgets' text
+ self.setWindowTitle(QApplication.translate("dlg_FormBeacon", "Beacon Form", None, QApplication.UnicodeUTF8))
+ self.label.setText(QApplication.translate("dlg_FormBeacon", "
*Required Field
", None, QApplication.UnicodeUTF8))
# connect ui widgets
self.btnbx_options.clicked.connect(self.executeOption)
QMetaObject.connectSlotsByName(self)
+
class dlg_FormParcel(QDialog):
""" This dialog enables a user to define and modify a parcel
"""
-
- def __init__(self, db, iface, layers, layersDict, autocomplete = [], values = {}, parent = None):
+
+ def __init__(self, db, iface, requiredLayers, SQL_BEACONS, SQL_PARCELS, autocomplete=[], data={}, parent=None):
# initialize QDialog class
super(dlg_FormParcel, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
- self.setupUi(autocomplete, layersDict)
- # initialize instance variables
+ self.setupUi(autocomplete)
self.db = db
self.iface = iface
- self.layers = layers
- self.layersDict = layersDict
+ self.layers = requiredLayers
+ self.SQL_BEACONS = SQL_BEACONS
+ self.SQL_PARCELS = SQL_PARCELS
self.autocomplete = autocomplete
self.values_old = {}
self.values_new = {}
+ self.sequence = []
self.new_accepted = False
# initialize selector tool
- self.selector = featureSelector(iface, layers["BEACONS"], False, self)
+ self.selector = featureSelector(iface, requiredLayers[0].layer, False, self)
# save qgis tool
self.tool = self.selector.parentTool
# populate form if values are given
- if bool(values):
- self.populateForm(values)
+ if bool(data):
+ self.populateForm(data)
self.pshbtn_reset.setEnabled(True)
- def getReturn(self):
- """ Return intended variable(s) after the dialog has been accepted
- """
+ def getValues(self):
return (self.values_old, self.values_new)
- def populateForm(self, values):
+ def populateForm(self, data):
""" Populte form with values given
"""
# get values
- checker = lambda d, v: d[v] if v in d.keys() else None
- feat_id = checker(values, "parcel_id")
- feat_sequence = checker(values, "sequence")
+ checker = lambda d, k: d[k] if k in d.keys() else None
+ feat_id = checker(data, "parcel_id")
+ feat_sequence = checker(data, "sequence")
# use values
if bool(feat_id):
# populate parcel_id
- self.values_old["parcel_id"] = self.db.query(self.layersDict["PARCELS"]["SQL"]["SELECT"], (feat_id,))[0][0]
+ self.values_old["parcel_id"] = self.db.query(
+ self.SQL_PARCELS["SELECT"], (feat_id,)
+ )[0][0]
self.lnedt_parcelID.setText(str(self.values_old["parcel_id"]))
- self.highlightFeature(self.layers["PARCELS"], feat_id)
+ self.highlightFeature(self.layers[1].layer, feat_id)
if bool(feat_sequence):
# populate sequence
+ self.sequence = []
self.values_old["sequence"] = []
- for beacon_id in feat_sequence:
- self.values_old["sequence"].append(self.db.query(self.layersDict["BEACONS"]["SQL"]["SELECT"], (beacon_id,))[0][0])
- self.lstwdg_sequence.addItem(QString(str(self.values_old["sequence"][-1])))
- self.highlightFeatures(self.layers["BEACONS"], feat_sequence)
+ for id in feat_sequence:
+ beacon_id = str(self.db.query(self.SQL_BEACONS["SELECT"], (id,))[0][0])
+ self.sequence.append(beacon_id)
+ self.values_old["sequence"].append(beacon_id)
+ self.lstwdg_sequence.addItem(beacon_id.replace("\n",""))
+ self.highlightFeatures(self.layers[0].layer, feat_sequence)
+ self.selector.selected = feat_sequence
# update selector selection
self.selector.selected = feat_sequence
@@ -312,7 +624,12 @@ def highlightFeatures(self, layer, features):
""" Highlight multiple features on a vector layer
"""
layer.setSelectedFeatures(features)
-
+
+ def captured(self, selected):
+ """ Notify the dialog of a feature selection and disable selecting
+ """
+ pass
+
def executeOption(self, button):
""" Perform validation and close the dialog
"""
@@ -320,66 +637,100 @@ def executeOption(self, button):
parcel_id = str(self.lnedt_parcelID.text()).strip()
# check that parcel id exists
if parcel_id == "":
- QMessageBox.information(self, "Invalid Parcel ID", "Please enter a parcel ID.")
+ QMessageBox.information(
+ self,
+ "Invalid Parcel ID",
+ "Please enter a parcel ID."
+ )
+ return
+ # check that parcel id is an int
+ try:
+ int(parcel_id)
+ except ValueError:
+ QMessageBox.information(
+ self,
+ "Invalid Parcel ID",
+ "Please enter a number for the parcel ID."
+ )
return
# check that parcel id is valid (i.e. current, unique, available)
if "parcel_id" in self.values_old.keys() and str(self.values_old["parcel_id"]) == parcel_id:
pass
- elif not bool(int(self.db.query(self.layersDict["PARCELS"]["SQL"]["UNIQUE"], (parcel_id,))[0][0])):
- if not self.new_accepted and QMessageBox.question(self, 'Confirm New Parcel ID', "Are you sure you want to create a new parcel ID?", QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
+ elif not bool(self.db.query(
+ self.SQL_PARCELS["UNIQUE"], (int(parcel_id),)
+ )[0][0]):
+ if not self.new_accepted and QMessageBox.question(
+ self,
+ 'Confirm New Parcel ID',
+ "Are you sure you want to create a new parcel ID?",
+ QMessageBox.Yes,
+ QMessageBox.No
+ ) == QMessageBox.No:
return
self.new_accepted = True
else:
- if not bool(self.db.query(self.layersDict["PARCELS"]["SQL"]["AVAILABLE"], (parcel_id,))[0][0]):
- QMessageBox.information(self, "Duplicated Parcel ID", "Please enter a unique or available parcel ID.")
+ if not bool(self.db.query(
+ self.SQL_PARCELS["AVAILABLE"],
+ (parcel_id,)
+ )[0][0]):
+ QMessageBox.information(
+ self,
+ "Duplicated Parcel ID",
+ "Please enter a unique or available parcel ID."
+ )
return
# check that at least 3 beacons exist within the sequence
if len(self.selector.selected) < 3:
- QMessageBox.information(self, "Too Few Beacons", "Please ensure that there are at least 3 beacons listed in the sequence.")
+ QMessageBox.information(
+ self,
+ "Too Few Beacons",
+ "Please ensure that there are at least 3 beacons listed in the sequence."
+ )
return
# save parcel id
self.values_new["parcel_id"] = parcel_id
# save sequence
- sequence = []
- for i in self.selector.selected:
- sequence.append(self.db.query(self.layersDict["BEACONS"]["SQL"]["SELECT"], (i,))[0][0])
- self.values_new["sequence"] = sequence
+ self.values_new["sequence"] = self.sequence
# reset qgis tool
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection
- for l in self.layers.values(): l.removeSelection()
+ for l in self.layers: l.layer.removeSelection()
# accept dialog
self.accept()
else:
# reset qgis tool
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection
- for l in self.layers.values(): l.removeSelection()
+ for l in self.layers: l.layer.removeSelection()
# accept dialog
self.reject()
- def captured(self, selected):
- """ Notify the dialog of a feature selection and disable selecting
- """
- # clear sequence
- self.lstwdg_sequence.clear()
- # create sequence
- for i in selected:
- self.lstwdg_sequence.addItem(QString(str(self.db.query(self.layersDict["BEACONS"]["SQL"]["SELECT"], (i,))[0][0])))
-
def newBeacon(self):
""" Define a new beacon on the fly to be added to the parcel sequence
"""
# disable self
self.setEnabled(False)
- # present beacon form
- data = self.db.getSchema(self.layersDict["BEACONS"]["TABLE"], [self.layersDict["BEACONS"]["GEOM"], self.layersDict["BEACONS"]["PKEY"]])
- frm = dlg_FormBeacon(self.db, data, self.layersDict["BEACONS"]["SQL"], parent = self)
+ # get fields
+ fields = self.db.getSchema(
+ self.layers[0].table, [
+ self.layers[0].geometry_column,
+ self.layers[0].primary_key
+ ])
+ # display form
+ frm = dlg_FormBeacon(
+ self.db,
+ self.SQL_BEACONS["UNIQUE"],
+ fields
+ )
frm.show()
frm_ret = frm.exec_()
if bool(frm_ret):
- # save new beacon
- id = self.db.query(self.layersDict["BEACONS"]["SQL"]["INSERT"].format(fields = ", ".join([f["NAME"] for f in data]), values = ", ".join(["%s" for f in data])), [frm.getReturn()[1][f["NAME"]] for f in data])[0][0]
+ # add beacon to database
+ values_old, values_new = frm.getValues()
+ id = self.db.query(
+ self.SQL_BEACONS["INSERT"].format(fields = ", ".join(sorted(values_new.keys())), values = ", ".join(["%s" for k in values_new.keys()])), [values_new[k] for k in sorted(values_new.keys())])[0][0]
+ self.iface.mapCanvas().refresh()
+ self.highlightFeature(self.layers[0].layer, id)
self.selector.appendSelection(id)
# enable self
self.setEnabled(True)
@@ -402,22 +753,29 @@ def stopSeq(self):
self.selector.disableCapturing()
# perform button stuffs
self.pshbtn_stop.setEnabled(False)
+ self.pshbtn_new.setEnabled(False)
+ self.lstwdg_sequence.clear()
+ self.sequence = []
+ for i in self.selector.selected:
+ beacon_id = str(self.db.query(self.SQL_BEACONS["SELECT"], (i,))[0][0])
+ self.sequence.append(beacon_id)
+ self.lstwdg_sequence.addItem(beacon_id.replace("\n",""))
self.pshbtn_start.setEnabled(True)
self.pshbtn_reset.setEnabled(True)
- self.pshbtn_new.setEnabled(False)
def resetSeq(self):
""" Reset captured sequence
"""
# clear selection
self.selector.clearSelection()
+ self.sequence = []
# clear sequence
self.lstwdg_sequence.clear()
# perform button stuffs
self.pshbtn_reset.setEnabled(False)
self.pshbtn_start.setEnabled(True)
- def setupUi(self, autocomplete, layersDict):
+ def setupUi(self, autocomplete):
""" Initialize ui
"""
# define ui widgets
@@ -490,13 +848,20 @@ def setupUi(self, autocomplete, layersDict):
self.verticalLayout_2.addWidget(self.btnbx_options)
self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1)
# translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_FormParcel", "Parcel Form", None, QApplication.UnicodeUTF8))
- self.lbl_parcelID.setText(QApplication.translate("dlg_FormParcel", "Parcel ID", None, QApplication.UnicodeUTF8))
- self.lbl_sequence.setText(QApplication.translate("dlg_FormParcel", "Beacon Sequence", None, QApplication.UnicodeUTF8))
- self.pshbtn_new.setText(QApplication.translate("dlg_FormParcel", "New Beacon", None, QApplication.UnicodeUTF8))
- self.pshbtn_start.setText(QApplication.translate("dlg_FormParcel", "Start", None, QApplication.UnicodeUTF8))
- self.pshbtn_stop.setText(QApplication.translate("dlg_FormParcel", "Stop", None, QApplication.UnicodeUTF8))
- self.pshbtn_reset.setText(QApplication.translate("dlg_FormParcel", "Reset", None, QApplication.UnicodeUTF8))
+ self.setWindowTitle(QApplication.translate("dlg_FormParcel",
+ "Parcel Form", None, QApplication.UnicodeUTF8))
+ self.lbl_parcelID.setText(QApplication.translate("dlg_FormParcel",
+ "Parcel ID", None, QApplication.UnicodeUTF8))
+ self.lbl_sequence.setText(QApplication.translate("dlg_FormParcel",
+ "Beacon Sequence", None, QApplication.UnicodeUTF8))
+ self.pshbtn_new.setText(QApplication.translate("dlg_FormParcel",
+ "New Beacon", None, QApplication.UnicodeUTF8))
+ self.pshbtn_start.setText(QApplication.translate("dlg_FormParcel",
+ "Start", None, QApplication.UnicodeUTF8))
+ self.pshbtn_stop.setText(QApplication.translate("dlg_FormParcel",
+ "Stop", None, QApplication.UnicodeUTF8))
+ self.pshbtn_reset.setText(QApplication.translate("dlg_FormParcel",
+ "Reset", None, QApplication.UnicodeUTF8))
# connect ui widgets
self.pshbtn_new.clicked.connect(self.newBeacon)
self.pshbtn_start.clicked.connect(self.startSeq)
@@ -505,287 +870,27 @@ def setupUi(self, autocomplete, layersDict):
self.btnbx_options.clicked.connect(self.executeOption)
QMetaObject.connectSlotsByName(self)
-class dlg_FormBeacon(QDialog):
- """ This dialog enables a user to define and modify a beacon
- """
-
- def __init__(self, db, data, query, values = [], parent = None):
- # initialize QDialog class
- super(dlg_FormBeacon, self).__init__(parent, Qt.WindowStaysOnTopHint)
- # initialize ui
- self.setupUi(data)
- # initialize instance variables
- self.db = db
- self.data = data
- self.query = query
- self.values_old = {}
- self.values_new = {}
- self.colours = {
- "REQUIRED":"background-color: rgba(255, 107, 107, 150);",
- "TYPE":"background-color: rgba(107, 107, 255, 150);",
- "UNIQUE":"background-color: rgba(107, 255, 107, 150);"
- }
- # populate form if values are given
- if bool(values):
- self.populateForm(values)
-
- def getReturn(self):
- """ Return intended variable(s) after the dialog has been accepted
- """
- return (self.values_old, self.values_new)
-
- def populateForm(self, values):
- """ Populate form with given values
- """
- for index,value in enumerate(values):
- if value is not None: self.lnedts[index].setText(str(value))
- self.values_old[self.data[index]["NAME"]] = value
-
- def executeOption(self, button):
- """ Perform validation and close the dialog
- """
- if self.btnbx_options.standardButton(button) == QDialogButtonBox.Save:
- values_new = {}
- # check required fields
- valid = True
- for lnedt in self.lnedts:
- if bool(lnedt.property("REQUIRED").toBool()):
- if str(lnedt.text()).strip() is "":
- lnedt.setStyleSheet(self.colours["REQUIRED"])
- valid = False
- else: lnedt.setStyleSheet("")
- if not valid:
- QMessageBox.information(self, "Empty Required Fields", "Please ensure that all required fields are completed.")
- return
- # check correct field types
- valid = True
- for index,lnedt in enumerate(self.lnedts):
- try:
- if str(lnedt.text()).strip() is not "":
- cast = self.data[index]["TYPE"]
- tmp = cast(str(lnedt.text()))
- values_new[self.data[index]["NAME"]] = tmp
- lnedt.setStyleSheet("")
- else:
- values_new[self.data[index]["NAME"]] = None
- except Exception as e:
- lnedt.setStyleSheet(self.colours["TYPE"])
- valid = False
- if not valid:
- QMessageBox.information(self, "Invalid Field Types", "Please ensure that fields are completed with valid types.")
- return
- # check unique fields
- valid = True
- for index,lnedt in enumerate(self.lnedts):
- if str(lnedt.text()).strip() is "": continue
- if bool(lnedt.property("UNIQUE").toBool()):
- if self.data[index]["NAME"] in self.values_old.keys() and values_new[self.data[index]["NAME"]] == self.values_old[self.data[index]["NAME"]]:
- lnedt.setStyleSheet("")
- elif bool(int(self.db.query(self.query["UNIQUE"] %(self.data[index]["NAME"], "%s"), (values_new[self.data[index]["NAME"]],))[0][0])):
- lnedt.setStyleSheet(self.colours["UNIQUE"])
- valid = False
- else: lnedt.setStyleSheet("")
- if not valid:
- QMessageBox.information(self, "Fields Not Unique", "Please ensure that fields are given unique values.")
- return
- # save values
- self.values_new = values_new
- # accept dialog
- self.accept()
- else:
- # reject dialog
- self.reject()
-
- def setupUi(self, data):
- """ Initialize ui
- """
- # define ui widgets
- self.setObjectName(_fromUtf8("dlg_FormBeacon"))
- self.setCursor(QCursor(Qt.ArrowCursor))
- self.setModal(True)
- self.gridLayout = QGridLayout(self)
- self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
- self.verticalLayout = QVBoxLayout()
- self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
- self.formLayout = QFormLayout()
- self.formLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
- self.data = data
- self.lbls = []
- self.lnedts = []
- # define form fields dynamically from the database schema
- for index,field in enumerate(self.data):
- lbl = QLabel(self)
- lbl.setObjectName(_fromUtf8("lbl_%s" %(field["NAME"],)))
- self.formLayout.setWidget(index, QFormLayout.LabelRole, lbl)
- self.lbls.append(lbl)
- lnedt = QLineEdit(self)
- lnedt.setProperty("REQUIRED", field["REQUIRED"])
- lnedt.setProperty("UNIQUE", field["UNIQUE"])
- lnedt.setObjectName(_fromUtf8("lnedt_%s" %(field["NAME"],)))
- self.formLayout.setWidget(index, QFormLayout.FieldRole, lnedt)
- self.lnedts.append(lnedt)
- lbl.setText(QApplication.translate("dlg_FormBeacon", ("*" if bool(self.lnedts[index].property("REQUIRED").toBool()) else "") + field["NAME"].title(), None, QApplication.UnicodeUTF8))
- lnedt.setProperty("TYPE", QApplication.translate("dlg_FormBeacon", str(field["TYPE"]), None, QApplication.UnicodeUTF8))
- self.verticalLayout.addLayout(self.formLayout)
- self.line_1 = QFrame(self)
- self.line_1.setFrameShape(QFrame.HLine)
- self.line_1.setFrameShadow(QFrame.Sunken)
- self.line_1.setObjectName(_fromUtf8("line_1"))
- self.verticalLayout.addWidget(self.line_1)
- self.label = QLabel(self)
- self.label.setObjectName(_fromUtf8("label"))
- self.verticalLayout.addWidget(self.label)
- self.line_2 = QFrame(self)
- self.line_2.setFrameShape(QFrame.HLine)
- self.line_2.setFrameShadow(QFrame.Sunken)
- self.line_2.setObjectName(_fromUtf8("line_2"))
- self.verticalLayout.addWidget(self.line_2)
- self.btnbx_options = QDialogButtonBox(self)
- self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Save)
- self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
- self.btnbx_options.setCursor(QCursor(Qt.PointingHandCursor))
- self.verticalLayout.addWidget(self.btnbx_options)
- self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
- # translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_FormBeacon", "Beacon Form", None, QApplication.UnicodeUTF8))
- self.label.setText(QApplication.translate("dlg_FormBeacon", "*Required Field
", None, QApplication.UnicodeUTF8))
- # connect ui widgets
- self.btnbx_options.clicked.connect(self.executeOption)
- QMetaObject.connectSlotsByName(self)
-
-class dlg_FormDatabase(QDialog):
- """ This dialog enables the user to define the database connection parameters
- """
-
- def __init__(self, parent = None):
- # initialize QDialog class
- super(dlg_FormDatabase, self).__init__(parent, Qt.WindowStaysOnTopHint)
- # initialize ui
- self.setupUi()
- # initialize instance variables
- self.save = False
- self.db = None
- self.params = {}
- self.colours = {
- "EMPTY":"background-color: rgba(255, 107, 107, 150);",
- }
-
- def getReturn(self):
- """ Return intended variable(s) after the dialog has been accepted
- """
- return (self.save, self.db, self.params)
-
- def executeOption(self, button):
- """ Perform validation and close the dialog
- """
- if self.btnbx_options.standardButton(button) == QDialogButtonBox.Ok:
- # validate fields
- params = {}
- valid = True
- for lnedt in self.findChildren(QLineEdit):
- if str(lnedt.text()).strip() is "":
- lnedt.setStyleSheet(self.colours["EMPTY"])
- valid = False
- else:
- lnedt.setStyleSheet("")
- params[str(lnedt.property("KEY").toString())] = str(lnedt.text())
- if not valid:
- QMessageBox.information(self, "Empty Database Fields", "Please ensure that all database fields are completed.")
- return
- # test connection
- db = None
- try:
- import database
- db = database.manager(params)
- except Exception:
- QMessageBox.information(self, "Invalid Database Settings", "Please ensure that the supplied database settings are correct.")
- return
- # save db
- self.db = db
- # save parameters
- self.params = params
- # accept dialog
- self.accept()
- else:
- # reject dialog
- self.reject()
-
- def saveConnection(self, state):
- """ Select option to save database settings
- """
- self.save = bool(state)
-
- def setupUi(self):
- """ Initialize ui
- """
- # define ui widgets
- fields = ["HOST","PORT","NAME","USER","PASSWORD"]
- self.setObjectName(_fromUtf8("dlg_FormDatabase"))
- self.setCursor(QCursor(Qt.ArrowCursor))
- self.setModal(True)
- self.gridLayout = QGridLayout(self)
- self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
- self.verticalLayout = QVBoxLayout()
- self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
- self.formLayout = QFormLayout()
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
- self.lbls = []
- self.lnedts = []
- # define form fields dynamically from above list
- for index, field in enumerate(fields):
- lbl = QLabel(self)
- lbl.setObjectName(_fromUtf8("lbl_%s" %(field.lower(),)))
- lbl.setText(QApplication.translate("dlg_FormDatabase", field.title(), None, QApplication.UnicodeUTF8))
- self.formLayout.setWidget(index, QFormLayout.LabelRole, lbl)
- lnedt = QLineEdit(self)
- lnedt.setObjectName(_fromUtf8("lnedt_%s" %(field.lower(),)))
- lnedt.setProperty("KEY", field)
- if field == "PASSWORD": lnedt.setEchoMode(QLineEdit.Password)
- self.formLayout.setWidget(index, QFormLayout.FieldRole, lnedt)
- self.lbls.append(lbl)
- self.lnedts.append(lnedt)
- self.verticalLayout.addLayout(self.formLayout)
- self.horizontalLayout = QHBoxLayout()
- self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
- self.chkbx_save = QCheckBox(self)
- self.chkbx_save.setObjectName(_fromUtf8("chkbx_save"))
- self.chkbx_save.setCursor(QCursor(Qt.PointingHandCursor))
- self.horizontalLayout.addWidget(self.chkbx_save)
- self.verticalLayout.addLayout(self.horizontalLayout)
- self.btnbx_options = QDialogButtonBox(self)
- self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
- self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
- self.btnbx_options.setCursor(QCursor(Qt.PointingHandCursor))
- self.verticalLayout.addWidget(self.btnbx_options)
- self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
- # translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_FormDatabase", "Database Settings", None, QApplication.UnicodeUTF8))
- self.chkbx_save.setText(QApplication.translate("dlg_FormDatabase", "Save Connection", None, QApplication.UnicodeUTF8))
- # connect ui widgets
- self.chkbx_save.stateChanged.connect(self.saveConnection)
- self.btnbx_options.clicked.connect(self.executeOption)
- QMetaObject.connectSlotsByName(self)
class dlg_FormBearDist(QDialog):
""" This dialog enables the user to define bearings and distances
"""
- def __init__(self, db, beardistSql, beaconSql, beaconSchema, parent = None):
+ def __init__(self, db, SQL_BEARDIST, SQL_BEACONS, requiredLayers, parent = None):
# initialize QDialog class
super(dlg_FormBearDist, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
self.setupUi()
- # initialize instance variables
self.db = db
- self.beardistSql = beardistSql
- self.beaconSql = beaconSql
- self.beaconSchema = beaconSchema
+ self.SQL_BEARDIST = SQL_BEARDIST
+ self.SQL_BEACONS = SQL_BEACONS
+ self.layers = requiredLayers
self.auto = {
- "SURVEYPLAN":self.db.query(self.beardistSql["AUTO_SURVEYPLAN"])[0][0],
- "REFERENCEBEACON":self.db.query(self.beardistSql["AUTO_REFERENCEBEACON"])[0][0],
+ "SURVEYPLAN":self.db.query(
+ SQL_BEARDIST["AUTO_SURVEYPLAN"]
+ )[0][0],
+ "REFERENCEBEACON":self.db.query(
+ SQL_BEARDIST["AUTO_REFERENCEBEACON"]
+ )[0][0],
"FROMBEACON":[]
}
self.surveyPlan = None
@@ -824,6 +929,9 @@ def setCurrentItem(self, index, clear=False, enabled=False):
def initItemSurveyPlan(self, forward=True):
""" Initialize form elements for the survey plan item
"""
+ if not forward:
+ if not self.confirmBack():
+ return
# update autocompletion
model = QStringListModel()
model.setStringList(self.auto["SURVEYPLAN"])
@@ -842,11 +950,16 @@ def checkItemSurveyPlan(self, forward):
# check direction
if forward:
# check that a server plan number was specified
- if str(self.lnedt_plan.text()).strip() is "":
- QMessageBox.information(self, "Empty Survey Plan Number", "Please enter a surver plan number.")
+ surveyPlan = str(self.lnedt_plan.text()).strip()
+ if surveyPlan is "":
+ QMessageBox.information(
+ self,
+ "Empty Survey Plan Number",
+ "Please enter a surver plan number."
+ )
return
# set survey plan number
- self.surveyPlan = str(self.lnedt_plan.text())
+ self.surveyPlan = surveyPlan
# display next toolbox item
self.initItemReferenceBeacon()
else: pass
@@ -854,6 +967,9 @@ def checkItemSurveyPlan(self, forward):
def initItemReferenceBeacon(self, forward=True):
""" Initialize form elements for the reference beacon item
"""
+ if not forward:
+ if not self.confirmBack():
+ return
# update autocompletion
model = QStringListModel()
model.setStringList(self.auto["REFERENCEBEACON"])
@@ -869,7 +985,10 @@ def initItemReferenceBeacon(self, forward=True):
if self.surveyPlan in self.auto["SURVEYPLAN"]:
# update item contents
self.lnedt_ref.setEnabled(False)
- self.lnedt_ref.setText(str(self.db.query(self.beardistSql["EXIST_REFERENCEBEACON"], (self.surveyPlan,))[0][0]))
+ self.lnedt_ref.setText(str(self.db.query(
+ self.SQL_BEARDIST["EXIST_REFERENCEBEACON"],
+ (self.surveyPlan,)
+ )[0][0]))
else:
# update item contents
self.lnedt_ref.setEnabled(True)
@@ -883,10 +1002,14 @@ def checkItemReferenceBeacon(self, forward):
# check direction
if forward:
# check that a reference beacon was specified
- if str(self.lnedt_ref.text()).strip() is "":
- QMessageBox.information(self, "Empty Reference Beacon", "Please enter a reference beacon.")
+ referenceBeacon = str(self.lnedt_ref.text()).strip()
+ if referenceBeacon is "":
+ QMessageBox.information(
+ self,
+ "Empty Reference Beacon",
+ "Please enter a reference beacon."
+ )
return
- referenceBeacon = str(self.lnedt_ref.text())
# check if reference beacon exists
if referenceBeacon in self.auto["REFERENCEBEACON"]:
# set reference beacon
@@ -897,15 +1020,31 @@ def checkItemReferenceBeacon(self, forward):
# disable self
self.setEnabled(False)
# present beacon form
- column_index = self.db.query(self.beardistSql["INDEX_REFERENCEBEACON"])[0][0]
- frm = dlg_FormBeacon(self.db, self.beaconSchema, self.beaconSql, parent = self)
+ column_index = self.db.query(
+ self.SQL_BEARDIST["INDEX_REFERENCEBEACON"]
+ )[0][0]
+ # get fields
+ fields = self.db.getSchema(
+ self.layers[0].table, [
+ self.layers[0].geometry_column,
+ self.layers[0].primary_key
+ ])
+ # display form
+ frm = dlg_FormBeacon(
+ self.db,
+ self.SQL_BEACONS["UNIQUE"],
+ fields,
+ parent = self
+ )
frm.lnedts[column_index].setText(referenceBeacon)
frm.lnedts[column_index].setEnabled(False)
frm.show()
frm_ret = frm.exec_()
if bool(frm_ret):
- # save new beacon
- self.db.query(self.beaconSql["INSERT"].format(fields = ", ".join([f["NAME"] for f in self.beaconSchema]), values = ", ".join(["%s" for f in self.beaconSchema])), [frm.getReturn()[1][f["NAME"]] for f in self.beaconSchema])[0][0]
+ # add beacon to database
+ values_old, values_new = frm.getValues()
+ self.db.query(
+ self.SQL_BEACONS["INSERT"].format(fields = ", ".join(sorted(values_new.keys())), values = ", ".join(["%s" for k in values_new.keys()])), [values_new[k] for k in sorted(values_new.keys())])
# set reference beacon
self.referenceBeacon = referenceBeacon
self.auto["REFERENCEBEACON"].append(referenceBeacon)
@@ -936,7 +1075,10 @@ def initItemBearDistChain(self, forward=True):
# check if survey plan number is predefined
if self.surveyPlan in self.auto["SURVEYPLAN"]:
# get defined bearings and distances
- records = self.db.query(self.beardistSql["EXIST_BEARDISTCHAINS"], (self.surveyPlan,))
+ records = self.db.query(
+ self.SQL_BEARDIST["EXIST_BEARDISTCHAINS"],
+ (self.surveyPlan,)
+ )
if records not in [None, []]:
for oid,link in enumerate(records):
self.beardistChain.append([list(link), "NULL", oid])
@@ -953,13 +1095,25 @@ def checkItemBearDistChain(self, forward):
# check direction
if forward:
if not bool(self.surveyPlan):
- QMessageBox.information(self, "No Survey Plan", "Please specify a survey plan number")
+ QMessageBox.information(
+ self,
+ "No Survey Plan",
+ "Please specify a survey plan number"
+ )
return
if not bool(self.referenceBeacon):
- QMessageBox.information(self, "No Reference Beacon", "Please specify a reference beacon")
+ QMessageBox.information(
+ self,
+ "No Reference Beacon",
+ "Please specify a reference beacon"
+ )
return
if not bool(self.beardistChain):
- QMessageBox.information(self, "No Bearing and Distance Chain", "Please capture bearings and distances")
+ QMessageBox.information(
+ self,
+ "No Bearing and Distance Chain",
+ "Please capture bearings and distances"
+ )
return
self.accept()
else:
@@ -1007,17 +1161,29 @@ def getSelectedIndex(self, action):
items = self.lst_chain.selectedItems()
# check list is non-empty
if len(items) == 0:
- QMessageBox.information(self, "No Link Selected", "Please select a link to edit")
+ QMessageBox.information(
+ self,
+ "No Link Selected",
+ "Please select a link to edit"
+ )
return None
# check list does not contain more than one item
if len(items) > 1:
- QMessageBox.information(self, "Too Many Links Selected", "Please select only one link to edit")
+ QMessageBox.information(
+ self,
+ "Too Many Links Selected",
+ "Please select only one link to edit"
+ )
return None
# get item index
index = self.lst_chain.row(items[0])
# check that index is of an end link
if not bool(self.isEndLink(index)):
- if QMessageBox.question(self, "Non End Link Selected", "The link you selected is not at the end of a chain. Are you sure you want to %s this link?" %(action.lower(),), QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
+ if QMessageBox.question(
+ self,
+ "Non End Link Selected",
+ "The link you selected is not at the end of a chain. If you {action} this link it will {action} all links that depend on this one. Are you sure you want to {action} this link?".format(action=action.lower()),
+ QMessageBox.Yes, QMessageBox.No) == QMessageBox.No:
return None
# return index
return index
@@ -1031,7 +1197,7 @@ def updateBearDistChainDependants(self):
# populate dependants
for link in self.beardistChain:
#QMessageBox.information(self,QString(','.join(link[0][:4])))
- self.lst_chain.addItem(QString(self.beardistStr %tuple(link[0][:4])))
+ self.lst_chain.addItem(self.beardistStr %tuple(link[0][:4]))
self.auto["FROMBEACON"].append(link[0][3])
self.auto["FROMBEACON"].sort()
@@ -1039,15 +1205,20 @@ def addLink(self):
""" Add a link to the beardist chain
"""
while True:
- dlg = dlg_FormBearDistLink(self.auto["FROMBEACON"], parent = self)
+ dlg = dlg_FormBearDistLink(
+ self.db,
+ self.auto["FROMBEACON"],
+ self.SQL_BEACONS["UNIQUE"],
+ parent = self
+ )
dlg.show()
dlg_ret = dlg.exec_()
if bool(dlg_ret):
- values = dlg.getReturn()
+ values = dlg.getValues()
self.beardistChain.append([values, "INSERT", None])
self.updateBearDistChainDependants()
else: break
- if len(self.beardistChain) == 1:
+ if len(self.beardistChain) >= 1:
self.pshbtn_chain_finish.setEnabled(True)
self.pshbtn_chain_edt.setEnabled(True)
self.pshbtn_chain_del.setEnabled(True)
@@ -1060,12 +1231,17 @@ def editLink(self):
# check selection
if index is not None:
# display dialog
- dlg = dlg_FormBearDistLink(self.auto["FROMBEACON"], values = self.beardistChain[index][0], parent = self)
+ dlg = dlg_FormBearDistLink(
+ self.db,
+ self.auto["FROMBEACON"],
+ self.SQL_BEACONS["UNIQUE"],
+ values = self.beardistChain[index][0],
+ parent = self)
if self.isLastAnchorLink(index): dlg.lnedts[2].setEnabled(False)
dlg.show()
dlg_ret = dlg.exec_()
if bool(dlg_ret):
- values = dlg.getReturn()
+ values = dlg.getValues()
# check if anything was changed
if values == self.beardistChain[index][0]: return
# check if reference beacon can be found
@@ -1095,7 +1271,11 @@ def deleteLink(self):
if index is not None:
# prevent last link to use reference beacon from being deleted
if self.isLastAnchorLink(index):
- QMessageBox.warning(self, "Last Link To Reference Beacon", "Cannot remove last link to reference beacon")
+ QMessageBox.warning(
+ self,
+ "Last Link To Reference Beacon",
+ "Cannot remove last link to reference beacon"
+ )
return
# recursively delete dependant links
self.deleteLinkDependants(self.beardistChain[index][0][3])
@@ -1250,20 +1430,79 @@ def setupUi(self):
self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
self.grdlyt_dlg.addWidget(self.btnbx_options, 1, 0, 1, 1)
# translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_FormBearDist", "Bearings and Distances Form", None, QApplication.UnicodeUTF8))
- self.lbl_plan.setText(QApplication.translate("dlg_FormBearDist", "Survey Plan", None, QApplication.UnicodeUTF8))
- self.pshbtn_plan_next.setText(QApplication.translate("dlg_FormBearDist", "Next", None, QApplication.UnicodeUTF8))
- self.tlbx.setItemText(self.tlbx.indexOf(self.itm_plan), QApplication.translate("dlg_FormBearDist", "Step 1: Define Survey Plan", None, QApplication.UnicodeUTF8))
- self.lbl_ref.setText(QApplication.translate("dlg_FormBearDist", "Reference Beacon", None, QApplication.UnicodeUTF8))
- self.pshbtn_ref_back.setText(QApplication.translate("dlg_FormBearDist", "Back", None, QApplication.UnicodeUTF8))
- self.pshbtn_ref_next.setText(QApplication.translate("dlg_FormBearDist", "Next", None, QApplication.UnicodeUTF8))
- self.tlbx.setItemText(self.tlbx.indexOf(self.itm_ref), QApplication.translate("dlg_FormBearDist", "Step 2: Define Reference Beacon", None, QApplication.UnicodeUTF8))
- self.pshbtn_chain_add.setText(QApplication.translate("dlg_FormBearDist", "Add Link", None, QApplication.UnicodeUTF8))
- self.pshbtn_chain_edt.setText(QApplication.translate("dlg_FormBearDist", "Edit Link", None, QApplication.UnicodeUTF8))
- self.pshbtn_chain_del.setText(QApplication.translate("dlg_FormBearDist", "Delete Link", None, QApplication.UnicodeUTF8))
- self.pshbtn_chain_back.setText(QApplication.translate("dlg_FormBearDist", "Back", None, QApplication.UnicodeUTF8))
- self.pshbtn_chain_finish.setText(QApplication.translate("dlg_FormBearDist", "Finish", None, QApplication.UnicodeUTF8))
- self.tlbx.setItemText(self.tlbx.indexOf(self.itm_chain), QApplication.translate("dlg_FormBearDist", "Step 3: Define Bearings and Distances Chain", None, QApplication.UnicodeUTF8))
+ self.setWindowTitle(QApplication.translate(
+ "dlg_FormBearDist",
+ "Bearings and Distances Form",
+ None,
+ QApplication.UnicodeUTF8))
+ self.lbl_plan.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Survey Plan",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_plan_next.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Next",
+ None,
+ QApplication.UnicodeUTF8))
+ self.tlbx.setItemText(self.tlbx.indexOf(self.itm_plan),
+ QApplication.translate(
+ "dlg_FormBearDist",
+ "Step 1: Define Survey Plan",
+ None,
+ QApplication.UnicodeUTF8))
+ self.lbl_ref.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Reference Beacon",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_ref_back.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Back",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_ref_next.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Next",
+ None,
+ QApplication.UnicodeUTF8))
+ self.tlbx.setItemText(self.tlbx.indexOf(self.itm_ref),
+ QApplication.translate(
+ "dlg_FormBearDist",
+ "Step 2: Define Reference Beacon",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_chain_add.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Add Link",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_chain_edt.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Edit Link",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_chain_del.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Delete Link",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_chain_back.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Back",
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_chain_finish.setText(QApplication.translate(
+ "dlg_FormBearDist",
+ "Finish",
+ None,
+ QApplication.UnicodeUTF8))
+ self.tlbx.setItemText(self.tlbx.indexOf(self.itm_chain),
+ QApplication.translate(
+ "dlg_FormBearDist",
+ "Step 3: Define Bearings and Distances Chain",
+ None,
+ QApplication.UnicodeUTF8))
# connect ui widgets
self.btnbx_options.accepted.connect(self.accept)
self.btnbx_options.rejected.connect(self.reject)
@@ -1277,11 +1516,19 @@ def setupUi(self):
self.pshbtn_chain_del.clicked.connect(self.deleteLink)
QMetaObject.connectSlotsByName(self)
+ def confirmBack(self):
+ return QMessageBox.question(
+ self,
+ "Going Back",
+ "Any changes made will be lost. Are your sure that you want to go back?",
+ QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes
+
+
class dlg_FormBearDistLink(QDialog):
""" This dialog enables the user to add a bearing and distance link
"""
- def __init__(self, fromBeacons, values=[], parent = None):
+ def __init__(self, db, fromBeacons, query, values=[], parent = None):
# initialize QDialog class
super(dlg_FormBearDistLink, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
@@ -1289,11 +1536,14 @@ def __init__(self, fromBeacons, values=[], parent = None):
# initialize instance variables
self.values_old = values
self.values_new = []
+ self.db = db
+ self.query = query
self.fromBeacons = fromBeacons
self.colours = {
"EMPTY":"background-color: rgba(255, 107, 107, 150);",
"TYPE":"background-color: rgba(107, 107, 255, 150);",
- "BEACON":"background-color: rgba(107, 255, 107, 150);"
+ "BEACON":"background-color: rgba(107, 255, 107, 150);",
+ "UNIQUE":"background-color: rgba(107, 255, 107, 150);"
}
# populate form if values are given
if bool(values):
@@ -1306,9 +1556,7 @@ def populateForm(self, values):
if values[index] is not None:
lnedt.setText(str(values[index]))
- def getReturn(self):
- """ Return intended variable(s) after the dialog has been accepted
- """
+ def getValues(self):
return self.values_new
def executeOption(self, button):
@@ -1319,20 +1567,24 @@ def executeOption(self, button):
# check required fields
valid = True
for index,lnedt in enumerate(self.lnedts):
- if not self.fields[index]["NULLABLE"]:
+ if self.fields[index].required:
if str(lnedt.text()).strip() is "":
lnedt.setStyleSheet(self.colours["EMPTY"])
valid = False
else: lnedt.setStyleSheet("")
if not valid:
- QMessageBox.information(self, "Empty Fields", "Please ensure that all fields are completed.")
+ QMessageBox.information(
+ self,
+ "Empty Required Fields",
+ "Please ensure that all required fields are completed."
+ )
return
# check correct field types
valid = True
for index,lnedt in enumerate(self.lnedts):
try:
- cast = self.fields[index]["TYPE"]
- txt = str(lnedt.text())
+ cast = self.fields[index].type
+ txt = str(lnedt.text()).strip()
if txt is "": tmp = None
else: tmp = cast(txt)
values_new.append(tmp)
@@ -1341,21 +1593,51 @@ def executeOption(self, button):
lnedt.setStyleSheet(self.colours["TYPE"])
valid = False
if not valid:
- QMessageBox.information(self, "Invalid Field Types", "Please ensure that fields are completed with valid types.")
+ QMessageBox.information(
+ self,
+ "Invalid Field Types",
+ "Please ensure that fields are completed with valid types."
+ )
return
# check valid from beacon field
valid = True
for index,lnedt in enumerate(self.lnedts):
- if self.fields[index]["NAME"].lower() == "from":
+ if self.fields[index].name.lower() == "from":
if str(lnedt.text()) not in self.fromBeacons:
lnedt.setStyleSheet(self.colours["BEACON"])
valid = False
- if not bool(self.values_old) and self.fields[index]["NAME"].lower() == "to":
- if str(lnedt.text()) in self.fromBeacons:
- lnedt.setStyleSheet(self.colours["BEACON"])
+ if not valid:
+ QMessageBox.information(
+ self,
+ "Invalid Reference",
+ "Please ensure that specified beacons are valid."
+ )
+ return
+ # check valid to beacon field
+ valid = True
+ for index,lnedt in enumerate(self.lnedts):
+ if self.fields[index].name.lower() == "to":
+ if bool(self.values_old):
+ if str(lnedt.text()) not in self.values_old:
+ if str(lnedt.text()) in self.fromBeacons:
+ lnedt.setStyleSheet(self.colours["UNIQUE"])
+ valid = False
+ break
+ elif not bool(self.values_old):
+ if str(lnedt.text()) in self.fromBeacons:
+ lnedt.setStyleSheet(self.colours["UNIQUE"])
+ valid = False
+ break
+ if bool(int(self.db.query(self.query %('beacon', "%s"), (str(lnedt.text()),))[0][0])):
+ lnedt.setStyleSheet(self.colours["UNIQUE"])
valid = False
+ break
if not valid:
- QMessageBox.information(self, "Invalid Reference", "Please ensure that specified beacons is valid.")
+ QMessageBox.information(
+ self,
+ "Invalid Reference",
+ "Please ensure that the new beacon is unique."
+ )
return
# save values
self.values_new = values_new
@@ -1376,22 +1658,27 @@ def setupUi(self, fromBeacons):
self.lbls = []
self.lnedts = []
self.fields = [
- {"NAME":"Bearing", "TYPE":float, "NULLABLE":False},
- {"NAME":"Distance", "TYPE":float, "NULLABLE":False},
- {"NAME":"From", "TYPE":str, "NULLABLE":False},
- {"NAME":"To", "TYPE":str, "NULLABLE":False},
- {"NAME":"Location", "TYPE":str, "NULLABLE":True},
- {"NAME":"Surveyor", "TYPE":str, "NULLABLE":True}
+ Field("Bearing", float, True, False),
+ Field("Distance", float, True, False),
+ Field("From", str, True, False),
+ Field("To", str, True, False),
+ Field("Location", str, False, False),
+ Field("Surveyor", str, False, False)
]
- for index, field in enumerate(self.fields):
+ for index, f in enumerate(self.fields):
lbl = QLabel(self)
self.formLayout.setWidget(index, QFormLayout.LabelRole, lbl)
- lbl.setText(QApplication.translate("dlg_FormBearDistEntry", field["NAME"].title(), None, QApplication.UnicodeUTF8))
+ lbl.setText(QApplication.translate(
+ "dlg_FormBearDistEntry",
+ ("*" if f.required else "") + f.name.title(),
+ None,
+ QApplication.UnicodeUTF8
+ ))
self.lbls.append(lbl)
lnedt = QLineEdit(self)
self.formLayout.setWidget(index, QFormLayout.FieldRole, lnedt)
self.lnedts.append(lnedt)
- if field["NAME"].lower() == "from":
+ if f.name.lower() == "from":
model = QStringListModel()
model.setStringList(fromBeacons)
completer = QCompleter()
@@ -1404,7 +1691,12 @@ def setupUi(self, fromBeacons):
self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Save)
self.gridLayout.addWidget(self.btnbx_options, 1, 0, 1, 1)
# translate ui widgets' text
- self.setWindowTitle(QApplication.translate("dlg_FormBearDistEntry", "Link Form", None, QApplication.UnicodeUTF8))
+ self.setWindowTitle(QApplication.translate(
+ "dlg_FormBearDistEntry",
+ "Link Form",
+ None,
+ QApplication.UnicodeUTF8
+ ))
# connect ui widgets
self.btnbx_options.clicked.connect(self.executeOption)
QMetaObject.connectSlotsByName(self)
diff --git a/__init__.py b/__init__.py
index befd9ea..1e70051 100644
--- a/__init__.py
+++ b/__init__.py
@@ -25,21 +25,17 @@
def name():
return "SML Surveyor"
-
def description():
return "SML Surveyor Plugin"
-
def version():
return "Version 0.1"
-
def icon():
return "icon.png"
-
def qgisMinimumVersion():
- return "1.8"
+ return "2.0"
def author():
return "AfriSpatial"
@@ -49,5 +45,5 @@ def email():
def classFactory(iface):
# load sml_surveyor class from file sml_surveyor
- from sml_surveyor import sml_surveyor
+ from plugin import sml_surveyor
return sml_surveyor(iface)
diff --git a/constants.py b/constants.py
new file mode 100644
index 0000000..2f19fc5
--- /dev/null
+++ b/constants.py
@@ -0,0 +1,52 @@
+SQL_BEACONS = {
+ "SELECT":"SELECT beacon FROM beacons WHERE gid = %s;",
+ "UNIQUE":"SELECT COUNT(*) FROM beacons WHERE %s = %s;",
+ "EDIT":"SELECT {fields} FROM beacons WHERE gid = %s;",
+ "DELETE":"DELETE FROM beacons WHERE gid = %s;",
+ "INSERT":"INSERT INTO beacons({fields}) VALUES ({values}) RETURNING gid;",
+ "UPDATE":"UPDATE beacons SET {set} WHERE {where};",
+ "BEARDIST":"SELECT CASE WHEN count(*) = 0 THEN FALSE ELSE TRUE END \
+ FROM beardist WHERE beacon_to = (SELECT beacon FROM \
+ beacons WHERE gid = %s);",
+}
+
+SQL_PARCELS = {
+ "SELECT":"SELECT parcel_id FROM parcel_lookup WHERE parcel_id = %s;",
+ "EDIT":"SELECT l.parcel_id, array_agg(s.gid ORDER BY s.sequence) \
+ FROM ( SELECT b.gid, d.parcel_id, d.sequence FROM beacons b \
+ INNER JOIN parcel_def d ON d.beacon = b.beacon) s JOIN \
+ parcel_lookup l ON s.parcel_id = l.parcel_id WHERE \
+ l.parcel_id = %s GROUP BY l.parcel_id;",
+ "AUTOCOMPLETE":"SELECT parcel_id FROM parcel_lookup WHERE available;",
+ "UNIQUE":"SELECT COUNT(*) FROM parcel_lookup WHERE parcel_id = %s;",
+ "AVAILABLE":"SELECT available FROM parcel_lookup WHERE parcel_id = %s;",
+ "INSERT":"INSERT INTO parcel_def(parcel_id, beacon, sequence) \
+ VALUES (%s, %s, %s);",
+ "DELETE":"DELETE FROM parcel_def WHERE parcel_id = %s;",
+}
+
+SQL_BEARDIST = {
+ "AUTO_SURVEYPLAN":"SELECT array_agg(plan_no) FROM survey;",
+ "AUTO_REFERENCEBEACON":"SELECT array_agg(beacon) FROM beacons \
+ WHERE beacon NOT IN (SELECT beacon_to FROM beardist WHERE \
+ beacon_to NOT IN (SELECT ref_beacon FROM survey));",
+ "EXIST_REFERENCEBEACON":"SELECT ref_beacon FROM survey where \
+ plan_no = %s;",
+ "EXIST_BEARDISTCHAINS":"SELECT bd.bearing, bd.distance, \
+ bd.beacon_from, bd.beacon_to, b.location, b.name FROM beardist \
+ bd INNER JOIN beacons b ON bd.beacon_to = b.beacon WHERE \
+ bd.plan_no = %s;",
+ "INDEX_REFERENCEBEACON":"SELECT i.column_index::integer FROM (SELECT \
+ row_number() over(ORDER BY c.ordinal_position) -1 as \
+ column_index, c.column_name FROM information_schema.columns c \
+ WHERE c.table_name = 'beacons' AND c.column_name NOT IN ('geom', \
+ 'gid') ORDER BY c.ordinal_position) as i WHERE i.column_name = \
+ 'beacon';",
+ "IS_SURVEYPLAN":"SELECT CASE WHEN COUNT(*) <> 0 THEN TRUE ELSE FALSE \
+ END FROM survey WHERE plan_no = %s;",
+ "INSERT_SURVEYPLAN":"INSERT INTO survey(plan_no, ref_beacon) \
+ VALUES(%s, %s);",
+ "UPDATE_LINK":"SELECT beardistupdate(%s, %s, %s, %s, %s, %s, %s, %s);",
+ "DELETE_LINK":"DELETE FROM beacons WHERE beacon = %s;",
+ "INSERT_LINK":"SELECT beardistinsert(%s, %s, %s, %s, %s, %s, %s);"
+}
diff --git a/database.py b/database.py
index 89bf644..f4c4846 100644
--- a/database.py
+++ b/database.py
@@ -15,10 +15,19 @@
* *
***************************************************************************/
"""
-
import psycopg2
-class manager():
+
+class Field:
+
+ def __init__(self, name, type, required, unique):
+ self.name = name
+ self.type = type
+ self.required = required
+ self.unique = unique
+
+
+class Manager:
def __init__(self, params):
# test db settings
@@ -91,8 +100,12 @@ def getSchema(self, tbl_name, fld_ignore):
""" Get information abot a specific table
@returns [, , ] (list)
"""
- info = [{"NAME":data[0], "TYPE":self._pythonize_type(data[1]), "REQUIRED":data[2], "UNIQUE":data[3]} for data in reversed(self.query("SELECT c.column_name, c.data_type, CASE WHEN c.is_nullable = 'NO' THEN TRUE ELSE FALSE END AS required, CASE WHEN u.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS unique FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name, tc.table_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') AND tc.table_name = '{table}') u ON u.column_name = c.column_name WHERE c.table_name = '{table}' AND c.column_name NOT IN ({ignore});".format(table = tbl_name, ignore = ", ".join("'%s'" %(i,) for i in fld_ignore))))]
- return info
+ return [Field(
+ data[0],
+ self._pythonize_type(data[1]),
+ data[2],
+ data[3]
+ ) for data in reversed(self.query("SELECT c.column_name, c.data_type, CASE WHEN c.is_nullable = 'NO' THEN TRUE ELSE FALSE END AS required, CASE WHEN u.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS unique FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name, tc.table_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') AND tc.table_name = '{table}') u ON u.column_name = c.column_name WHERE c.table_name = '{table}' AND c.column_name NOT IN ({ignore});".format(table = tbl_name, ignore = ", ".join("'%s'" %(i,) for i in fld_ignore))))]
def _pythonize_type(self, db_type):
""" Get python type
diff --git a/metadata.txt b/metadata.txt
index ff58f72..b318a06 100644
--- a/metadata.txt
+++ b/metadata.txt
@@ -1,38 +1,13 @@
-# This file contains metadata for your plugin. Beginning
-# with version 1.8 this is the preferred way to supply information about a
-# plugin. The current method of embedding metadata in __init__.py will
-# be supported until version 2.0
-
-# This file should be included when you package your plugin.
-
-# Mandatory items:
-
+; Mandatory items:
[general]
name=SML Surveyor Plugin
-qgisMinimumVersion=1.8
description=SML Surveyor Plugin
-version=0.1
+qgisMinimumVersion=2.0
+version=Version 0.1
author=AfriSpatial
email=robert@afrispatial.co.za
-# end of mandatory metadata
-
-# Optional items:
-
-# Uncomment the following line and add your changelog entries:
-# changelog=
-
-# tags are comma separated with spaces allowed
-tags=survey
-
-homepage=
-tracker=
-repository=
-icon=icon.png
-# experimental flag
+; Optional items:
experimental=False
-
-# deprecated flag (applies to the whole plugin, not just a single version
-deprecated=False
-
+icon=icon.png
diff --git a/plugin.py b/plugin.py
new file mode 100644
index 0000000..ea566d7
--- /dev/null
+++ b/plugin.py
@@ -0,0 +1,615 @@
+# -*- coding: utf-8 -*-
+"""
+/***************************************************************************
+ sml_surveyor
+ A QGIS plugin
+ SML Surveyor Plugin
+ -------------------
+ begin : 2012-12-28
+ modified last : 2014-01-07
+ copyright : (C) 2012 by AfriSpatial
+ email : robert@afrispatial.co.za
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+"""
+
+# qgis imports
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from qgis.core import *
+# python imports
+import os
+# user imports
+import __init__ as metadata
+from PyQt4Dialogs import *
+import database
+from constants import *
+from datetime import datetime
+
+class RequiredLayer:
+
+ def __init__(self,
+ name,
+ name_plural,
+ table,
+ primary_key,
+ geometry_type,
+ geometry_column='the_geom',
+ schema='public',
+ layer=None
+ ):
+ self.name = name
+ self.name_plural = name_plural
+ self.table = table
+ self.primary_key = primary_key
+ self.geometry_type = geometry_type
+ self.geometry_column = geometry_column
+ self.schema = schema
+ self.layer = layer
+
+
+class Mode:
+
+ def __init__(self, actor, action):
+ self.actor = actor
+ self.action = action
+
+
+class sml_surveyor:
+
+ def __init__(self, iface):
+ # save reference to the QGIS interface
+ self.iface = iface
+ # get plugin directory
+ self.plugin_dir = os.path.dirname(os.path.realpath(__file__))
+ self.uri = None
+ self.db = None
+ self.datetime = datetime.now()
+ self.requiredLayers = []
+ # 1. beacons
+ # 2. parcels
+ self.requiredLayers.append(RequiredLayer(
+ 'Beacon', 'Beacons', 'beacons', 'gid', 'points'
+ ))
+ self.requiredLayers.append(RequiredLayer(
+ 'Parcel', 'Parcels', 'parcels', 'parcel_id', 'polygons'
+ ))
+
+
+ def initGui(self):
+ """ Initialize gui
+ """
+ # create plugin toolbar
+ self.createPluginToolBar()
+
+
+ def unload(self):
+ """ Uninitialize gui
+ """
+ # remove plugin toolbar
+ self.removePluginToolBar()
+ # remove layers
+ self.refreshLayers()
+ for l in self.requiredLayers:
+ if bool(l.layer):
+ QgsMapLayerRegistry.instance().removeMapLayers([l.layer.id(),])
+
+
+ def createPluginToolBar(self):
+ """ Create plugin toolbar to house buttons
+ """
+ # create plugin toolbar
+ self.pluginToolBar = QToolBar(metadata.name())
+ self.pluginToolBar.setObjectName(metadata.name())
+ # create Beardist button
+ self.actionBearDist = QAction(
+ QIcon(os.path.join(self.plugin_dir, "images", "beardist.png")),
+ "Manage Bearings and Distances",
+ self.iface.mainWindow()
+ )
+ self.actionBearDist.setWhatsThis("Manage bearings and distances")
+ self.actionBearDist.setStatusTip("Manage bearings and distances")
+ self.actionBearDist.triggered.connect(self.manageBearDist)
+ # create Beacons button
+ self.actionBeacons = QAction(
+ QIcon(os.path.join(self.plugin_dir, "images", "beacon.gif")),
+ "Manage Beacons",
+ self.iface.mainWindow()
+ )
+ self.actionBeacons.setWhatsThis("Manage beacons")
+ self.actionBeacons.setStatusTip("Manage beacons")
+ self.actionBeacons.triggered.connect(self.manageBeacons)
+ # create Parcels button
+ self.actionParcels = QAction(
+ QIcon(os.path.join(self.plugin_dir, "images", "parcel.png")),
+ "Manage Parcels",
+ self.iface.mainWindow()
+ )
+ self.actionParcels.setWhatsThis("Manage parcels")
+ self.actionParcels.setStatusTip("Manage parcels")
+ self.actionParcels.triggered.connect(self.manageParcels)
+ # populate plugin toolbar
+ self.pluginToolBar.addAction(self.actionBearDist)
+ self.pluginToolBar.addAction(self.actionBeacons)
+ self.pluginToolBar.addAction(self.actionParcels)
+ # add plugin toolbar to gui
+ self.iface.mainWindow().addToolBar(self.pluginToolBar)
+
+
+ def removePluginToolBar(self):
+ """ Remove plugin toolbar which houses buttons
+ """
+ # remove app toolbar from gui
+ if hasattr(self,"pluginToolBar"):
+ self.iface.mainWindow().removeToolBar(self.pluginToolBar)
+ self.pluginToolBar.hide()
+
+
+ def setDatabaseConnection(self):
+ """ Create a database connection
+ """
+ # fetch settings
+ settings_plugin = QSettings()
+ settings_postgis = QSettings()
+ settings_plugin.beginGroup(metadata.name().replace(" ","_"))
+ settings_postgis.beginGroup('PostgreSQL/connections')
+ # fetch pre-chosen database connection
+ conn = settings_plugin.value("DatabaseConnection", None)
+ # check if still exists
+ if bool(conn):
+ if conn not in settings_postgis.childGroups():
+ settings_plugin.setValue("DatabaseConnection", "")
+ conn = None
+ # fetch from user if necessary
+ if not bool(conn):
+ dlg = dlg_DatabaseConnection()
+ dlg.show()
+ if bool(dlg.exec_()):
+ conn = dlg.getDatabaseConnection()
+ settings_plugin.setValue("DatabaseConnection", conn)
+ # validate database connection
+ if bool(conn):
+ db_host = settings_postgis.value(conn+'/host')
+ db_port = settings_postgis.value(conn+'/port')
+ db_name = settings_postgis.value(conn+'/database')
+ db_username = ''
+ db_password = ''
+ self.uri = QgsDataSourceURI()
+ self.uri.setConnection(
+ db_host,
+ db_port,
+ db_name,
+ db_username,
+ db_password,
+ )
+ max_attempts = 3
+ msg = "Please enter the username and password."
+ for i in range(max_attempts):
+ ok, db_username, db_password = QgsCredentials.instance().get(
+ self.uri.connectionInfo(),
+ db_username,
+ db_password,
+ msg
+ )
+ if not ok: break
+ db_username.replace(" ", "")
+ db_password.replace(" ", "")
+ try:
+ self.db = database.Manager({
+ "HOST":db_host,
+ "NAME":db_name,
+ "PORT":db_port,
+ "USER":db_username,
+ "PASSWORD":db_password
+ })
+ self.uri.setConnection(
+ db_host,
+ db_port,
+ db_name,
+ db_username,
+ db_password,
+ )
+ self.datetime = datetime.now()
+ break
+ except Exception as e:
+ msg = "Invalid username and password."
+ settings_plugin.endGroup()
+ settings_postgis.endGroup()
+
+
+ def refreshLayers(self):
+ """ Ensure all required layers exist
+ """
+ if bool(self.db):
+ for l in reversed(self.requiredLayers):
+ for layer in self.iface.legendInterface().layers():
+ if l.name_plural.lower() == layer.name().lower():
+ l.layer = layer
+ break
+ if not bool(l.layer):
+ self.uri.setDataSource(
+ l.schema,
+ l.table,
+ l.geometry_column,
+ '',
+ l.primary_key
+ )
+ self.iface.addVectorLayer(
+ self.uri.uri(),
+ l.name_plural,
+ "postgres"
+ )
+ for layer in self.iface.legendInterface().layers():
+ if l.name_plural == layer.name(): l.layer = layer
+
+
+ def manageBeacons(self):
+ """ Portal which enables the management of beacons
+ """
+ if self.datetime.date() != datetime.now().date(): self.db = None
+ if self.db is None:
+ self.setDatabaseConnection()
+ if self.db is None: return
+ self.refreshLayers()
+ BeaconManager(self.iface, self.db, self.requiredLayers)
+ self.iface.mapCanvas().refresh()
+
+
+ def manageParcels(self):
+ """ Portal which enables the management of parcels
+ """
+ if self.datetime.date() != datetime.now().date(): self.db = None
+ if self.db is None:
+ self.setDatabaseConnection()
+ if self.db is None: return
+ self.refreshLayers()
+ ParcelManager(self.iface, self.db, self.requiredLayers)
+ self.iface.mapCanvas().refresh()
+
+
+ def manageBearDist(self):
+ """ Portal which enables the management of
+ bearings and distances
+ """
+ if self.datetime.date() != datetime.now().date(): self.db = None
+ if self.db is None:
+ self.setDatabaseConnection()
+ if self.db is None: return
+ self.refreshLayers()
+ BearDistManager(self.iface, self.db, self.requiredLayers)
+ self.iface.mapCanvas().refresh()
+
+
+class BeaconManager():
+
+ def __init__(self, iface, db, requiredLayers):
+ self.iface = iface
+ self.db = db
+ self.requiredLayers = requiredLayers
+ self.run()
+
+
+ def run(self):
+ """ Main method
+ """
+ # display manager dialog
+ mng = dlg_Manager(self.requiredLayers[0])
+ mng.show()
+ mng_ret = mng.exec_()
+ if bool(mng_ret):
+
+ if mng.getOption() == 0: # create new beacon
+ while True:
+ # get fields
+ fields = self.db.getSchema(
+ self.requiredLayers[0].table, [
+ self.requiredLayers[0].geometry_column,
+ self.requiredLayers[0].primary_key
+ ])
+ # display form
+ frm = dlg_FormBeacon(
+ self.db,
+ SQL_BEACONS["UNIQUE"],
+ fields
+ )
+ frm.show()
+ frm_ret = frm.exec_()
+ if bool(frm_ret):
+ # add beacon to database
+ values_old, values_new = frm.getValues()
+ self.db.query(
+ SQL_BEACONS["INSERT"].format(fields = ", ".join(sorted(values_new.keys())), values = ", ".join(["%s" for k in values_new.keys()])), [values_new[k] for k in sorted(values_new.keys())])
+ self.iface.mapCanvas().refresh()
+ else: break
+
+ elif mng.getOption() == 1: # edit existing beacon
+ # select beacon
+ mode = Mode("EDITOR","EDIT")
+ query = SQL_BEACONS["SELECT"]
+ slc = dlg_Selector(
+ self.db,
+ self.iface,
+ self.requiredLayers[0],
+ mode,
+ query,
+ preserve = True
+ )
+ slc.show()
+ slc_ret = slc.exec_()
+ self.iface.mapCanvas().setMapTool(slc.tool)
+ if bool(slc_ret):
+ featID = slc.getFeatureId()
+ # check if defined by a bearing and distance
+ if self.db.query(SQL_BEACONS["BEARDIST"], (featID,))[0][0]:
+ QMessageBox.warning(
+ None,
+ "Bearing and Distance Definition",
+ "Cannot edit beacon defined by distance and bearing via this tool"
+ )
+ for l in self.requiredLayers: l.layer.removeSelection()
+ return
+ # get fields
+ fields = self.db.getSchema(
+ self.requiredLayers[0].table, [
+ self.requiredLayers[0].geometry_column,
+ self.requiredLayers[0].primary_key
+ ])
+ # get values
+ values = [v for v in self.db.query(SQL_BEACONS["EDIT"].format(fields = ",".join([f.name for f in fields])), (featID,))[0]]
+ # display form
+ frm = dlg_FormBeacon(
+ self.db,
+ SQL_BEACONS["UNIQUE"],
+ fields,
+ values
+ )
+ frm.show()
+ frm_ret = frm.exec_()
+ if bool(frm_ret):
+ # edit beacon in database
+ fields_old = []
+ fields_new = []
+ values_old = []
+ values_new = []
+ for f in fields:
+ if frm.getValues()[0][f.name] is not None:
+ fields_old.append(f.name)
+ values_old.append(frm.getValues()[0][f.name])
+ fields_new.append(f.name)
+ values_new.append(frm.getValues()[1][f.name])
+ set = ", ".join(["{field} = %s".format(field = f) for f in fields_new])
+ where = " AND ".join(["{field} = %s".format(field = f) for f in fields_old])
+ self.db.query(
+ SQL_BEACONS["UPDATE"].format(
+ set = set,
+ where = where
+ ), values_new + values_old
+ )
+ for l in self.requiredLayers: l.layer.removeSelection()
+
+ elif mng.getOption() == 2: # delete existing beacon
+ # select beacon
+ mode = Mode("REMOVER","REMOVE")
+ query = SQL_BEACONS["SELECT"]
+ slc = dlg_Selector(
+ self.db,
+ self.iface,
+ self.requiredLayers[0],
+ mode,
+ query,
+ preserve = True
+ )
+ slc.show()
+ slc_ret = slc.exec_()
+ self.iface.mapCanvas().setMapTool(slc.tool)
+ if bool(slc_ret):
+ featID = slc.getFeatureId()
+ # check if defined by a bearing and distance
+ if self.db.query(SQL_BEACONS["BEARDIST"], (featID,))[0][0]:
+ QMessageBox.warning(None, "Bearing and Distance Definition", "Cannot delete beacon defined by distance and bearing via this tool")
+ for l in self.requiredLayers: l.layer.removeSelection()
+ return
+ # delete beacon from database
+ self.db.query(SQL_BEACONS["DELETE"], (featID,))
+ for l in self.requiredLayers: l.layer.removeSelection()
+
+
+class ParcelManager():
+
+ def __init__(self, iface, db, requiredLayers):
+ self.iface = iface
+ self.db = db
+ self.requiredLayers = requiredLayers
+ self.run()
+
+
+ def run(self):
+ """ Main method
+ """
+ # display manager dialog
+ mng = dlg_Manager(self.requiredLayers[1])
+ mng.show()
+ mng_ret = mng.exec_()
+ if bool(mng_ret):
+
+ if mng.getOption() == 0: # create new parcel
+ while True:
+ # show parcel form
+ autocomplete = [str(i[0]) for i in self.db.query(
+ SQL_PARCELS["AUTOCOMPLETE"]
+ )]
+ frm = dlg_FormParcel(
+ self.db,
+ self.iface,
+ self.requiredLayers,
+ SQL_BEACONS,
+ SQL_PARCELS,
+ autocomplete
+ )
+ frm.show()
+ frm_ret = frm.exec_()
+ self.iface.mapCanvas().setMapTool(frm.tool)
+ if bool(frm_ret):
+ # add parcel to database
+ sql = ""
+ for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
+ sql += self.db.queryPreview(
+ SQL_PARCELS["INSERT"],
+ (frm.getValues()[1]["parcel_id"], beacon, i)
+ )
+ self.db.query(sql)
+ self.iface.mapCanvas().refresh()
+ else:
+ break
+ for l in self.requiredLayers: l.layer.removeSelection()
+
+ elif mng.getOption() == 1: # edit existing parcel
+ # select parcel
+ mode = Mode("EDITOR","EDIT")
+ query = SQL_PARCELS["SELECT"]
+ slc = dlg_Selector(
+ self.db,
+ self.iface,
+ self.requiredLayers[1],
+ mode,
+ query,
+ preserve = True
+ )
+ slc.show()
+ slc_ret = slc.exec_()
+ self.iface.mapCanvas().setMapTool(slc.tool)
+ if bool(slc_ret):
+ # show parcel form
+ autocomplete = [str(i[0]) for i in self.db.query(
+ SQL_PARCELS["AUTOCOMPLETE"]
+ )]
+ data = (lambda t: {"parcel_id":t[0], "sequence":t[1]})(
+ self.db.query(
+ SQL_PARCELS["EDIT"], (slc.getFeatureId(),)
+ )[0]
+ )
+ frm = dlg_FormParcel(
+ self.db,
+ self.iface,
+ self.requiredLayers,
+ SQL_BEACONS,
+ SQL_PARCELS,
+ autocomplete,
+ data
+ )
+ frm.show()
+ frm_ret = frm.exec_()
+ self.iface.mapCanvas().setMapTool(frm.tool)
+ if bool(frm_ret):
+ # edit parcel in database
+ self.db.query(
+ SQL_PARCELS["DELETE"],
+ (frm.getValues()[0]["parcel_id"],)
+ )
+ sql = ""
+ for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
+ sql += self.db.queryPreview(
+ SQL_PARCELS["INSERT"],
+ (frm.getValues()[1]["parcel_id"], beacon, i)
+ )
+ self.db.query(sql)
+ for l in self.requiredLayers: l.layer.removeSelection()
+
+ elif mng.getOption() == 2: # delete existing parcel
+ # select parcel
+ mode = Mode("REMOVER","REMOVE")
+ query = SQL_PARCELS["SELECT"]
+ slc = dlg_Selector(
+ self.db,
+ self.iface,
+ self.requiredLayers[1],
+ mode,
+ query,
+ preserve = True
+ )
+ slc.show()
+ slc_ret = slc.exec_()
+ self.iface.mapCanvas().setMapTool(slc.tool)
+ if bool(slc_ret):
+ # delete parcel from database
+ featID = slc.getFeatureId()
+ self.db.query(SQL_PARCELS["DELETE"], (self.db.query(
+ SQL_PARCELS["SELECT"], (featID,)
+ )[0][0],))
+ for l in self.requiredLayers: l.layer.removeSelection()
+
+
+class BearDistManager():
+
+ def __init__(self, iface, db, requiredLayers):
+ self.iface = iface
+ self.db = db
+ self.requiredLayers = requiredLayers
+ self.run()
+
+ def run(self):
+ """ Main method
+ """
+ dlg = dlg_FormBearDist(
+ self.db,
+ SQL_BEARDIST,
+ SQL_BEACONS,
+ self.requiredLayers
+ )
+ dlg.show()
+ dlg_ret = dlg.exec_()
+ if bool(dlg_ret):
+ surveyPlan, referenceBeacon, beardistChain = dlg.getReturn()
+ # check whether survey plan is defined otherwise define it
+ if not self.db.query(
+ SQL_BEARDIST["IS_SURVEYPLAN"],
+ (surveyPlan,)
+ )[0][0]:
+ self.db.query(
+ SQL_BEARDIST["INSERT_SURVEYPLAN"],
+ (surveyPlan, referenceBeacon)
+ )
+ # get list of existing links
+ beardistChainExisting = []
+ for index, link in enumerate(self.db.query(SQL_BEARDIST["EXIST_BEARDISTCHAINS"],(surveyPlan,))):
+ beardistChainExisting.append([list(link), "NULL", index])
+ # perform appropriate action for each link in the beardist chain
+ new = []
+ old = []
+ for link in beardistChain:
+ if link[2] is None: new.append(link)
+ else: old.append(link)
+ # sort out old links
+ tmp = list(beardistChainExisting)
+ for elink in beardistChainExisting:
+ for olink in old:
+ if elink[2] == olink[2]:
+ if olink[1] == "NULL":
+ tmp.remove(elink)
+ break;
+ self.db.query(
+ SQL_BEARDIST["UPDATE_LINK"],
+ [surveyPlan] + olink[0] + [olink[2]]
+ )
+ tmp.remove(elink)
+ break;
+ beardistChainExisting = tmp
+ for elink in beardistChainExisting:
+ self.db.query(
+ SQL_BEARDIST["DELETE_LINK"],
+ (elink[0][3],)
+ )
+ # sort out new links
+ for nlink in new:
+ self.db.query(
+ SQL_BEARDIST["INSERT_LINK"],
+ [surveyPlan] + nlink[0]
+ )
diff --git a/portqgis2_db_changes.sql b/portqgis2_db_changes.sql
new file mode 100644
index 0000000..488b5a4
--- /dev/null
+++ b/portqgis2_db_changes.sql
@@ -0,0 +1,41 @@
+CREATE OR REPLACE FUNCTION beardistupdate(arg_plan_no character varying, arg_bearing double precision, arg_distance double precision, arg_beacon_from character varying, arg_beacon_to character varying, arg_location character varying, arg_name character varying, arg_index integer)
+ RETURNS void AS
+$BODY$
+ DECLARE
+ the_id_beardist integer;
+ the_id_beacons integer;
+ the_x double precision;
+ the_y double precision;
+ the_geom_ geometry(Point, 26331);
+ BEGIN
+ SELECT i.id INTO the_id_beardist FROM (
+ SELECT bd.id, row_number() over(ORDER BY bd.id) -1 as index
+ FROM beardist bd
+ INNER JOIN beacons b ON bd.beacon_to = b.beacon
+ WHERE bd.plan_no = arg_plan_no
+ ) AS i
+ WHERE i.index = arg_index;
+ SELECT gid INTO the_id_beacons FROM beacons b INNER JOIN beardist bd ON b.beacon = bd.beacon_to WHERE bd.id = the_id_beardist;
+ SELECT x INTO the_x FROM beacons WHERE beacon = arg_beacon_from;
+ SELECT y INTO the_y FROM beacons WHERE beacon = arg_beacon_from;
+ SELECT pointfrombearinganddistance(the_x, the_y, arg_bearing, arg_distance, 3, 26331) INTO the_geom_;
+ UPDATE beacons SET
+ beacon = arg_beacon_to,
+ y = st_y(the_geom_),
+ x = st_x(the_geom_),
+ "location" = arg_location,
+ "name" = arg_name
+ WHERE gid = the_id_beacons;
+ UPDATE beardist SET
+ plan_no = arg_plan_no,
+ bearing = arg_bearing,
+ distance = arg_distance,
+ beacon_from = arg_beacon_from,
+ beacon_to = arg_beacon_to
+ WHERE id = the_id_beardist;
+ END
+$BODY$
+ LANGUAGE plpgsql VOLATILE
+ COST 100;
+ALTER FUNCTION beardistupdate(character varying, double precision, double precision, character varying, character varying, character varying, character varying, integer)
+ OWNER TO robert;
\ No newline at end of file
diff --git a/qgisToolbox.py b/qgisToolbox.py
index bf19f6a..c44437a 100644
--- a/qgisToolbox.py
+++ b/qgisToolbox.py
@@ -25,7 +25,7 @@ class featureSelector():
""" This tool enables the selection of a single feature or multiple features from a vector layer, returning the feature's id or features' ids after each selection via the captured method in the invoking class
"""
- def __init__(self, iface, layer, capturing = True, parent = None):
+ def __init__(self, iface, layer, capturing=True, parent=None):
# initialize instance valiables
self.parent = parent # assume parent has a captured method
self.iface = iface
@@ -64,11 +64,9 @@ def appendSelection(self, id):
""" Append a feature to the list of currently selected features
"""
# toggle selection
- if id in self.selected: del self.selected[self.selected.index(id)]
- else: self.selected.append(id)
- self.layer.setSelectedFeatures(self.selected)
+ self.selected.append(id)
# notify parent of changed selection
- if self.parent is not None: self.parent.captured(self.selected)
+ self.parent.captured(self.selected)
def capture(self, point, button):
""" Capture id of feature selected by the selector tool
@@ -76,10 +74,21 @@ def capture(self, point, button):
# check that capturing has been enabled
if self.capturing:
pnt_geom = QgsGeometry.fromPoint(point)
- pnt_buffer = pnt_geom.buffer((self.iface.mapCanvas().mapUnitsPerPixel()*5),0)
+ pnt_buffer = pnt_geom.buffer((self.iface.mapCanvas().mapUnitsPerPixel()*4),0)
pnt_rect = pnt_buffer.boundingBox()
- self.layer.select([], pnt_rect, True, True)
- feat = QgsFeature()
- while self.layer.nextFeature(feat):
- self.appendSelection(feat.id())
- break
+ self.layer.invertSelectionInRectangle(pnt_rect)
+ if bool(self.layer.selectedFeaturesIds()):
+ for id in self.layer.selectedFeaturesIds():
+ if id not in self.selected:
+ self.selected.append(id)
+ selected = self.selected
+ for id in selected:
+ if id not in self.layer.selectedFeaturesIds():
+ self.selected.remove(id)
+ self.parent.captured(self.selected)
+
+ #self.layer.select([], pnt_rect, True, True)
+ #feat = QgsFeature()
+ #while self.layer.nextFeature(feat):
+ # self.appendSelection(feat.id())
+ # break
diff --git a/settings.py b/settings.py
deleted file mode 100644
index a051a28..0000000
--- a/settings.py
+++ /dev/null
@@ -1,79 +0,0 @@
-"""
-File: settings.py
-Description: stores local settings used by the qgis python plugin
-Author: Robert Moerman
-Contact: robert@afrispatial.co.za
-Company: AfriSpatial
-"""
-
-import database_params
-
-# Database settings
-DATABASE_HOST = database_params.DATABASE_PARAMS["HOST"]
-DATABASE_PORT = database_params.DATABASE_PARAMS["PORT"]
-DATABASE_NAME = database_params.DATABASE_PARAMS["NAME"]
-DATABASE_USER = database_params.DATABASE_PARAMS["USER"]
-DATABASE_PASSWORD = database_params.DATABASE_PARAMS["PASSWORD"]
-DATABASE_PARAMS = database_params.DATABASE_PARAMS
-DATABASE_SCHEMA = "public"
-DATABASE_LAYERS = {}
-
-# Define database layers
-DATABASE_LAYERS["BEACONS"] = {
- "SCHEMA":"public",
- "TABLE":"beacons",
- "NAME":"Beacon",
- "NAME_PLURAL":"Beacons",
- "PKEY":"gid",
- "GEOM":"the_geom",
- "GEOM_TYPE":"points",
- "SQL":{
- "SELECT":"SELECT beacon FROM beacons WHERE gid = %s;",
- "UNIQUE":"SELECT COUNT(*) FROM beacons WHERE %s = %s;",
- "EDIT":"SELECT {fields} FROM beacons WHERE gid = %s;",
- "DELETE":"DELETE FROM beacons WHERE gid = %s;",
- "INSERT":"INSERT INTO beacons({fields}) VALUES ({values}) RETURNING gid;",
- "UPDATE":"UPDATE beacons SET {set} WHERE {where};",
- "BEARDIST":"SELECT CASE WHEN count(*) = 0 THEN FALSE ELSE TRUE END FROM beardist WHERE beacon_to = (SELECT beacon FROM beacons WHERE gid = %s);"
- }
-}
-DATABASE_LAYERS["PARCELS"] = {
- "SCHEMA":"public",
- "TABLE":"parcels",
- "NAME":"Parcel",
- "NAME_PLURAL":"Parcels",
- "PKEY":"parcel_id",
- "GEOM":"the_geom",
- "GEOM_TYPE":"polygons",
- "SQL":{
- "SELECT":"SELECT parcel_id FROM parcel_lookup WHERE parcel_id = %s;",
- "EDIT":"SELECT l.parcel_id, array_agg(s.gid ORDER BY s.sequence) FROM ( SELECT b.gid, d.parcel_id, d.sequence FROM beacons b INNER JOIN parcel_def d ON d.beacon = b.beacon) s JOIN parcel_lookup l ON s.parcel_id = l.parcel_id WHERE l.parcel_id = %s GROUP BY l.parcel_id;",
- "AUTOCOMPLETE":"SELECT parcel_id FROM parcel_lookup WHERE available;",
- "UNIQUE":"SELECT COUNT(*) FROM parcel_lookup WHERE parcel_id = %s;",
- "AVAILABLE":"SELECT available FROM parcel_lookup WHERE parcel_id = %s;",
- "INSERT":"INSERT INTO parcel_def(parcel_id, beacon, sequence) VALUES (%s, %s, %s);",
- "DELETE":"DELETE FROM parcel_def WHERE parcel_id = %s;",
- }
-}
-
-# DB Triggers:
-# - auto create parcel id in parcel_lookup if it does not exist on
-# insert/update on parcel_def
-# - auto update available field in parcel_lookup on insert/update/delete on
-# parcel_def
-
-DATABASE_LAYERS_ORDER = ["BEACONS", "PARCELS"]
-
-# Define other sql commands
-DATABASE_OTHER_SQL = {
- "AUTO_SURVEYPLAN":"SELECT array_agg(plan_no) FROM survey;",
- "AUTO_REFERENCEBEACON":"SELECT array_agg(beacon) FROM beacons WHERE beacon NOT IN (SELECT beacon_to FROM beardist WHERE beacon_to NOT IN (SELECT ref_beacon FROM survey));",
- "EXIST_REFERENCEBEACON":"SELECT ref_beacon FROM survey where plan_no = %s;",
- "EXIST_BEARDISTCHAINS":"SELECT bd.bearing, bd.distance, bd.beacon_from, bd.beacon_to, b.location, b.name FROM beardist bd INNER JOIN beacons b ON bd.beacon_to = b.beacon WHERE bd.plan_no = %s;",
- "INDEX_REFERENCEBEACON":"SELECT i.column_index::integer FROM (SELECT row_number() over(ORDER BY c.ordinal_position) -1 as column_index, c.column_name FROM information_schema.columns c WHERE c.table_name = 'beacons' AND c.column_name NOT IN ('geom', 'gid') ORDER BY c.ordinal_position) as i WHERE i.column_name = 'beacon';",
- "IS_SURVEYPLAN":"SELECT CASE WHEN COUNT(*) <> 0 THEN TRUE ELSE FALSE END FROM survey WHERE plan_no = %s;",
- "INSERT_SURVEYPLAN":"INSERT INTO survey(plan_no, ref_beacon) VALUES(%s, %s);",
- "UPDATE_LINK":"SELECT beardistupdate(%s, %s, %s, %s, %s, %s, %s, %s);",
- "DELETE_LINK":"DELETE FROM beacons WHERE beacon = %s;",
- "INSERT_LINK":"SELECT beardistinsert(%s, %s, %s, %s, %s, %s, %s);"
-}
diff --git a/sml_surveyor.py b/sml_surveyor.py
deleted file mode 100644
index d9ad15b..0000000
--- a/sml_surveyor.py
+++ /dev/null
@@ -1,376 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-/***************************************************************************
- sml_surveyor
- A QGIS plugin
- SML Surveyor Plugin
- -------------------
- begin : 2012-12-28
- copyright : (C) 2012 by AfriSpatial
- email : robert@afrispatial.co.za
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * 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 *
-from qgis.core import *
-import __init__ as meta
-import database
-import settings
-from PyQt4Dialogs import *
-import json
-import os
-
-class sml_surveyor:
-
- def __init__(self, iface):
- # Save reference to the QGIS interface
- self.iface = iface
- # initialize plugin directory
- self.plugin_dir = os.path.dirname(os.path.realpath(__file__))
- # initialize locale
- localePath = ""
- locale = QSettings().value("locale/userLocale").toString()[0:2]
- if QFileInfo(self.plugin_dir).exists():
- localePath = os.path.join(self.plugin_dir, "i18n", "sml_surveyor_" + str(locale) + ".qm")
- if QFileInfo(localePath).exists():
- self.translator = QTranslator()
- self.translator.load(localePath)
- if qVersion() > '4.3.3':
- QCoreApplication.installTranslator(self.translator)
- # initialize instance variables
- self.layers = {}
- self.db = None
-
- def initGui(self):
- """ Initialize gui
- """
- # find database
- self.db = None
- try:
- self.db = database.manager(settings.DATABASE_PARAMS)
- except:
- QMessageBox.information(None, "Configure Database Settings", "Please define database parameters.")
- if not(self.manageDatabase()): raise Exception("Unspecied database parameters")
- # find layers
- self.getLayers()
- self.showLayers()
- # add app toolbar
- self.createAppToolBar()
-
- def unload(self):
- """ Uninitialize gui
- """
- # remove layers
- #self.dropLayers()
- # remove app toolbar
- self.removeAppToolBar()
-
- def createAppToolBar(self):
- """ Create plugin toolbar to house buttons
- """
- # create app toolbar
- self.appToolBar = QToolBar(meta.name())
- self.appToolBar.setObjectName(meta.name())
- # create apps for toolbar
- self.actionBearDist = QAction(QIcon(os.path.join(self.plugin_dir, "images", "beardist.png")), "Manage Bearings and Distances", self.iface.mainWindow())
- self.actionBearDist.triggered.connect(self.manageBearDist)
- self.actionBeacons = QAction(QIcon(os.path.join(self.plugin_dir, "images", "beacon.gif")), "Manage %s" %(settings.DATABASE_LAYERS["BEACONS"]["NAME_PLURAL"].title(),), self.iface.mainWindow())
- self.actionBeacons.triggered.connect(self.manageBeacons)
- self.actionParcels = QAction(QIcon(os.path.join(self.plugin_dir, "images", "parcel.png")), "Manage %s" %(settings.DATABASE_LAYERS["PARCELS"]["NAME_PLURAL"].title(),), self.iface.mainWindow())
- self.actionParcels.triggered.connect(self.manageParcels)
- # populate app toolbar
- self.appToolBar.addAction(self.actionBearDist)
- self.appToolBar.addAction(self.actionBeacons)
- self.appToolBar.addAction(self.actionParcels)
- # add app toolbar to gui
- self.iface.mainWindow().addToolBar(self.appToolBar)
-
- def removeAppToolBar(self):
- """ Remove plugin toolbar which houses buttons
- """
- # remove app toolbar from gui
- if hasattr(self,"appToolBar"): self.iface.mainWindow().removeToolBar(self.appToolBar)
-
- def addLayers(self):
- """ Add postgres layers
- """
- uri = QgsDataSourceURI()
- uri.setConnection(settings.DATABASE_HOST, settings.DATABASE_PORT, settings.DATABASE_NAME, settings.DATABASE_USER, settings.DATABASE_PASSWORD)
- for name in reversed(settings.DATABASE_LAYERS_ORDER):
- lyr = settings.DATABASE_LAYERS[name]
- uri.setDataSource(lyr["SCHEMA"], lyr["TABLE"], lyr["GEOM"], "", lyr["PKEY"])
- self.iface.addVectorLayer(uri.uri(), lyr["NAME_PLURAL"], "postgres")
-
- def getLayers(self):
- """ Save reference to added postgis layers
- """
- self.layers = {}
- names = list(settings.DATABASE_LAYERS_ORDER)
- for l in self.iface.legendInterface().layers():
- for n in names:
- if settings.DATABASE_LAYERS[n]["NAME_PLURAL"].lower() == str(l.name()).lower():
- self.layers[n] = l
- names.remove(n)
-
- def showLayers(self):
- """ Show added postgis layers on map canvas
- """
- for n,l in self.layers.items():
- self.iface.legendInterface().setLayerVisible(l, True)
- l.loadNamedStyle(os.path.join(self.plugin_dir, "styles", "%s.qml" %(n.lower(),)))
- self.iface.legendInterface().refreshLayerSymbology(l)
-
- def dropLayers(self):
- """ Drop added postgis layers
- """
- self.getLayers()
- QgsMapLayerRegistry.instance().removeMapLayers([l.id() for l in self.layers.values()])
-
- def manageBeacons(self):
- """ Portal which enables the management of beacons
- """
- self.manageLayers()
- p = Beacons(self.iface, self.layers, settings.DATABASE_LAYERS, self.db)
- p.run()
- self.iface.mapCanvas().refresh()
-
- def manageParcels(self):
- """ Portal which enables the management of parcels
- """
- self.manageLayers()
- p = Parcels(self.iface, self.layers, settings.DATABASE_LAYERS, self.db)
- p.run()
- self.iface.mapCanvas().refresh()
-
- def manageDatabase(self):
- """ Portal which enables configuration of database settings
- """
- dlg = dlg_FormDatabase()
- dlg.show()
- if bool(dlg.exec_()):
- save, db, params = dlg.getReturn()
- # save new db reference
- self.db = db
- # save new params if needed
- if save:
- import os
- p = os.path.join(os.path.dirname(__file__), 'database_params.py')
- f = open(p, 'w')
- f.write('DATABASE_PARAMS = %s' %(json.dumps(params),))
- f.close()
- reload(settings.database_params)
- reload(settings)
- return True
- return False
-
- def manageBearDist(self):
- """
- """
- self.manageLayers()
- dlg = dlg_FormBearDist(self.db, settings.DATABASE_OTHER_SQL, settings.DATABASE_LAYERS["BEACONS"]["SQL"], self.db.getSchema(settings.DATABASE_LAYERS["BEACONS"]["TABLE"], [settings.DATABASE_LAYERS["BEACONS"]["GEOM"], settings.DATABASE_LAYERS["BEACONS"]["PKEY"]]))
- dlg.show()
- if bool(dlg.exec_()):
- #tmp = ('YE125', 'PBA6015', [[[29.45, 46.29, 'PBA6015', 'PBA6016lol', None, None], 'UPDATE', 0], [[123.0, 123.0, 'PBA6016lol', 'l', None, None], 'INSERT', None], [[123.0, 234.0, 'l', 'lo', None, None], 'INSERT', None], [[123.0, 345.0, 'lo', 'lol', None, None], 'INSERT', None]])
- surveyPlan, referenceBeacon, beardistChain = dlg.getReturn()
- # check whether survey plan is defined otherwise define it
- if not self.db.query(settings.DATABASE_OTHER_SQL["IS_SURVEYPLAN"], (surveyPlan,))[0][0]:
- QMessageBox.information(None, "", "need to make a plan about this survey")
- self.db.query(settings.DATABASE_OTHER_SQL["INSERT_SURVEYPLAN"], (surveyPlan, referenceBeacon))
- # get list of existing links
- beardistChainExisting = []
- for index, link in enumerate(self.db.query(settings.DATABASE_OTHER_SQL["EXIST_BEARDISTCHAINS"],(surveyPlan,))):
- beardistChainExisting.append([list(link), "NULL", index] )
- # perform appropriate action for each link in the beardist chain
- new = []
- old = []
- for link in beardistChain:
- if link[2] is None: new.append(link)
- else: old.append(link)
- #QMessageBox.information(None, "", str(beardistChainExisting))
- #QMessageBox.information(None, "", str(new))
- #QMessageBox.information(None, "", str(old))
- # sort out old links
- tmp = list(beardistChainExisting)
- for elink in beardistChainExisting:
- for olink in old:
- if elink[2] == olink[2]:
- if olink[1] == "NULL":
- tmp.remove(elink)
- break;
- self.db.query(settings.DATABASE_OTHER_SQL["UPDATE_LINK"], [surveyPlan] + olink[0] + [olink[2]])
- tmp.remove(elink)
- break;
- beardistChainExisting = tmp
- for elink in beardistChainExisting:
- self.db.query(settings.DATABASE_OTHER_SQL["DELETE_LINK"], (elink[0][3],))
- # sort out new links
- for nlink in new:
- self.db.query(settings.DATABASE_OTHER_SQL["INSERT_LINK"], [surveyPlan] + nlink[0])
- self.iface.mapCanvas().refresh()
-
- def manageLayers(self):
- """ Load layers if not yet loaded
- """
- self.getLayers()
- if len(self.layers.keys()) != len(settings.DATABASE_LAYERS.keys()):
- self.dropLayers()
- self.addLayers()
- self.getLayers()
- self.showLayers()
-
-class Parcels():
- """ Class managing parcels
- """
-
- def __init__(self, iface, layers, layersDict, db):
- self.iface = iface
- self.layers = layers
- self.layersDict = layersDict
- self.db = db
-
- def run(self):
- """ Main method
- """
- # display manager dialog
- mng = dlg_Manager(obj = {"NAME":self.layersDict["PARCELS"]["NAME"],})
- mng.show()
- if bool(mng.exec_()):
- if mng.getReturn() == 0:
- while True:
- # create new parcel
- autocomplete = [str(i[0]) for i in self.db.query(self.layersDict["PARCELS"]["SQL"]["AUTOCOMPLETE"])]
- #autocomplete = [i[0] for i in self.db.query(self.layersDict["PARCELS"]["SQL"]["AUTOCOMPLETE"])]
- frm = dlg_FormParcel(self.db, self.iface, self.layers, self.layersDict, autocomplete)
- frm.show()
- frm_ret = frm.exec_()
- self.iface.mapCanvas().setMapTool(frm.tool)
- if bool(frm_ret):
- sql = ""
- for i, beacon in enumerate(frm.getReturn()[1]["sequence"]):
- sql += self.db.queryPreview(self.layersDict["PARCELS"]["SQL"]["INSERT"], (frm.getReturn()[1]["parcel_id"], beacon, i))
- self.db.query(sql)
- self.iface.mapCanvas().refresh()
- else:
- break
- for l in self.layers.values(): l.removeSelection()
- elif mng.getReturn() == 1:
- # edit existing parcel
- obj = {"NAME":self.layersDict["PARCELS"]["NAME"],"PURPOSE":"EDITOR","ACTION":"EDIT"}
- slc = dlg_Selector(self.db, self.iface, self.layers["PARCELS"], self.layersDict["PARCELS"]["SQL"], obj = obj, preserve = True)
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
- autocomplete = [str(i[0]) for i in self.db.query(self.layersDict["PARCELS"]["SQL"]["AUTOCOMPLETE"])]
- #autocomplete = [i[0] for i in self.db.query(self.layersDict["PARCELS"]["SQL"]["AUTOCOMPLETE"])]
- values = (lambda t: {"parcel_id":t[0], "sequence":t[1]})(self.db.query(self.layersDict["PARCELS"]["SQL"]["EDIT"], (slc.getReturn(),))[0])
- frm = dlg_FormParcel(self.db, self.iface, self.layers, self.layersDict, autocomplete, values)
- frm.show()
- frm_ret = frm.exec_()
- self.iface.mapCanvas().setMapTool(frm.tool)
- if bool(frm_ret):
- self.db.query(self.layersDict["PARCELS"]["SQL"]["DELETE"], (frm.getReturn()[0]["parcel_id"],))
- sql = ""
- for i, beacon in enumerate(frm.getReturn()[1]["sequence"]):
- sql += self.db.queryPreview(self.layersDict["PARCELS"]["SQL"]["INSERT"], (frm.getReturn()[1]["parcel_id"], beacon, i))
- self.db.query(sql)
- for l in self.layers.values(): l.removeSelection()
- elif mng.getReturn() == 2:
- # delete existing parcel
- obj = {"NAME":self.layersDict["PARCELS"]["NAME"],"PURPOSE":"REMOVER","ACTION":"REMOVE"}
- slc = dlg_Selector(self.db, self.iface, self.layers["PARCELS"], self.layersDict["PARCELS"]["SQL"], obj = obj, preserve = True)
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
- self.db.query(self.layersDict["PARCELS"]["SQL"]["DELETE"], (self.db.query(self.layersDict["PARCELS"]["SQL"]["SELECT"], (slc.getReturn(),))[0][0],))
- for l in self.layers.values(): l.removeSelection()
-
-class Beacons():
- """ Class managing beacons
- """
-
- def __init__(self, iface, layers, layersDict, db):
- self.iface = iface
- self.layers = layers
- self.layersDict = layersDict
- self.db = db
-
- def run(self):
- """ Main method
- """
- # display manager dialog
- mng = dlg_Manager(obj = {"NAME":self.layersDict["BEACONS"]["NAME"],})
- mng.show()
- if bool(mng.exec_()):
- if mng.getReturn() == 0:
- while True:
- # create new beacon
- data = self.db.getSchema(self.layersDict["BEACONS"]["TABLE"], [self.layersDict["BEACONS"]["GEOM"], self.layersDict["BEACONS"]["PKEY"]])
- frm = dlg_FormBeacon(self.db, data, self.layersDict["BEACONS"]["SQL"])
- frm.show()
- frm_ret = frm.exec_()
- if bool(frm_ret):
- values_old, values_new = frm.getReturn()
- self.db.query(self.layersDict["BEACONS"]["SQL"]["INSERT"].format(fields = ", ".join(sorted(values_new.keys())), values = ", ".join(["%s" for k in values_new.keys()])), [values_new[k] for k in sorted(values_new.keys())])
- self.iface.mapCanvas().refresh()
- else:
- break
- elif mng.getReturn() == 1:
- # edit existing beacon
- obj = {"NAME":self.layersDict["BEACONS"]["NAME"],"PURPOSE":"EDITOR","ACTION":"EDIT"}
- slc = dlg_Selector(self.db, self.iface, self.layers["BEACONS"], self.layersDict["BEACONS"]["SQL"], obj = obj, preserve = True)
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
- if self.db.query(self.layersDict["BEACONS"]["SQL"]["BEARDIST"], (slc.getReturn(),))[0][0]:
- QMessageBox.warning(None, "Bearing and Distance Definition", "Cannot edit beacon defined by distance and bearing via this tool")
- for l in self.layers.values(): l.removeSelection()
- return
- data = self.db.getSchema(self.layersDict["BEACONS"]["TABLE"], [self.layersDict["BEACONS"]["GEOM"], self.layersDict["BEACONS"]["PKEY"]])
- fields = ",".join([f["NAME"] for f in data])
- values = [v for v in self.db.query(self.layersDict["BEACONS"]["SQL"]["EDIT"].format(fields = fields), (slc.getReturn(),))[0]]
- frm = dlg_FormBeacon(self.db, data, self.layersDict["BEACONS"]["SQL"], values)
- frm.show()
- frm_ret = frm.exec_()
- if bool(frm_ret):
- fields_old = []
- fields_new = []
- values_old = []
- values_new = []
- for f in data:
- if frm.getReturn()[0][f["NAME"]] is not None:
- fields_old.append(f["NAME"])
- values_old.append(frm.getReturn()[0][f["NAME"]])
- fields_new.append(f["NAME"])
- values_new.append(frm.getReturn()[1][f["NAME"]])
- set = ", ".join(["{field} = %s".format(field = f) for f in fields_new])
- where = " AND ".join(["{field} = %s".format(field = f) for f in fields_old])
- self.db.query(self.layersDict["BEACONS"]["SQL"]["UPDATE"].format(set = set, where = where), values_new + values_old)
- for l in self.layers.values(): l.removeSelection()
- elif mng.getReturn() == 2:
- # delete existing beacon
- obj = {"NAME":self.layersDict["BEACONS"]["NAME"],"PURPOSE":"REMOVER","ACTION":"REMOVE"}
- slc = dlg_Selector(self.db, self.iface, self.layers["BEACONS"], self.layersDict["BEACONS"]["SQL"], obj = obj, preserve = True)
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
- if self.db.query(self.layersDict["BEACONS"]["SQL"]["BEARDIST"], (slc.getReturn(),))[0][0]:
- QMessageBox.warning(None, "Bearing and Distance Definition", "Cannot delete beacon defined by distance and bearing via this tool")
- for l in self.layers.values(): l.removeSelection()
- return
- self.db.query(self.layersDict["BEACONS"]["SQL"]["DELETE"], (slc.getReturn(),))
- for l in self.layers.values(): l.removeSelection()
-
From 47bd735cc577de2ba22f2ff8133da88463888e66 Mon Sep 17 00:00:00 2001
From: gavin
Date: Sat, 11 Jan 2014 16:39:12 +0200
Subject: [PATCH 2/7] styles from March 2013 in Lagos
---
DMS2DD.ods | Bin 10867 -> 10672 bytes
database_changes_v2.sql | 7 +-
metadata.txt | 2 +-
styles/beacons.qml | 49 +++++---
styles/parcel_area_errors.qml | 209 ++++++++++++++++++++++++++++++++++
styles/parcels.qml | 129 ++++++++++++++-------
styles/parcels.sld | 171 ++++++++++++++++++++++++++++
styles/parcels_18.qml | 192 +++++++++++++++++++++++++++++++
8 files changed, 701 insertions(+), 58 deletions(-)
create mode 100644 styles/parcel_area_errors.qml
create mode 100644 styles/parcels.sld
create mode 100644 styles/parcels_18.qml
diff --git a/DMS2DD.ods b/DMS2DD.ods
index e23a105095b756f42669e66a836f28c593d1dd97..2da7a71ddaedddfe43128e7be6e3513a8c03939d 100644
GIT binary patch
delta 6738
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
S$$#lH0tsYiLuY0AP5eK?p$KIF
diff --git a/database_changes_v2.sql b/database_changes_v2.sql
index 44c443e..47434db 100644
--- a/database_changes_v2.sql
+++ b/database_changes_v2.sql
@@ -244,4 +244,9 @@ drop view parcels;
alter table parcel_lookup alter column plot_sn type character varying;
-CREATE VIEW parcels ...
\ No newline at end of file
+CREATE VIEW parcels ...
+
+--power cut corrupted parcel view? some parcels defined by < 3 beacons in parcel_def. So to remove them:
+
+delete from parcel_def where parcel_id in
+(select parcel_id from parcel_def group by parcel_id having count(parcel_id) <3)
diff --git a/metadata.txt b/metadata.txt
index b318a06..217c8e8 100644
--- a/metadata.txt
+++ b/metadata.txt
@@ -4,7 +4,7 @@
name=SML Surveyor Plugin
description=SML Surveyor Plugin
qgisMinimumVersion=2.0
-version=Version 0.1
+version=Version 0.2
author=AfriSpatial
email=robert@afrispatial.co.za
diff --git a/styles/beacons.qml b/styles/beacons.qml
index 80b5fd5..a23898b 100644
--- a/styles/beacons.qml
+++ b/styles/beacons.qml
@@ -1,5 +1,5 @@
-
+
255
@@ -11,12 +11,12 @@
-
+
-
+
@@ -27,7 +27,7 @@
-
+
@@ -52,9 +52,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -65,8 +80,8 @@
-
-
+
+
@@ -92,14 +107,14 @@
-
+
-
+
-
+
@@ -141,8 +156,16 @@
.
.
- generatedlayout
-
-
+
+
+
+
+
+
+ Pie
+ 0
+
+ 0
+
diff --git a/styles/parcel_area_errors.qml b/styles/parcel_area_errors.qml
new file mode 100644
index 0000000..e9b3d23
--- /dev/null
+++ b/styles/parcel_area_errors.qml
@@ -0,0 +1,209 @@
+
+
+ 255
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generatedlayout
+
+
+
+
diff --git a/styles/parcels.qml b/styles/parcels.qml
index b0636c0..0ede778 100644
--- a/styles/parcels.qml
+++ b/styles/parcels.qml
@@ -1,10 +1,16 @@
255
-
+
+
+
+
+
+
+
-
-
+
+
@@ -13,9 +19,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
@@ -26,37 +65,37 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -64,14 +103,14 @@
-
+
-
+
@@ -86,15 +125,15 @@
-
+
-
+
-
+
@@ -107,7 +146,7 @@
- id
+ parcel_id
@@ -130,14 +169,18 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
.
diff --git a/styles/parcels.sld b/styles/parcels.sld
new file mode 100644
index 0000000..e635038
--- /dev/null
+++ b/styles/parcels.sld
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+ parcels
+
+
+ group 0
+ Feature
+
+ acquisition
+
+
+ block
+ acquisition
+
+
+
+
+ #A020F0
+ 1.0
+
+
+
+
+ block
+
+
+ Arial
+ 12.0
+ normal
+ normal
+
+
+
+
+ 0.0
+ 0.0
+
+
+ 0.0
+ 0.0
+
+
+
+
+ 1
+
+ #FFFF00
+
+
+
+ #A020F0
+
+
+
+
+ perimeter
+
+
+ block
+ perimeter
+
+
+
+
+ #A52A2A
+ 1.0
+
+
+
+
+ scheme
+
+
+ Arial
+ 12.0
+ normal
+ normal
+
+
+
+
+ 0.0
+ 0.0
+
+
+ 0.0
+ 0.0
+
+
+
+
+ 1
+
+ #FFFF00
+
+
+
+ #A52A2A
+
+
+
+
+ parcels
+
+
+
+
+
+ block
+ perimeter
+
+
+ block
+ acquisition
+
+
+
+
+ block
+
+
+
+ 32001.0
+
+
+ 0.3
+
+
+
+
+ parcel_number
+
+
+ Arial
+ 10.0
+ normal
+ normal
+
+
+
+
+ 0.0
+ 0.0
+
+
+ 0.0
+ 0.0
+
+
+
+
+ 1
+
+ #FFFFFF
+
+
+
+ #000000
+
+
+
+
+
+
+
+
diff --git a/styles/parcels_18.qml b/styles/parcels_18.qml
new file mode 100644
index 0000000..c6ab5e4
--- /dev/null
+++ b/styles/parcels_18.qml
@@ -0,0 +1,192 @@
+
+
+ 255
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ .
+ generatedlayout
+
+
+
+
From b400204c0f28ec5eab0bcb407c94ca25b8669438 Mon Sep 17 00:00:00 2001
From: Christian Christelis
Date: Thu, 19 Jun 2014 12:22:30 +0200
Subject: [PATCH 3/7] Fix create parcel issue.
---
database.py | 44 ++++++++------
plugin.py | 166 +++++++++++++++++++++++++++-------------------------
2 files changed, 111 insertions(+), 99 deletions(-)
diff --git a/database.py b/database.py
index f4c4846..6124f65 100644
--- a/database.py
+++ b/database.py
@@ -28,7 +28,7 @@ def __init__(self, name, type, required, unique):
class Manager:
-
+
def __init__(self, params):
# test db settings
self.params = params
@@ -39,7 +39,7 @@ def connect(self, params):
""" Create a backend postgres database connection
"""
try:
- # check if connection object exist
+ # check if connection object exist
if not hasattr(self, 'conn') or self.conn is None:
self.conn = psycopg2.connect("host='{HOST}' dbname='{NAME}' user='{USER}' password='{PASSWORD}' port='{PORT}'".format(HOST=params["HOST"], NAME=params["NAME"], USER=params["USER"], PASSWORD= params["PASSWORD"], PORT=params["PORT"]))
# check if cursor objet exists
@@ -47,17 +47,17 @@ def connect(self, params):
self.cursor = self.conn.cursor()
except Exception as e:
raise Exception('Could not connect to database!\nError raised: {error}.'.format(error = str(e)))
-
+
def disconnect(self):
""" Terminate a backend postgres database connection
"""
try:
- # check if a cursor object exists
- if hasattr(self, 'cursor') and self.cursor is not None:
+ # check if a cursor object exists
+ if hasattr(self, 'cursor') and self.cursor is not None:
self.cursor.close()
self.cursor = None
# check if a connection object exists
- if hasattr(self, 'conn') and self.conn is not None:
+ if hasattr(self, 'conn') and self.conn is not None:
self.conn.close()
self.conn = None
except Exception as e:
@@ -69,12 +69,14 @@ def query(self, query, data=None):
"""
try:
self.connect(self.params)
- if data is None: self.cursor.execute(query)
- else: self.cursor.execute(query, data)
+ if data is None:
+ self.cursor.execute(query)
+ else:
+ self.cursor.execute(query, data)
records = None
try:
records = self.cursor.fetchall()
- except:
+ except:
pass
self.conn.commit()
self.disconnect()
@@ -82,30 +84,36 @@ def query(self, query, data=None):
except Exception as e:
raise Exception('Backend database query failed!\nError raised: %s.' %(str(e),))
- def queryPreview(self, query, data=None):
+ def queryPreview(self, query, data=None, multi_data=False):
""" Preview query
@returns query (str)
"""
try:
self.connect(self.params)
sql = ""
- if data is None: sql = self.cursor.mogrify(query)
- else: sql = self.cursor.mogrify(query, data)
+ if data is None:
+ sql = self.cursor.mogrify(query)
+ else:
+ if multi_data:
+ sql = self.cursor.mogrify(query, data)
+ sql = sql.replace('((', '(').replace('))', ')')
+ else:
+ sql = self.cursor.mogrify(query, data)
self.disconnect()
return sql
except Exception as e:
raise Exception('Backend database mogrification failed!\nError raised: %s.' %(str(e),))
-
+
def getSchema(self, tbl_name, fld_ignore):
- """ Get information abot a specific table
+ """ Get information abot a specific table
@returns [, , ] (list)
"""
return [Field(
- data[0],
- self._pythonize_type(data[1]),
- data[2],
+ data[0],
+ self._pythonize_type(data[1]),
+ data[2],
data[3]
- ) for data in reversed(self.query("SELECT c.column_name, c.data_type, CASE WHEN c.is_nullable = 'NO' THEN TRUE ELSE FALSE END AS required, CASE WHEN u.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS unique FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name, tc.table_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') AND tc.table_name = '{table}') u ON u.column_name = c.column_name WHERE c.table_name = '{table}' AND c.column_name NOT IN ({ignore});".format(table = tbl_name, ignore = ", ".join("'%s'" %(i,) for i in fld_ignore))))]
+ ) for data in reversed(self.query("SELECT c.column_name, c.data_type, CASE WHEN c.is_nullable = 'NO' THEN TRUE ELSE FALSE END AS required, CASE WHEN u.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS unique FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name, tc.table_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') AND tc.table_name = '{table}') u ON u.column_name = c.column_name WHERE c.table_name = '{table}' AND c.column_name NOT IN ({ignore});".format(table = tbl_name, ignore = ", ".join("'%s'" %(i,) for i in fld_ignore))))]
def _pythonize_type(self, db_type):
""" Get python type
diff --git a/plugin.py b/plugin.py
index ea566d7..428587b 100644
--- a/plugin.py
+++ b/plugin.py
@@ -36,14 +36,14 @@
class RequiredLayer:
- def __init__(self,
- name,
- name_plural,
- table,
+ def __init__(self,
+ name,
+ name_plural,
+ table,
primary_key,
- geometry_type,
- geometry_column='the_geom',
- schema='public',
+ geometry_type,
+ geometry_column='the_geom',
+ schema='public',
layer=None
):
self.name = name
@@ -57,14 +57,14 @@ def __init__(self,
class Mode:
-
+
def __init__(self, actor, action):
self.actor = actor
self.action = action
class sml_surveyor:
-
+
def __init__(self, iface):
# save reference to the QGIS interface
self.iface = iface
@@ -89,8 +89,8 @@ def initGui(self):
"""
# create plugin toolbar
self.createPluginToolBar()
-
-
+
+
def unload(self):
""" Uninitialize gui
"""
@@ -102,7 +102,7 @@ def unload(self):
if bool(l.layer):
QgsMapLayerRegistry.instance().removeMapLayers([l.layer.id(),])
-
+
def createPluginToolBar(self):
""" Create plugin toolbar to house buttons
"""
@@ -111,17 +111,17 @@ def createPluginToolBar(self):
self.pluginToolBar.setObjectName(metadata.name())
# create Beardist button
self.actionBearDist = QAction(
- QIcon(os.path.join(self.plugin_dir, "images", "beardist.png")),
- "Manage Bearings and Distances",
+ QIcon(os.path.join(self.plugin_dir, "images", "beardist.png")),
+ "Manage Bearings and Distances",
self.iface.mainWindow()
)
self.actionBearDist.setWhatsThis("Manage bearings and distances")
self.actionBearDist.setStatusTip("Manage bearings and distances")
- self.actionBearDist.triggered.connect(self.manageBearDist)
+ self.actionBearDist.triggered.connect(self.manageBearDist)
# create Beacons button
self.actionBeacons = QAction(
- QIcon(os.path.join(self.plugin_dir, "images", "beacon.gif")),
- "Manage Beacons",
+ QIcon(os.path.join(self.plugin_dir, "images", "beacon.gif")),
+ "Manage Beacons",
self.iface.mainWindow()
)
self.actionBeacons.setWhatsThis("Manage beacons")
@@ -129,8 +129,8 @@ def createPluginToolBar(self):
self.actionBeacons.triggered.connect(self.manageBeacons)
# create Parcels button
self.actionParcels = QAction(
- QIcon(os.path.join(self.plugin_dir, "images", "parcel.png")),
- "Manage Parcels",
+ QIcon(os.path.join(self.plugin_dir, "images", "parcel.png")),
+ "Manage Parcels",
self.iface.mainWindow()
)
self.actionParcels.setWhatsThis("Manage parcels")
@@ -148,10 +148,10 @@ def removePluginToolBar(self):
""" Remove plugin toolbar which houses buttons
"""
# remove app toolbar from gui
- if hasattr(self,"pluginToolBar"):
+ if hasattr(self,"pluginToolBar"):
self.iface.mainWindow().removeToolBar(self.pluginToolBar)
self.pluginToolBar.hide()
-
+
def setDatabaseConnection(self):
""" Create a database connection
@@ -194,9 +194,9 @@ def setDatabaseConnection(self):
msg = "Please enter the username and password."
for i in range(max_attempts):
ok, db_username, db_password = QgsCredentials.instance().get(
- self.uri.connectionInfo(),
- db_username,
- db_password,
+ self.uri.connectionInfo(),
+ db_username,
+ db_password,
msg
)
if not ok: break
@@ -220,7 +220,7 @@ def setDatabaseConnection(self):
self.datetime = datetime.now()
break
except Exception as e:
- msg = "Invalid username and password."
+ msg = "Invalid username and password."
settings_plugin.endGroup()
settings_postgis.endGroup()
@@ -231,7 +231,7 @@ def refreshLayers(self):
if bool(self.db):
for l in reversed(self.requiredLayers):
for layer in self.iface.legendInterface().layers():
- if l.name_plural.lower() == layer.name().lower():
+ if l.name_plural.lower() == layer.name().lower():
l.layer = layer
break
if not bool(l.layer):
@@ -244,13 +244,13 @@ def refreshLayers(self):
)
self.iface.addVectorLayer(
self.uri.uri(),
- l.name_plural,
+ l.name_plural,
"postgres"
)
for layer in self.iface.legendInterface().layers():
if l.name_plural == layer.name(): l.layer = layer
-
-
+
+
def manageBeacons(self):
""" Portal which enables the management of beacons
"""
@@ -266,7 +266,7 @@ def manageBeacons(self):
def manageParcels(self):
""" Portal which enables the management of parcels
"""
- if self.datetime.date() != datetime.now().date(): self.db = None
+ if self.datetime.date() != datetime.now().date(): self.db = None
if self.db is None:
self.setDatabaseConnection()
if self.db is None: return
@@ -276,10 +276,10 @@ def manageParcels(self):
def manageBearDist(self):
- """ Portal which enables the management of
+ """ Portal which enables the management of
bearings and distances
"""
- if self.datetime.date() != datetime.now().date(): self.db = None
+ if self.datetime.date() != datetime.now().date(): self.db = None
if self.db is None:
self.setDatabaseConnection()
if self.db is None: return
@@ -305,18 +305,18 @@ def run(self):
mng.show()
mng_ret = mng.exec_()
if bool(mng_ret):
-
- if mng.getOption() == 0: # create new beacon
+
+ if mng.getOption() == 0: # create new beacon
while True:
# get fields
fields = self.db.getSchema(
self.requiredLayers[0].table, [
- self.requiredLayers[0].geometry_column,
+ self.requiredLayers[0].geometry_column,
self.requiredLayers[0].primary_key
])
# display form
frm = dlg_FormBeacon(
- self.db,
+ self.db,
SQL_BEACONS["UNIQUE"],
fields
)
@@ -324,10 +324,10 @@ def run(self):
frm_ret = frm.exec_()
if bool(frm_ret):
# add beacon to database
- values_old, values_new = frm.getValues()
+ values_old, values_new = frm.getValues()
self.db.query(
SQL_BEACONS["INSERT"].format(fields = ", ".join(sorted(values_new.keys())), values = ", ".join(["%s" for k in values_new.keys()])), [values_new[k] for k in sorted(values_new.keys())])
- self.iface.mapCanvas().refresh()
+ self.iface.mapCanvas().refresh()
else: break
elif mng.getOption() == 1: # edit existing beacon
@@ -335,10 +335,10 @@ def run(self):
mode = Mode("EDITOR","EDIT")
query = SQL_BEACONS["SELECT"]
slc = dlg_Selector(
- self.db,
- self.iface,
- self.requiredLayers[0],
- mode,
+ self.db,
+ self.iface,
+ self.requiredLayers[0],
+ mode,
query,
preserve = True
)
@@ -350,8 +350,8 @@ def run(self):
# check if defined by a bearing and distance
if self.db.query(SQL_BEACONS["BEARDIST"], (featID,))[0][0]:
QMessageBox.warning(
- None,
- "Bearing and Distance Definition",
+ None,
+ "Bearing and Distance Definition",
"Cannot edit beacon defined by distance and bearing via this tool"
)
for l in self.requiredLayers: l.layer.removeSelection()
@@ -359,15 +359,15 @@ def run(self):
# get fields
fields = self.db.getSchema(
self.requiredLayers[0].table, [
- self.requiredLayers[0].geometry_column,
+ self.requiredLayers[0].geometry_column,
self.requiredLayers[0].primary_key
])
# get values
values = [v for v in self.db.query(SQL_BEACONS["EDIT"].format(fields = ",".join([f.name for f in fields])), (featID,))[0]]
# display form
frm = dlg_FormBeacon(
- self.db,
- SQL_BEACONS["UNIQUE"],
+ self.db,
+ SQL_BEACONS["UNIQUE"],
fields,
values
)
@@ -389,7 +389,7 @@ def run(self):
where = " AND ".join(["{field} = %s".format(field = f) for f in fields_old])
self.db.query(
SQL_BEACONS["UPDATE"].format(
- set = set,
+ set = set,
where = where
), values_new + values_old
)
@@ -400,10 +400,10 @@ def run(self):
mode = Mode("REMOVER","REMOVE")
query = SQL_BEACONS["SELECT"]
slc = dlg_Selector(
- self.db,
- self.iface,
- self.requiredLayers[0],
- mode,
+ self.db,
+ self.iface,
+ self.requiredLayers[0],
+ mode,
query,
preserve = True
)
@@ -418,7 +418,7 @@ def run(self):
for l in self.requiredLayers: l.layer.removeSelection()
return
# delete beacon from database
- self.db.query(SQL_BEACONS["DELETE"], (featID,))
+ self.db.query(SQL_BEACONS["DELETE"], (featID,))
for l in self.requiredLayers: l.layer.removeSelection()
@@ -429,7 +429,7 @@ def __init__(self, iface, db, requiredLayers):
self.db = db
self.requiredLayers = requiredLayers
self.run()
-
+
def run(self):
""" Main method
@@ -447,11 +447,11 @@ def run(self):
SQL_PARCELS["AUTOCOMPLETE"]
)]
frm = dlg_FormParcel(
- self.db,
- self.iface,
- self.requiredLayers,
+ self.db,
+ self.iface,
+ self.requiredLayers,
SQL_BEACONS,
- SQL_PARCELS,
+ SQL_PARCELS,
autocomplete
)
frm.show()
@@ -459,12 +459,16 @@ def run(self):
self.iface.mapCanvas().setMapTool(frm.tool)
if bool(frm_ret):
# add parcel to database
- sql = ""
+ points = []
for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
- sql += self.db.queryPreview(
- SQL_PARCELS["INSERT"],
- (frm.getValues()[1]["parcel_id"], beacon, i)
- )
+ points.append(
+ (frm.getValues()[1]["parcel_id"], beacon, i))
+ sql = self.db.queryPreview(
+ SQL_PARCELS["INSERT"],
+ data=points,
+ multi_data=True
+ )
+ print sql
self.db.query(sql)
self.iface.mapCanvas().refresh()
else:
@@ -476,10 +480,10 @@ def run(self):
mode = Mode("EDITOR","EDIT")
query = SQL_PARCELS["SELECT"]
slc = dlg_Selector(
- self.db,
- self.iface,
- self.requiredLayers[1],
- mode,
+ self.db,
+ self.iface,
+ self.requiredLayers[1],
+ mode,
query,
preserve = True
)
@@ -511,27 +515,27 @@ def run(self):
if bool(frm_ret):
# edit parcel in database
self.db.query(
- SQL_PARCELS["DELETE"],
+ SQL_PARCELS["DELETE"],
(frm.getValues()[0]["parcel_id"],)
)
sql = ""
for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
sql += self.db.queryPreview(
- SQL_PARCELS["INSERT"],
+ SQL_PARCELS["INSERT"],
(frm.getValues()[1]["parcel_id"], beacon, i)
)
self.db.query(sql)
for l in self.requiredLayers: l.layer.removeSelection()
-
+
elif mng.getOption() == 2: # delete existing parcel
# select parcel
mode = Mode("REMOVER","REMOVE")
query = SQL_PARCELS["SELECT"]
slc = dlg_Selector(
- self.db,
- self.iface,
- self.requiredLayers[1],
- mode,
+ self.db,
+ self.iface,
+ self.requiredLayers[1],
+ mode,
query,
preserve = True
)
@@ -543,7 +547,7 @@ def run(self):
featID = slc.getFeatureId()
self.db.query(SQL_PARCELS["DELETE"], (self.db.query(
SQL_PARCELS["SELECT"], (featID,)
- )[0][0],))
+ )[0][0],))
for l in self.requiredLayers: l.layer.removeSelection()
@@ -559,7 +563,7 @@ def run(self):
""" Main method
"""
dlg = dlg_FormBearDist(
- self.db,
+ self.db,
SQL_BEARDIST,
SQL_BEACONS,
self.requiredLayers
@@ -570,11 +574,11 @@ def run(self):
surveyPlan, referenceBeacon, beardistChain = dlg.getReturn()
# check whether survey plan is defined otherwise define it
if not self.db.query(
- SQL_BEARDIST["IS_SURVEYPLAN"],
+ SQL_BEARDIST["IS_SURVEYPLAN"],
(surveyPlan,)
)[0][0]:
self.db.query(
- SQL_BEARDIST["INSERT_SURVEYPLAN"],
+ SQL_BEARDIST["INSERT_SURVEYPLAN"],
(surveyPlan, referenceBeacon)
)
# get list of existing links
@@ -596,7 +600,7 @@ def run(self):
tmp.remove(elink)
break;
self.db.query(
- SQL_BEARDIST["UPDATE_LINK"],
+ SQL_BEARDIST["UPDATE_LINK"],
[surveyPlan] + olink[0] + [olink[2]]
)
tmp.remove(elink)
@@ -604,12 +608,12 @@ def run(self):
beardistChainExisting = tmp
for elink in beardistChainExisting:
self.db.query(
- SQL_BEARDIST["DELETE_LINK"],
+ SQL_BEARDIST["DELETE_LINK"],
(elink[0][3],)
)
# sort out new links
for nlink in new:
self.db.query(
- SQL_BEARDIST["INSERT_LINK"],
+ SQL_BEARDIST["INSERT_LINK"],
[surveyPlan] + nlink[0]
)
From cda918ce7be246a6eed81d54d4bb4904196abecf Mon Sep 17 00:00:00 2001
From: Christian Christelis
Date: Thu, 19 Jun 2014 13:51:59 +0200
Subject: [PATCH 4/7] Allow for more than 3 points to be added in the insert
statement.
---
constants.py | 2 ++
database.py | 3 ++-
plugin.py | 3 +--
3 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/constants.py b/constants.py
index 2f19fc5..366612f 100644
--- a/constants.py
+++ b/constants.py
@@ -22,6 +22,8 @@
"AVAILABLE":"SELECT available FROM parcel_lookup WHERE parcel_id = %s;",
"INSERT":"INSERT INTO parcel_def(parcel_id, beacon, sequence) \
VALUES (%s, %s, %s);",
+ "INSERT_GENERAL": "INSERT INTO parcel_def(parcel_id, beacon, sequence) \
+ VALUES %s;",
"DELETE":"DELETE FROM parcel_def WHERE parcel_id = %s;",
}
diff --git a/database.py b/database.py
index 6124f65..d9a0cea 100644
--- a/database.py
+++ b/database.py
@@ -95,8 +95,9 @@ def queryPreview(self, query, data=None, multi_data=False):
sql = self.cursor.mogrify(query)
else:
if multi_data:
+ placeholders = ','.join(['%s' for dummy in data])
+ query = query % (placeholders)
sql = self.cursor.mogrify(query, data)
- sql = sql.replace('((', '(').replace('))', ')')
else:
sql = self.cursor.mogrify(query, data)
self.disconnect()
diff --git a/plugin.py b/plugin.py
index 428587b..f06e0fc 100644
--- a/plugin.py
+++ b/plugin.py
@@ -464,11 +464,10 @@ def run(self):
points.append(
(frm.getValues()[1]["parcel_id"], beacon, i))
sql = self.db.queryPreview(
- SQL_PARCELS["INSERT"],
+ SQL_PARCELS["INSERT_GENERAL"],
data=points,
multi_data=True
)
- print sql
self.db.query(sql)
self.iface.mapCanvas().refresh()
else:
From cda1558ed9221f22009c65f4bfa192c91b9a9870 Mon Sep 17 00:00:00 2001
From: Christian Christelis
Date: Thu, 19 Jun 2014 15:13:41 +0200
Subject: [PATCH 5/7] fixing editing parcel (same problem as adding)
---
plugin.py | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/plugin.py b/plugin.py
index f06e0fc..3fbf55d 100644
--- a/plugin.py
+++ b/plugin.py
@@ -517,12 +517,15 @@ def run(self):
SQL_PARCELS["DELETE"],
(frm.getValues()[0]["parcel_id"],)
)
- sql = ""
+ points = []
for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
- sql += self.db.queryPreview(
- SQL_PARCELS["INSERT"],
- (frm.getValues()[1]["parcel_id"], beacon, i)
- )
+ points.append(
+ (frm.getValues()[1]["parcel_id"], beacon, i))
+ sql = self.db.queryPreview(
+ SQL_PARCELS["INSERT_GENERAL"],
+ data=points,
+ multi_data=True
+ )
self.db.query(sql)
for l in self.requiredLayers: l.layer.removeSelection()
From bfb77ba67c934d32a085e31763b6e48b14171962 Mon Sep 17 00:00:00 2001
From: Gavin Fleming
Date: Thu, 30 Mar 2017 09:04:19 +0200
Subject: [PATCH 6/7] updated README
---
README.html | 32 -------------------------------
README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++
README.txt | 34 ---------------------------------
3 files changed, 54 insertions(+), 66 deletions(-)
delete mode 100644 README.html
create mode 100644 README.md
delete mode 100644 README.txt
diff --git a/README.html b/README.html
deleted file mode 100644
index 2d03414..0000000
--- a/README.html
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-Plugin Builder Results
-
-Your plugin
sml_surveyor was created in:
-
/home/robert/Development/sml_surveyor
-
-Your QGIS plugin directory is located at:
- /home/robert/.qgis//python/plugins
-
-What's Next
-
- - Copy the entire directory containing your new plugin to the QGIS plugin directory
-
- Compile the ui file using pyuic4
-
- Compile the resources file using pyrcc4
-
- Test the plugin by enabling it in the QGIS plugin manager
-
- Customize it by editing the implementation file sml_surveyor.py
-
- Create your own custom icon, replacing the default icon.png
-
- Modify your user interface by opening sml_surveyor.ui in Qt Designer (don't forget to compile it with pyuic4 after changing it)
-
- You can use the Makefile to compile your Ui and resource files when you make changes. This requires GNU make (gmake)
-
-
-
-
-©2012 GeoApt LLC - geoapt.com
-
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c65d957
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# CoGo plugin for QGIS
+
+First developed by Afrispatial cc in 2012
+
+Sponsored by: SpatialMatrix, Lagos for Ogun State Government, Nigeria.
+
+Original authors: Robert Moerman, Gavin Fleming
+
+Maintained by Kartoza (Pty) Ltd
+
+Copyright Kartoza 2017
+
+2017 update for Niger State, Nigeria and potentially for general release.
+
+Licence:
+
+# Description
+
+This plugin was developed to enable efficient bulk capture of coordinate geometry off survey diagrams.
+
+It caters for addition, modification and deletion of cadastral properties.
+
+## Bearings and distances
+
+Where bearing and distance data are available, they can and should be used to defined property boundaries. An initial beacon coordinate is captured and then bearings and distances are captured to define all other beacons in a chain.
+
+Then properties are constructed by grouping beacons in a specific order to define a polygon.
+
+## Coordinates
+
+Where bearings and distances are not available, coordinates can be used instead to define beacons.
+
+## Dependencies
+
+This plugin depends on a PostGIS database with a predefined schema including tables, materialised views and triggers.
+
+# User manual
+
+http://goo.gl/CY9TYn
+
+
+# What's Next
+(Robert's old note - needs to be updated)
+
+- Copy the entire directory containing your new plugin to the QGIS plugin directory
+- Compile the ui file using pyuic4
+- Compile the resources file using pyrcc4
+- Test the plugin by enabling it in the QGIS plugin manager
+- Customize it by editing the implementation file `sml_surveyor.py`
+- Create your own custom icon, replacing the default `icon.png`
+- Modify your user interface by opening `sml_surveyor.ui` in Qt Designer (don't forget to compile it with pyuic4 after changing it)
+- You can use the `Makefile` to compile your Ui and resource files when you make changes. This requires GNU make (gmake)
+
+
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 9a83c38..0000000
--- a/README.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-Plugin Builder Results
-
-Your plugin sml_surveyor was created in:
- /home/robert/Development/sml_surveyor
-
-Your QGIS plugin directory is located at:
- /home/robert/.qgis//python/plugins
-
-What's Next:
-
- * Copy the entire directory containing your new plugin to the QGIS plugin
- directory
-
- * Compile the ui file using pyuic4
-
- * Compile the resources file using pyrcc4
-
- * Test the plugin by enabling it in the QGIS plugin manager
-
- * Customize it by editing the implementation file:
- sml_surveyor.py
-
- * Create your own custom icon, replacing the default icon.png
-
- * Modify your user interface by opening sml_surveyor.ui
- in Qt Designer (don't forget to compile it with pyuic4 after changing it)
-
- * You can use the Makefile to compile your Ui and resource files when
- you make changes. This requires GNU make (gmake)
-
-For more information, see the PyQGIS Developer Cookbook at:
-http://www.qgis.org/pyqgis-cookbook/index.html
-
-(C) 2012 GeoApt LLC - geoapt.com
From b421104a3c080b63595c9deb45520a81a7168c73 Mon Sep 17 00:00:00 2001
From: Gavin Fleming
Date: Thu, 30 Mar 2017 09:08:26 +0200
Subject: [PATCH 7/7] updated README
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index c65d957..a225760 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ First developed by Afrispatial cc in 2012
Sponsored by: SpatialMatrix, Lagos for Ogun State Government, Nigeria.
-Original authors: Robert Moerman, Gavin Fleming
+Original authors: Robert Moerman, Admire Nyakudya, Gavin Fleming
Maintained by Kartoza (Pty) Ltd