diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..eded1e5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,73 @@
+services:
+ - docker
+
+env:
+ global:
+ - ON_TRAVIS=true
+ # - IMAGE=elpaso/qgis-testing-environment
+ # - IMAGE=boundlessgeo/qgis-testing-environment
+ - IMAGE=kartoza/qgis-testing
+
+ matrix:
+ # Tags from elpaso
+ # - QGIS_VERSION_TAG=release-2_8
+ # - QGIS_VERSION_TAG=release-2_14
+
+ # Tags from boundlessgeo
+ # - QGIS_VERSION_TAG=release # Current 2.14
+ # - QGIS_VERSION_TAG=master_2 # Current 2.17
+ # - QGIS_VERSION_TAG=master # Current 3.0
+
+ # Tags from kartoza
+ - QGIS_VERSION_TAG=boundlessgeo-2.14.7
+
+language: python
+
+cache:
+ directories:
+ - $HOME/.cache/pip
+
+python:
+ - "2.7"
+
+virtualenv:
+ system_site_packages: true
+
+branches:
+ only:
+ - master
+ - develop
+
+addons:
+ apt:
+ packages:
+ - git
+ - python-software-properties
+
+before_install:
+ - docker pull ${IMAGE}:${QGIS_VERSION_TAG}
+
+install:
+ - pip install --upgrade pip
+ - pip install --upgrade pep8
+ - docker run -d --name qgis-testing-environment -v ${TRAVIS_BUILD_DIR}:/tests_directory -e ON_TRAVIS=${ON_TRAVIS} -e DISPLAY=:99 ${IMAGE}:${QGIS_VERSION_TAG}
+ - sleep 10
+ - docker exec -it qgis-testing-environment sh -c "qgis_setup.sh parcel_plugin"
+
+script:
+ - docker exec -it qgis-testing-environment sh -c "qgis_testrunner.sh test_suite.test_package"
+ - make pep8
+
+notifications:
+ webhooks:
+ urls:
+ - https://webhooks.gitter.im/e/02b5a8e3e7c3c47df25c
+ on_success: change # options: [always|never|change] default: always
+ on_failure: always # options: [always|never|change] default: always
+ on_start: always # options: [always|never|change] default: always
+
+ email:
+ - tim@kartoza.com
+ - gavin@kartoza.com
+
+sudo: false
diff --git a/DMS2DD.ods b/DMS2DD.ods
old mode 100644
new mode 100755
diff --git a/Makefile b/Makefile
old mode 100644
new mode 100755
index 430fae2..9226150
--- a/Makefile
+++ b/Makefile
@@ -115,3 +115,13 @@ clean:
# build documentation with sphinx
doc:
cd help; make html
+
+# Run pep8 style checking
+#http://pypi.python.org/pypi/pep8
+pep8:
+ @echo
+ @echo "-----------"
+ @echo "PEP8 issues"
+ @echo "-----------"
+ @pep8 --version
+ @pep8 --repeat --ignore=E203,E121,E122,E123,E124,E125,E126,E127,E128,E402 . || true
diff --git a/PyQt4Dialogs.py b/PyQt4Dialogs.py
old mode 100644
new mode 100755
index f78c879..8910be2
--- a/PyQt4Dialogs.py
+++ b/PyQt4Dialogs.py
@@ -15,201 +15,583 @@
* *
***************************************************************************/
"""
+from collections import OrderedDict
+import __init__ as metadata
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.gui import *
from qgis.core import *
-from qgisToolbox import featureSelector
+from qgisToolbox import FeatureSelector
from PyQt4Widgets import XQPushButton, XQDialogButtonBox
from database import *
+from utilities import images_path, get_ui_class, get_path, ExtendedComboBox
-def _fromUtf8(s):
+UI_CLASS = get_ui_class("ui_pgnewconnection.ui")
+
+
+def _from_utf8(s):
return s
# All dialogs using selector tool have a captured function
-# All dialogs have a getReturn function
+# All dialogs have a get_return function
+
+
+class NewDatabaseConnectionDialog(QDialog, UI_CLASS):
+ """Dialog implementation class for new db connection."""
+
+ def __init__(self, parent):
+ """Constructor"""
+
+ ssl_disable = {
+ 'name': 'disable',
+ 'value': 0
+ }
+ ssl_allow = {
+ 'name': 'allow',
+ 'value': 1
+ }
+ ssl_prefer = {
+ 'name': 'prefer',
+ 'value': 2
+ }
+ ssl_require = {
+ 'name': 'require',
+ 'value': 3
+ }
+ ssl_verify_ca = {
+ 'name': 'verify_ca',
+ 'value': 4
+ }
+ ssl_verify_full = {
+ 'name': 'verify_full',
+ 'value': 5
+ }
+
+ QDialog.__init__(self, None)
+ self.setupUi(self)
+
+ self.parent = parent
+
+ # add ssl mode option
+ self.cbxSSLmode.addItem(
+ ssl_disable['name'], ssl_disable['value'])
+ self.cbxSSLmode.addItem(
+ ssl_allow['name'], ssl_allow['value'])
+ self.cbxSSLmode.addItem(
+ ssl_prefer['name'], ssl_prefer['value'])
+ self.cbxSSLmode.addItem(
+ ssl_require['name'], ssl_require['value'])
+ self.cbxSSLmode.addItem(
+ ssl_verify_ca['name'], ssl_verify_ca['value'])
+ self.cbxSSLmode.addItem(
+ ssl_verify_full['name'], ssl_verify_full['value'])
+
+ self.auth_config = QgsAuthConfigSelect(self, "postgres")
+ self.tabAuthentication.insertTab(1, self.auth_config, "Configurations")
+
+ def accept(self):
-class dlg_DatabaseConnection(QDialog):
+ settings = QSettings()
+ base_key = "/PostgreSQL/connections/"
+ settings.setValue(base_key + "selected", self.txtName.text())
+ auth_config = self.auth_config.configId()
+
+ if not auth_config and self.chkStorePassword.isChecked():
+ message = ("WARNING: You have opted to save your password. "
+ "It will be stored in unsecured plain text in your "
+ "project files and in your home directory "
+ "(Unix-like OS) or user profile (Windows). "
+ "If you want to avoid this, press Cancel and "
+ "either:\n\na) Don't save a password in the connection "
+ "settings — it will be requested interactively when "
+ "needed;\nb) Use the Configuration tab to add your "
+ "credentials in an HTTP Basic Authentication method "
+ "and store them in an encrypted database.")
+ answer = QMessageBox.question(self,
+ "Saving passwords",
+ message,
+ QMessageBox.Ok | QMessageBox.Cancel)
+ if answer == QMessageBox.Cancel:
+ return
+
+ if settings.contains(base_key + self.txtName.text() + "/service") or \
+ settings.contains(base_key + self.txtName.text() + "/host"):
+ message = ("Should the existing connection %s be overwritten?")
+ answer = QMessageBox.question(self,
+ "Saving connection",
+ message % (self.txtName.text()),
+ QMessageBox.Ok | QMessageBox.Cancel)
+ if answer == QMessageBox.Cancel:
+ return
+
+ base_key += self.txtName.text()
+ settings.setValue(base_key + "/service", self.txtService.text())
+ settings.setValue(base_key + "/host", self.txtHost.text())
+ settings.setValue(base_key + "/port", self.txtPort.text())
+ settings.setValue(base_key + "/database", self.txtDatabase.text())
+ settings.setValue(base_key + "/authcfg", self.auth_config.configId())
+ settings.setValue(
+ base_key + "/publicOnly", self.cb_publicSchemaOnly.isChecked())
+ settings.setValue(
+ base_key + "/geometryColumnsOnly",
+ self.cb_geometryColumnsOnly.isChecked())
+ settings.setValue(
+ base_key + "/dontResolveType", self.cb_dontResolveType.isChecked())
+ settings.setValue(
+ base_key + "/allowGeometrylessTables",
+ self.cb_allowGeometrylessTables.isChecked())
+ settings.setValue(
+ base_key + "/sslmode",
+ self.cbxSSLmode.itemData(self.cbxSSLmode.currentIndex()))
+ settings.setValue(
+ base_key + "/estimatedMetadata",
+ self.cb_useEstimatedMetadata.isChecked())
+
+ if self.chkStoreUsername.isChecked():
+ settings.setValue(base_key + "/username", self.txtUsername.text())
+ settings.setValue(base_key + "/saveUsername", "true")
+ else:
+ settings.setValue(base_key + "/username", "")
+ settings.setValue(base_key + "/saveUsername", "false")
+
+ if self.chkStorePassword.isChecked():
+ settings.setValue(base_key + "/password", self.txtPassword.text())
+ settings.setValue(base_key + "/savePassword", "true")
+ else:
+ settings.setValue(base_key + "/password", "")
+ settings.setValue(base_key + "/savePassword", "false")
+
+ settings.remove(base_key + "/save")
+
+ self.parent.populate_database_choices()
+ new_index = self.parent.cmbbx_conn.findText(
+ self.txtName.text(), Qt.MatchExactly)
+ if new_index >= 0:
+ self.parent.cmbbx_conn.setCurrentIndex(new_index)
+
+ QDialog.accept(self)
+
+
+class DatabaseConnectionDialog(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)
+ super(
+ DatabaseConnectionDialog, self).__init__(
+ None, Qt.WindowStaysOnTopHint)
# initialize ui
- self.conn = None
- self.setupUi()
- self.populateDatabaseChoices()
+ self.connection = None
+ self.crs = None
+ self.setup_ui()
+ self.populate_database_choices()
- def getDatabaseConnection(self):
- return self.conn
+ def get_database_connection(self):
+ return self.connection
- def populateDatabaseChoices(self):
+ def get_crs(self):
+ return self.crs
+
+ def populate_database_choices(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):
+ self.cmbbx_conn.clear()
+ settings_postgres = QSettings()
+ settings_postgres.beginGroup('PostgreSQL/connections')
+ settings_plugin = QSettings()
+ settings_plugin.beginGroup(metadata.name().replace(" ", "_"))
+
+ for index, database in enumerate(settings_postgres.childGroups()):
+ self.cmbbx_conn.addItem(database)
+
+ current_connection = settings_plugin.value("DatabaseConnection", None)
+ if current_connection:
+ for myCount in range(0, self.cmbbx_conn.count()):
+ item_text = self.cmbbx_conn.itemText(myCount)
+ if item_text == current_connection:
+ self.cmbbx_conn.setCurrentIndex(myCount)
+ break
+
+ def test_database_connection(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")
+ connection = str(self.cmbbx_conn.currentText())
+ if not bool(connection.replace(" ", "")):
+ QMessageBox.information(
+ self,
+ "Database Connection",
+ "Please select a database connection")
else:
- self.conn = conn
+ self.connection = connection
+
+ if self.connection:
+ ok = self.test_database_schema()
+ if not ok:
+ return
self.accept()
- def setupUi(self):
+ def test_database_schema(self):
+ """Test whether co-go schema is applied in the database."""
+ query = "SELECT EXISTS (SELECT 1 AS result FROM pg_tables " \
+ "WHERE schemaname = 'public' AND tablename = 'beacons')"
+ extension_query = '''CREATE EXTENSION IF NOT EXISTS postgis ; '''
+
+ settings_postgis = QSettings()
+ settings_postgis.beginGroup('PostgreSQL/connections')
+ max_attempts = 3
+ is_credential_exist = True
+
+ db_service = settings_postgis.value(self.connection + '/service')
+ db_host = settings_postgis.value(self.connection + '/host')
+ db_port = settings_postgis.value(self.connection + '/port')
+ db_name = settings_postgis.value(self.connection + '/database')
+ db_username = settings_postgis.value(self.connection + '/username')
+ db_password = settings_postgis.value(self.connection + '/password')
+
+ uri = QgsDataSourceURI()
+ uri.setConnection(
+ db_host,
+ db_port,
+ db_name,
+ db_username,
+ db_password)
+
+ if not db_username and not db_password:
+ msg = "Please enter the username and password."
+ for i in range(max_attempts):
+ ok, db_username, db_password = (
+ QgsCredentials.instance().get(
+ uri.connectionInfo(),
+ db_username,
+ db_password,
+ msg
+ ))
+
+ self.db_connection = None
+ if is_credential_exist:
+ if db_service:
+ self.db_connection = psycopg2.connect(
+ "service='{SERVICE}' user='{USER}' "
+ "password='{PASSWORD}'".format(
+ SERVICE=db_service,
+ USER=db_username,
+ PASSWORD=db_password
+ ))
+ else:
+ self.db_connection = psycopg2.connect(
+ "host='{HOST}' dbname='{NAME}' user='{USER}' "
+ "password='{PASSWORD}' port='{PORT}'".format(
+ HOST=db_host,
+ NAME=db_name,
+ USER=db_username,
+ PASSWORD=db_password,
+ PORT=db_port
+ ))
+
+ if self.db_connection:
+ cursor = self.db_connection.cursor()
+ cursor.execute(extension_query)
+ cursor.execute(query)
+ is_schema_valid = cursor.fetchall()[0][0]
+ del cursor
+
+ if is_schema_valid:
+ return True
+
+ else:
+
+ dialog = CrsSelectorDialog(self)
+ dialog.exec_()
+
+ crs = dialog.selected_srid
+
+ if crs:
+ query = open(
+ get_path("scripts","database_setup.sql"), "r").read()
+ query = query.replace(":CRS", "{CRS}")
+ cursor = self.db_connection.cursor()
+ cursor.execute(query.format(CRS=crs))
+ self.db_connection.commit()
+ del cursor
+
+ return True
+
+ def setup_ui(self):
""" Initialize ui
"""
# define ui widgets
- self.setObjectName(_fromUtf8("dlg_DatabaseConnection"))
+ self.setObjectName(_from_utf8("DatabaseConnectionDialog"))
self.resize(370, 200)
self.setCursor(QCursor(Qt.ArrowCursor))
self.setModal(True)
self.gridLayout = QGridLayout(self)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.gridLayout.setObjectName(_from_utf8("gridLayout"))
self.verticalLayout = QVBoxLayout()
- self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.verticalLayout.setObjectName(_from_utf8("verticalLayout"))
self.lbl_instr = QLabel(self)
self.lbl_instr.setWordWrap(True)
- self.lbl_instr.setObjectName(_fromUtf8("lbl_instr"))
+ self.lbl_instr.setObjectName(_from_utf8("lbl_instr"))
self.verticalLayout.addWidget(self.lbl_instr)
self.formLayout = QFormLayout()
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
+ self.formLayout.setObjectName(_from_utf8("formLayout"))
self.lbl_conn = QLabel(self)
- self.lbl_conn.setObjectName(_fromUtf8("lbl_conn"))
+ self.lbl_conn.setObjectName(_from_utf8("lbl_conn"))
self.formLayout.setWidget(0, QFormLayout.LabelRole, self.lbl_conn)
self.cmbbx_conn = QComboBox(self)
- self.cmbbx_conn.setObjectName(_fromUtf8("cmbbx_conn"))
+ self.cmbbx_conn.setObjectName(_from_utf8("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.horizontalLayout = QHBoxLayout()
+ self.btn_new_conn = QPushButton("")
+ self.btn_new_conn.setObjectName(_from_utf8("new_conn"))
+ self.btn_new_conn.setMaximumSize(32, 32)
+ self.btn_new_conn.setIcon(QIcon(images_path("icons", "add.png")))
+ self.btn_refresh_conn = QPushButton("")
+ self.btn_refresh_conn.setObjectName(_from_utf8("refresh_conn"))
+ self.btn_refresh_conn.setMaximumSize(32, 32)
+ self.btn_refresh_conn.setIcon(
+ QIcon(images_path("icons", "refresh.png")))
+ self.horizontalLayout.addWidget(self.btn_new_conn)
+ self.horizontalLayout.addWidget(self.btn_refresh_conn)
+ self.horizontalLayout.setAlignment(Qt.AlignRight)
+ self.formLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.formLayout.setLayout(
+ 1, QFormLayout.FieldRole, self.horizontalLayout)
+ self.verticalLayout.addSpacerItem(QSpacerItem(50, 10))
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.btnbx_options.setStandardButtons(
+ XQDialogButtonBox.Cancel | XQDialogButtonBox.Ok)
+ self.btnbx_options.setObjectName(_from_utf8("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,
+ "DatabaseConnectionDialog",
+ "Database Connection",
+ None,
QApplication.UnicodeUTF8
))
self.lbl_conn.setText(QApplication.translate(
- "dlg_DatabaseConnection",
- "Connection: ",
+ "DatabaseConnectionDialog",
+ "Connection: ",
+ None
+ ))
+ self.lbl_instr.setText(QApplication.translate(
+ "DatabaseConnectionDialog",
+ "A database connection has not yet been selected or "
+ "is no longer valid. Please select a database connection or "
+ "define a new 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.",
+ self.btn_new_conn.setToolTip(QApplication.translate(
+ "DatabaseConnectionDialog",
+ "Add new PostgreSQL connection",
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.",
+ self.btn_refresh_conn.setToolTip(QApplication.translate(
+ "DatabaseConnectionDialog",
+ "Refresh available PostgreSQL connection",
None
))
# connect ui widgets
- self.btnbx_options.accepted.connect(self.testDatabaseConnection)
+ self.btnbx_options.accepted.connect(self.test_database_connection)
self.btnbx_options.rejected.connect(self.reject)
+ self.btn_new_conn.clicked.connect(self.create_new_connection)
+ self.btn_refresh_conn.clicked.connect(self.populate_database_choices)
QMetaObject.connectSlotsByName(self)
+ def create_new_connection(self):
+ dialog = NewDatabaseConnectionDialog(self)
+ dialog.exec_()
+
+UI_CLASS = get_ui_class("ui_crsselector.ui")
-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
+
+class CrsSelectorDialog(QDialog, UI_CLASS):
+ """This dialog enables the user to choose a crs for setting up
+ the database.
+ """
+
+ def __init__(self, parent):
+ """Constructor"""
+ QDialog.__init__(self, None)
+ self.setupUi(self)
+
+ self.parent = parent
+ self.db_connection = parent.db_connection
+ self.selected_srid = None
+
+ self.crs_combobox = ExtendedComboBox()
+ self.verticalLayout_2.addWidget(self.crs_combobox)
+ self.populate_combobox()
+
+ self.default_crs_radio.toggled.connect(self.toggle_crs_selectors)
+ self.toggle_crs_selectors(self.default_crs_radio.isChecked())
+
+ def populate_combobox(self):
+ """Populate the crs combobox with available spatial reference system
+ from PostGIS.
+ """
+ query = "SELECT auth_name, srid FROM public.spatial_ref_sys"
+ cursor = self.db_connection.cursor()
+ cursor.execute(query)
+ results = cursor.fetchall()
+ del cursor
+
+ dict_of_crs = {}
+ for result in results:
+ result = list(result)
+ for index, item in enumerate(result):
+ result[index] = str(item)
+ crs_string = ':'.join(result)
+ srid = int(result[1])
+ dict_of_crs[srid] = crs_string
+
+ ordered_dict_of_crs = OrderedDict(sorted(dict_of_crs.items()))
+ self.crs_combobox.setInsertPolicy(QComboBox.InsertAlphabetically)
+ for key, item in ordered_dict_of_crs.iteritems():
+ self.crs_combobox.addItem(item, key)
+
+ def accept(self):
+ if self.default_crs_radio.isChecked():
+ selected_index = self.crs_combobox.currentIndex()
+ self.selected_srid = self.crs_combobox.itemData(selected_index)
+ else:
+ query = (
+ "INSERT into spatial_ref_sys (srid, auth_name, auth_srid, "
+ "proj4text, srtext) values ( {SRID}, '{AUTH_NAME}', "
+ "{AUTH_SRID}, '{PROJTEXT}', '{SRTEXT}')"
+ )
+ SRID = int(self.crs_custom_srid.text())
+ AUTH_SRID = int(self.crs_custom_auth_srid.text())
+ AUTH_NAME = self.crs_custom_auth_name.text()
+ PROJTEXT = self.crs_custom_proj4text.text()
+ SRTEXT = self.crs_custom_srtext.text()
+
+ cursor = self.db_connection.cursor()
+ try:
+ cursor.execute(
+ query.format(
+ SRID=SRID,
+ AUTH_NAME=AUTH_NAME,
+ AUTH_SRID=AUTH_SRID,
+ PROJTEXT=PROJTEXT,
+ SRTEXT=SRTEXT))
+ self.db_connection.commit()
+ self.selected_srid = SRID
+ except Exception as e:
+ QMessageBox.information(
+ self,
+ "Fail to execute",
+ "The query is failed to be executed with error: "
+ "{error_message}".format(error_message=e))
+ return
+ del cursor
+ QDialog.accept(self)
+
+ def toggle_crs_selectors(self, checked):
+ if checked:
+ self.crs_combobox.setEnabled(True)
+ self.crs_custom_srid.setEnabled(False)
+ self.crs_custom_auth_srid.setEnabled(False)
+ self.crs_custom_auth_name.setEnabled(False)
+ self.crs_custom_proj4text.setEnabled(False)
+ self.crs_custom_srtext.setEnabled(False)
+ else:
+ self.crs_combobox.setEnabled(False)
+ self.crs_custom_srid.setEnabled(True)
+ self.crs_custom_auth_srid.setEnabled(True)
+ self.crs_custom_auth_name.setEnabled(True)
+ self.crs_custom_proj4text.setEnabled(True)
+ self.crs_custom_srtext.setEnabled(True)
+
+
+class SelectorDialog(QDialog):
+ """ 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, requiredLayer, mode, query, preserve = False, parent = None):
+ def __init__(self,
+ database,
+ iface,
+ required_layer,
+ mode,
+ query,
+ preserve=False,
+ parent=None):
+
# initialize QDialog class
- super(dlg_Selector, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ super(SelectorDialog, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
- self.setupUi(requiredLayer, mode)
- self.db = db
+ self.setup_ui(required_layer, mode)
+ self.database = database
self.iface = iface
- self.layer = requiredLayer
+ self.layer = required_layer
self.mode = mode
self.query = query
self.preserve = preserve
self.confirmed = False
- self.featID = None
+ self.feat_id = None
# initialize selector tool
- self.selector = featureSelector(iface, requiredLayer.layer, True, self)
+ self.selector = FeatureSelector(
+ iface, required_layer.layer, True, self)
# save qgis tool
- self.tool = self.selector.parentTool
-
- def getFeatureId(self):
- return self.featID
+ self.tool = self.selector.parent_tool
+
+ def get_feature_id(self):
+ return self.feat_id
- def executeOption(self, button):
+ def execute_option(self, button):
""" Perform validation and close the dialog
"""
if self.btnbx_options.standardButton(button) == QDialogButtonBox.Ok:
# check that a feature has been selected
- if self.featID is None:
+ if self.feat_id is None:
QMessageBox.information(
- self,
- "No %s Selected" %(self.layer.name.title(),),
- "Please select a %s." %(self.layer.name.lower(),)
- )
+ self,
+ "No %s Selected" % (self.layer.name.title(),),
+ "Please select a %s." % (self.layer.name.lower()))
return
# check confirmation
- if not self.confirmed:
+ if not self.confirmed:
QMessageBox.information(
- self,
- "No Confirmation",
- "Please tick the confimation check box."
- )
+ 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.layer.removeSelection()
+ # remove selection if needed
+ if not self.preserve:
+ self.layer.layer.removeSelection()
# accept dialog
self.accept()
- else:
+ else:
# reset qgis tool
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection
self.layer.layer.removeSelection()
# reject dialog
self.reject()
-
+
def captured(self, selected):
""" Notify the dialog of a feature selection and disable selecting
"""
# disable selector tool
- self.selector.disableCapturing()
+ self.selector.disable_capturing()
# update dialog
- self.featID = selected[0]
- self.lnedt_featID.setText(str(self.db.query(self.query, (self.featID,))[0][0]))
+ self.feat_id = selected[0]
+ self.lnedt_featID.setText(
+ str(self.database.query(self.query, (self.feat_id,))[0][0]))
self.pshbtn_re.setEnabled(True)
self.chkbx_confirm.setEnabled(True)
-
+
def reselect(self):
""" Blat original selection and re-enable selecting
"""
@@ -217,11 +599,11 @@ def reselect(self):
self.pshbtn_re.setEnabled(False)
self.chkbx_confirm.setEnabled(False)
self.lnedt_featID.setText("")
- self.featID = None
+ self.feat_id = None
# clear selector tool selection
- self.selector.clearSelection()
- # enable selector tool
- self.selector.enableCapturing()
+ self.selector.clear_selection()
+ # enable selector tool
+ self.selector.enable_capturing()
def confirm(self, state):
""" Confirm that the selected feature is correct
@@ -229,398 +611,451 @@ def confirm(self, state):
self.pshbtn_re.setEnabled(not bool(state))
self.confirmed = bool(state)
- def setupUi(self, layer, mode):
+ def setup_ui(self, layer, mode):
""" Initialize ui
"""
# define ui widgets
- self.setObjectName(_fromUtf8("dlg_Selector"))
+ self.setObjectName(_from_utf8("SelectorDialog"))
self.setCursor(QCursor(Qt.ArrowCursor))
self.gridLayout = QGridLayout(self)
self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.gridLayout.setObjectName(_from_utf8("gridLayout"))
self.verticalLayout = QVBoxLayout()
- self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.verticalLayout.setObjectName(_from_utf8("verticalLayout"))
self.splitter = QSplitter(self)
self.splitter.setOrientation(Qt.Horizontal)
- self.splitter.setObjectName(_fromUtf8("splitter"))
+ self.splitter.setObjectName(_from_utf8("splitter"))
self.widget = QWidget(self.splitter)
- self.widget.setObjectName(_fromUtf8("widget"))
+ self.widget.setObjectName(_from_utf8("widget"))
self.formLayout = QFormLayout(self.widget)
self.formLayout.setMargin(0)
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
+ self.formLayout.setObjectName(_from_utf8("formLayout"))
self.lbl_featID = QLabel(self.widget)
- self.lbl_featID.setObjectName(_fromUtf8("lbl_featID"))
+ self.lbl_featID.setObjectName(_from_utf8("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"))
+ self.lnedt_featID.setObjectName(_from_utf8("lnedt_featID"))
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.lnedt_featID)
self.pshbtn_re = QPushButton(self.splitter)
self.pshbtn_re.setEnabled(False)
- self.pshbtn_re.setObjectName(_fromUtf8("pshbtn_re"))
+ self.pshbtn_re.setObjectName(_from_utf8("pshbtn_re"))
self.pshbtn_re.setCursor(QCursor(Qt.PointingHandCursor))
self.verticalLayout.addWidget(self.splitter)
self.chkbx_confirm = QCheckBox(self)
self.chkbx_confirm.setEnabled(False)
- self.chkbx_confirm.setObjectName(_fromUtf8("chkbx_confirm"))
+ self.chkbx_confirm.setObjectName(_from_utf8("chkbx_confirm"))
self.chkbx_confirm.setCursor(QCursor(Qt.PointingHandCursor))
self.verticalLayout.addWidget(self.chkbx_confirm)
self.btnbx_options = QDialogButtonBox(self)
- self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
- self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
+ self.btnbx_options.setStandardButtons(
+ QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
+ self.btnbx_options.setObjectName(_from_utf8("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_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))
+ self.setWindowTitle(
+ QApplication.translate(
+ "SelectorDialog",
+ "%s %s" % (layer.name.title(), mode.actor.title()),
+ None,
+ QApplication.UnicodeUTF8))
+ self.lbl_featID.setText(
+ QApplication.translate(
+ "SelectorDialog",
+ "%s ID" % (layer.name.title()),
+ None,
+ QApplication.UnicodeUTF8))
+ self.pshbtn_re.setText(
+ QApplication.translate(
+ "SelectorDialog", "Re-select", None, QApplication.UnicodeUTF8))
+ self.chkbx_confirm.setText(
+ QApplication.translate(
+ "SelectorDialog",
+ "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)
+ self.btnbx_options.clicked.connect(self.execute_option)
QMetaObject.connectSlotsByName(self)
-class dlg_Manager(QDialog):
- """ This dialog enables the user to select an option with regards to managing a vector layer
+class ManagerDialog(QDialog):
+ """ This dialog enables the user to select an option
+ with regards to managing a vector layer
"""
- def __init__(self, requiredLayer, parent=None):
- super(dlg_Manager, self).__init__(parent, Qt.WindowStaysOnTopHint)
- self.setupUi(requiredLayer)
- self.layer = requiredLayer
+ def __init__(self, required_layer, parent=None):
+ super(ManagerDialog, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ self.setup_ui(required_layer)
+ self.layer = required_layer
self.option = None
- def getOption(self):
+ def get_option(self):
return self.option
- def executeOption(self, button):
+ def execute_option(self, button):
""" Perform validation and close the dialog
"""
if self.btnbx_options.standardButton(button) == QDialogButtonBox.Ok:
# get selected option
- for i, rdbtn in enumerate(self.findChildren(QRadioButton)):
- if rdbtn.isChecked():
- self.option = i
+ for index, radio_button in enumerate(
+ self.findChildren(QRadioButton)):
+ if radio_button.isChecked():
+ self.option = index
break
# check that an option was selected
- if self.option is not None:
+ if self.option is not None:
# accept dialog
self.accept()
- else: QMessageBox.information(self, "Invalid Selection", "Please select an option before clicking OK")
+ else:
+ QMessageBox.information(
+ self,
+ "Invalid Selection",
+ "Please select an option before clicking OK")
else:
# reject dialog
self.reject()
-
- def setupUi(self, layer):
+
+ def setup_ui(self, layer):
""" Initialize ui
"""
# define ui widgets
- self.setObjectName(_fromUtf8("dlg_Manager"))
+ self.setObjectName(_from_utf8("ManagerDialog"))
self.setCursor(QCursor(Qt.ArrowCursor))
- self.setModal(True)
+ self.setModal(False)
self.mainlyt = QGridLayout(self)
self.mainlyt.setSizeConstraint(QLayout.SetFixedSize)
- self.mainlyt.setObjectName(_fromUtf8("mainlyt"))
+ self.mainlyt.setObjectName(_from_utf8("mainlyt"))
self.vrtlyt = QVBoxLayout()
- self.vrtlyt.setObjectName(_fromUtf8("vrtlyt"))
+ self.vrtlyt.setObjectName(_from_utf8("vrtlyt"))
self.grdlyt = QGridLayout()
- self.grdlyt.setObjectName(_fromUtf8("grdlyt"))
+ self.grdlyt.setObjectName(_from_utf8("grdlyt"))
self.rdbtn_add = QRadioButton(self)
- self.rdbtn_add.setObjectName(_fromUtf8("rdbtn_add"))
+ self.rdbtn_add.setObjectName(_from_utf8("rdbtn_add"))
self.rdbtn_add.setCursor(QCursor(Qt.PointingHandCursor))
self.grdlyt.addWidget(self.rdbtn_add, 0, 0, 1, 1)
self.rdbtn_edit = QRadioButton(self)
- self.rdbtn_edit.setObjectName(_fromUtf8("rdbtn_edit"))
+ self.rdbtn_edit.setObjectName(_from_utf8("rdbtn_edit"))
self.rdbtn_edit.setCursor(QCursor(Qt.PointingHandCursor))
self.grdlyt.addWidget(self.rdbtn_edit, 1, 0, 1, 1)
self.rdbtn_del = QRadioButton(self)
- self.rdbtn_del.setObjectName(_fromUtf8("rdbtn_del"))
+ self.rdbtn_del.setObjectName(_from_utf8("rdbtn_del"))
self.rdbtn_del.setCursor(QCursor(Qt.PointingHandCursor))
self.grdlyt.addWidget(self.rdbtn_del, 2, 0, 1, 1)
self.vrtlyt.addLayout(self.grdlyt)
self.btnbx_options = QDialogButtonBox(self)
- self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
+ self.btnbx_options.setStandardButtons(
+ QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
self.btnbx_options.setCenterButtons(False)
- self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
+ self.btnbx_options.setObjectName(_from_utf8("btnbx_options"))
self.btnbx_options.setCursor(QCursor(Qt.PointingHandCursor))
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" %(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
- ))
+ self.setWindowTitle(
+ QApplication.translate(
+ "ManagerDialog",
+ "%s Manager" % (layer.name.title()),
+ None,
+ QApplication.UnicodeUTF8))
+ self.rdbtn_add.setText(
+ QApplication.translate(
+ "ManagerDialog",
+ "Create New %s" % (layer.name.title()),
+ None,
+ QApplication.UnicodeUTF8))
+ self.rdbtn_edit.setText(
+ QApplication.translate(
+ "ManagerDialog",
+ "Edit Existing %s" % (layer.name.title()),
+ None,
+ QApplication.UnicodeUTF8))
+ self.rdbtn_del.setText(
+ QApplication.translate(
+ "ManagerDialog",
+ "Delete Existing %s" % (layer.name.title()),
+ None,
+ QApplication.UnicodeUTF8))
# connect ui widgets
- self.btnbx_options.clicked.connect(self.executeOption)
+ self.btnbx_options.clicked.connect(self.execute_option)
QMetaObject.connectSlotsByName(self)
-class dlg_FormBeacon(QDialog):
+class FormBeaconDialog(QDialog):
""" This dialog enables a user to define and modify a beacon
"""
- def __init__(self, db, query, fields, values=[], parent = None):
+ def __init__(self, database, query, fields, values=[], parent=None):
# initialize QDialog class
- super(dlg_FormBeacon, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ super(FormBeaconDialog, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
- self.setupUi(fields)
+ self.setup_ui(fields)
# initialize instance variables
- self.db = db
+ self.db = database
self.query = query
self.fields = fields
- self.values_old = {}
- self.values_new = {}
+ self.old_values = {}
+ self.new_values = {}
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);"
+ "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):
+ if bool(values):
+ self.populate_form(values)
+
+ def get_values(self):
""" Return intended variable(s) after the dialog has been accepted
"""
- return (self.values_old, self.values_new)
+ return (self.old_values, self.new_values)
- def populateForm(self, values):
+ def populate_form(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
+ for index, value in enumerate(values):
+ if value is not None:
+ self.lnedts[index].setText(str(value))
+ self.old_values[self.fields[index].name] = value
- def executeOption(self, button):
+ def execute_option(self, button):
""" Perform validation and close the dialog
"""
- if self.btnbx_options.standardButton(button) == QDialogButtonBox.Save:
+ if self.options_buttonbox.standardButton(button) == \
+ QDialogButtonBox.Save:
values_new = {}
- # check required fields
+ # 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"])
+ for line_edit in self.lnedts:
+ if bool(line_edit.property("REQUIRED")):
+ if str(line_edit.text()).strip() is "":
+ line_edit.setStyleSheet(self.colours["REQUIRED"])
valid = False
- else: lnedt.setStyleSheet("")
- if not valid:
+ else:
+ line_edit.setStyleSheet("")
+ if not valid:
QMessageBox.information(
- self,
- "Empty Required Fields",
- "Please ensure that all required fields are completed."
- )
+ 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):
+ for index, line_edit in enumerate(self.lnedts):
try:
- if str(lnedt.text()).strip() is not "":
+ if str(line_edit.text()).strip() is not "":
cast = self.fields[index].type
- tmp = cast(str(lnedt.text()).strip())
+ tmp = cast(str(line_edit.text()).strip())
values_new[self.fields[index].name] = tmp
- lnedt.setStyleSheet("")
+ line_edit.setStyleSheet("")
else:
values_new[self.fields[index].name] = None
except Exception as e:
- lnedt.setStyleSheet(self.colours["TYPE"])
+ line_edit.setStyleSheet(self.colours["TYPE"])
valid = False
- if not valid:
+ if not valid:
QMessageBox.information(
- self,
- "Invalid Field Types",
+ 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"])
+ for index, line_edit in enumerate(self.lnedts):
+ if str(line_edit.text()).strip() is "":
+ continue
+ if bool(line_edit.property("UNIQUE")):
+ if self.fields[index].name in self.old_values.keys() and \
+ values_new[self.fields[index].name] == \
+ self.old_values[self.fields[index].name]:
+ line_edit.setStyleSheet("")
+ elif bool(int(self.db.query(self.query % (
+ self.fields[index].name, "%s"),
+ (values_new[self.fields[index].name],))[0][0])):
+ line_edit.setStyleSheet(self.colours["UNIQUE"])
valid = False
- else: lnedt.setStyleSheet("")
- if not valid:
+ else:
+ line_edit.setStyleSheet("")
+ if not valid:
QMessageBox.information(
- self,
- "Fields Not Unique",
- "Please ensure that fields are given unique values."
- )
+ self,
+ "Fields Not Unique",
+ "Please ensure that fields are given unique values.")
return
# save values
- self.values_new = values_new
+ self.new_values = values_new
# accept dialog
self.accept()
else:
# reject dialog
self.reject()
- def setupUi(self, fields):
+ def setup_ui(self, fields):
""" Initialize ui
"""
# define ui widgets
- self.setObjectName(_fromUtf8("dlg_FormBeacon"))
+ self.setObjectName(_from_utf8("FormBeaconDialog"))
self.setCursor(QCursor(Qt.ArrowCursor))
self.setModal(True)
self.gridLayout = QGridLayout(self)
self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+ self.gridLayout.setObjectName(_from_utf8("gridLayout"))
self.verticalLayout = QVBoxLayout()
- self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
+ self.verticalLayout.setObjectName(_from_utf8("verticalLayout"))
self.formLayout = QFormLayout()
self.formLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
+ self.formLayout.setObjectName(_from_utf8("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,)))
+ lbl.setObjectName(_from_utf8("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,)))
+ lnedt.setObjectName(_from_utf8("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,
+ lbl.setText(QApplication.translate(
+ "FormBeaconDialog",
+ ("*" 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))
+ lnedt.setProperty(
+ "TYPE",
+ QApplication.translate(
+ "FormBeaconDialog",
+ 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.line_1.setObjectName(_from_utf8("line_1"))
self.verticalLayout.addWidget(self.line_1)
self.label = QLabel(self)
- self.label.setObjectName(_fromUtf8("label"))
+ self.label.setObjectName(_from_utf8("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.line_2.setObjectName(_from_utf8("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.options_buttonbox = QDialogButtonBox(self)
+ self.options_buttonbox.setStandardButtons(
+ QDialogButtonBox.Cancel | QDialogButtonBox.Save)
+ self.options_buttonbox.setObjectName(_from_utf8("btnbx_options"))
+ self.options_buttonbox.setCursor(QCursor(Qt.PointingHandCursor))
+ self.verticalLayout.addWidget(self.options_buttonbox)
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))
+ self.setWindowTitle(
+ QApplication.translate(
+ "FormBeaconDialog",
+ "Beacon Form",
+ None,
+ QApplication.UnicodeUTF8))
+ self.label.setText(
+ QApplication.translate(
+ "FormBeaconDialog",
+ ""
+ "*Required Field
",
+ None,
+ QApplication.UnicodeUTF8))
# connect ui widgets
- self.btnbx_options.clicked.connect(self.executeOption)
+ self.options_buttonbox.clicked.connect(self.execute_option)
QMetaObject.connectSlotsByName(self)
-class dlg_FormParcel(QDialog):
+class FormParcelDialog(QDialog):
""" This dialog enables a user to define and modify a parcel
"""
-
- def __init__(self, db, iface, requiredLayers, SQL_BEACONS, SQL_PARCELS, autocomplete=[], data={}, parent=None):
+
+ def __init__(
+ self,
+ database,
+ iface,
+ required_layers,
+ SQL_BEACONS,
+ SQL_PARCELS,
+ autocomplete=[],
+ data={},
+ parent=None):
# initialize QDialog class
- super(dlg_FormParcel, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ super(FormParcelDialog, self).__init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
- self.setupUi(autocomplete)
- self.db = db
+ self.setup_ui(autocomplete)
+ self.database = database
self.iface = iface
- self.layers = requiredLayers
+ self.layers = required_layers
self.SQL_BEACONS = SQL_BEACONS
self.SQL_PARCELS = SQL_PARCELS
self.autocomplete = autocomplete
- self.values_old = {}
- self.values_new = {}
+ self.old_values = {}
+ self.new_values = {}
self.sequence = []
self.new_accepted = False
# initialize selector tool
- self.selector = featureSelector(iface, requiredLayers[0].layer, False, self)
+ self.selector = FeatureSelector(
+ iface, required_layers[0].layer, False, self)
# save qgis tool
- self.tool = self.selector.parentTool
+ self.tool = self.selector.parent_tool
# populate form if values are given
- if bool(data):
- self.populateForm(data)
- self.pshbtn_reset.setEnabled(True)
-
- def getValues(self):
- return (self.values_old, self.values_new)
-
- def populateForm(self, data):
- """ Populte form with values given
+ if bool(data):
+ self.populate_form(data)
+ self.reset_pushbutton.setEnabled(True)
+
+ def get_values(self):
+ return (self.old_values, self.new_values)
+
+ def populate_form(self, data):
+ """ Populate form with values given
"""
# get values
- checker = lambda d, k: d[k] if k in d.keys() else None
+ def checker(data, key):
+ return lambda data, key: data[key] if key in data.keys() else None
feat_id = checker(data, "parcel_id")
feat_sequence = checker(data, "sequence")
# use values
- if bool(feat_id):
+ if bool(feat_id):
# populate parcel_id
- 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[1].layer, feat_id)
+ self.old_values["parcel_id"] = self.database.query(
+ self.SQL_PARCELS["SELECT"], (feat_id,))[0][0]
+ self.parcel_id_lineedit.setText(str(self.old_values["parcel_id"]))
+ self.highlight_feature(self.layers[1].layer, feat_id)
if bool(feat_sequence):
# populate sequence
self.sequence = []
- self.values_old["sequence"] = []
+ self.old_values["sequence"] = []
for id in feat_sequence:
- beacon_id = str(self.db.query(self.SQL_BEACONS["SELECT"], (id,))[0][0])
+ beacon_id = str(
+ self.database.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.old_values["sequence"].append(beacon_id)
+ self.sequence_listwidget.addItem(beacon_id.replace("\n", ""))
+ self.highlight_features(self.layers[0].layer, feat_sequence)
self.selector.selected = feat_sequence
# update selector selection
self.selector.selected = feat_sequence
- def highlightFeature(self, layer, feature):
+ def highlight_feature(self, layer, feature):
""" Highlight a single feature on a vector layer
"""
- self.highlightFeatures(layer, [feature,])
+ self.highlight_features(layer, [feature, ])
- def highlightFeatures(self, layer, features):
+ def highlight_features(self, layer, features):
""" Highlight multiple features on a vector layer
"""
layer.setSelectedFeatures(features)
@@ -628,1035 +1063,1153 @@ def highlightFeatures(self, layer, features):
def captured(self, selected):
""" Notify the dialog of a feature selection and disable selecting
"""
- pass
-
- def executeOption(self, button):
+ pass
+
+ def execute_option(self, button):
""" Perform validation and close the dialog
"""
- if self.btnbx_options.standardButton(button) == QDialogButtonBox.Save:
- parcel_id = str(self.lnedt_parcelID.text()).strip()
+ if self.options_buttonbox.standardButton(button) == \
+ QDialogButtonBox.Save:
+ parcel_id = str(self.parcel_id_lineedit.text()).strip()
# check that parcel id exists
- if parcel_id == "":
+ if parcel_id == "":
QMessageBox.information(
- self,
- "Invalid Parcel ID",
- "Please enter a parcel ID."
- )
+ 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."
- )
+ 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:
+ if "parcel_id" in self.old_values.keys() and \
+ str(self.old_values["parcel_id"]) == parcel_id:
pass
- elif not bool(self.db.query(
- self.SQL_PARCELS["UNIQUE"], (int(parcel_id),)
- )[0][0]):
+ elif not bool(
+ self.database.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,
+ self,
+ 'Confirm New Parcel ID',
+ "Are you sure you want to create a new parcel ID?",
+ QMessageBox.Yes,
QMessageBox.No
- ) == QMessageBox.No:
+ ) == QMessageBox.No:
return
self.new_accepted = True
else:
- if not bool(self.db.query(
- self.SQL_PARCELS["AVAILABLE"],
- (parcel_id,)
- )[0][0]):
+ if not bool(
+ self.database.query(
+ self.SQL_PARCELS["AVAILABLE"],
+ (parcel_id,))[0][0]):
QMessageBox.information(
- self,
- "Duplicated Parcel ID",
- "Please enter a unique or available parcel ID."
- )
+ 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:
+ 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."
- )
+ 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
+ self.new_values["parcel_id"] = parcel_id
# save sequence
- self.values_new["sequence"] = self.sequence
- # reset qgis tool
+ self.new_values["sequence"] = self.sequence
+ # refresh canvas and reset qgis tool
+ self.iface.mapCanvas().refresh()
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection
- for l in self.layers: l.layer.removeSelection()
+ for layer in self.layers:
+ layer.layer.removeSelection()
# accept dialog
self.accept()
else:
# reset qgis tool
self.iface.mapCanvas().setMapTool(self.tool)
# remove selection
- for l in self.layers: l.layer.removeSelection()
+ for layer in self.layers:
+ layer.layer.removeSelection()
# accept dialog
self.reject()
- def newBeacon(self):
+ def new_beacon(self):
""" Define a new beacon on the fly to be added to the parcel sequence
"""
# disable self
self.setEnabled(False)
# get fields
- fields = self.db.getSchema(
+ fields = self.database.get_schema(
self.layers[0].table, [
- self.layers[0].geometry_column,
+ self.layers[0].geometry_column,
self.layers[0].primary_key
])
# display form
- frm = dlg_FormBeacon(
- self.db,
+ form = FormBeaconDialog(
+ self.database,
self.SQL_BEACONS["UNIQUE"],
fields
)
- frm.show()
- frm_ret = frm.exec_()
- if bool(frm_ret):
+ form.show()
+ form_ret = form.exec_()
+ if bool(form_ret):
# 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]
+ old_values, new_values = form.get_values()
+ id = self.database.query(
+ self.SQL_BEACONS["INSERT"].format(
+ fields=", ".join(sorted(new_values.keys())),
+ values=", ".join(["%s" for k in new_values.keys()])),
+ [new_values[k] for k in sorted(new_values.keys())])[0][0]
self.iface.mapCanvas().refresh()
- self.highlightFeature(self.layers[0].layer, id)
- self.selector.appendSelection(id)
+ self.highlight_feature(self.layers[0].layer, id)
+ self.selector.append_selection(id)
# enable self
self.setEnabled(True)
- def startSeq(self):
+ def start_sequence(self):
""" Start sequence capturing
"""
# enable capturing
- self.selector.enableCapturing()
+ self.selector.enable_capturing()
# perform button stuffs
- self.pshbtn_start.setEnabled(False)
- self.pshbtn_reset.setEnabled(False)
- self.pshbtn_stop.setEnabled(True)
- self.pshbtn_new.setEnabled(True)
-
- def stopSeq(self):
+ self.start_pushbutton.setEnabled(False)
+ self.reset_pushbutton.setEnabled(False)
+ self.stop_pushbutton.setEnabled(True)
+ self.new_pushbutton.setEnabled(True)
+
+ def stop_sequence(self):
""" Stop sequence capturing
"""
# disable capturing
- self.selector.disableCapturing()
+ self.selector.disable_capturing()
# perform button stuffs
- self.pshbtn_stop.setEnabled(False)
- self.pshbtn_new.setEnabled(False)
- self.lstwdg_sequence.clear()
+ self.stop_pushbutton.setEnabled(False)
+ self.new_pushbutton.setEnabled(False)
+ self.sequence_listwidget.clear()
self.sequence = []
- for i in self.selector.selected:
- beacon_id = str(self.db.query(self.SQL_BEACONS["SELECT"], (i,))[0][0])
+ for feature in self.selector.selected:
+ beacon_id = str(
+ self.database.query(
+ self.SQL_BEACONS["SELECT"], (feature,))[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)
-
- def resetSeq(self):
+ self.sequence_listwidget.addItem(beacon_id.replace("\n", ""))
+ self.start_pushbutton.setEnabled(True)
+ self.reset_pushbutton.setEnabled(True)
+
+ def reset_sequence(self):
""" Reset captured sequence
"""
# clear selection
- self.selector.clearSelection()
+ self.selector.clear_selection()
self.sequence = []
# clear sequence
- self.lstwdg_sequence.clear()
+ self.sequence_listwidget.clear()
# perform button stuffs
- self.pshbtn_reset.setEnabled(False)
- self.pshbtn_start.setEnabled(True)
+ self.reset_pushbutton.setEnabled(False)
+ self.start_pushbutton.setEnabled(True)
- def setupUi(self, autocomplete):
+ def setup_ui(self, autocomplete):
""" Initialize ui
"""
# define ui widgets
- self.setObjectName(_fromUtf8("dlg_FormParcel"))
+ self.setObjectName(_from_utf8("FormParcelDialog"))
self.setCursor(QCursor(Qt.ArrowCursor))
- self.gridLayout = QGridLayout(self)
- self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
- self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
- self.verticalLayout_2 = QVBoxLayout()
- self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
- self.formLayout = QFormLayout()
- self.formLayout.setObjectName(_fromUtf8("formLayout"))
- self.lbl_parcelID = QLabel(self)
- self.lbl_parcelID.setObjectName(_fromUtf8("lbl_parcelID"))
- self.formLayout.setWidget(0, QFormLayout.LabelRole, self.lbl_parcelID)
- self.lnedt_parcelID = QLineEdit(self)
- self.lnedt_parcelID.setObjectName(_fromUtf8("lnedt_parcelID"))
- self.formLayout.setWidget(0, QFormLayout.FieldRole, self.lnedt_parcelID)
+ self.grid_layout = QGridLayout(self)
+ self.grid_layout.setSizeConstraint(QLayout.SetFixedSize)
+ self.grid_layout.setObjectName(_from_utf8("gridLayout"))
+ self.vertical_layout_2 = QVBoxLayout()
+ self.vertical_layout_2.setObjectName(_from_utf8("verticalLayout_2"))
+ self.form_layout = QFormLayout()
+ self.form_layout.setObjectName(_from_utf8("formLayout"))
+ self.parcel_id_label = QLabel(self)
+ self.parcel_id_label.setObjectName(_from_utf8("lbl_parcelID"))
+ self.form_layout.setWidget(
+ 0, QFormLayout.LabelRole, self.parcel_id_label)
+ self.parcel_id_lineedit = QLineEdit(self)
+ self.parcel_id_lineedit.setObjectName(_from_utf8("lnedt_parcelID"))
+ self.form_layout.setWidget(
+ 0, QFormLayout.FieldRole, self.parcel_id_lineedit)
model = QStringListModel()
model.setStringList(autocomplete)
- completer = QCompleter()
+ completer = QCompleter()
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setModel(model)
- self.lnedt_parcelID.setCompleter(completer)
- self.verticalLayout_2.addLayout(self.formLayout)
- self.horizontalLayout_2 = QHBoxLayout()
- self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
- self.lbl_sequence = QLabel(self)
- self.lbl_sequence.setObjectName(_fromUtf8("lbl_sequence"))
- self.horizontalLayout_2.addWidget(self.lbl_sequence)
- spacerItem_1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
- self.horizontalLayout_2.addItem(spacerItem_1)
- self.pshbtn_new = QPushButton(self)
- self.pshbtn_new.setEnabled(False)
- self.pshbtn_new.setObjectName(_fromUtf8("pshbtn_new"))
- self.pshbtn_new.setCursor(QCursor(Qt.PointingHandCursor))
- self.horizontalLayout_2.addWidget(self.pshbtn_new)
- self.verticalLayout_2.addLayout(self.horizontalLayout_2)
- self.horizontalLayout_1 = QHBoxLayout()
- self.horizontalLayout_1.setObjectName(_fromUtf8("horizontalLayout_1"))
- self.verticalLayout_1 = QVBoxLayout()
- self.verticalLayout_1.setObjectName(_fromUtf8("verticalLayout_1"))
- self.pshbtn_start = QPushButton(self)
- self.pshbtn_start.setEnabled(True)
- self.pshbtn_start.setObjectName(_fromUtf8("pshbtn_start"))
- self.pshbtn_start.setCursor(QCursor(Qt.PointingHandCursor))
- self.verticalLayout_1.addWidget(self.pshbtn_start)
- self.pshbtn_stop = QPushButton(self)
- self.pshbtn_stop.setEnabled(False)
- self.pshbtn_stop.setObjectName(_fromUtf8("pshbtn_stop"))
- self.pshbtn_stop.setCursor(QCursor(Qt.PointingHandCursor))
- self.verticalLayout_1.addWidget(self.pshbtn_stop)
- self.pshbtn_reset = QPushButton(self)
- self.pshbtn_reset.setEnabled(False)
- self.pshbtn_reset.setObjectName(_fromUtf8("pshbtn_reset"))
- self.pshbtn_reset.setCursor(QCursor(Qt.PointingHandCursor))
- self.verticalLayout_1.addWidget(self.pshbtn_reset)
- spacerItem_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
- self.verticalLayout_1.addItem(spacerItem_2)
- self.horizontalLayout_1.addLayout(self.verticalLayout_1)
- self.lstwdg_sequence = QListWidget(self)
- self.lstwdg_sequence.setEnabled(False)
- self.lstwdg_sequence.setObjectName(_fromUtf8("lstwdg_sequence"))
- self.horizontalLayout_1.addWidget(self.lstwdg_sequence)
- self.verticalLayout_2.addLayout(self.horizontalLayout_1)
- 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_2.addWidget(self.btnbx_options)
- self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1)
+ self.parcel_id_lineedit.setCompleter(completer)
+ self.vertical_layout_2.addLayout(self.form_layout)
+ self.horizontal_layout_2 = QHBoxLayout()
+ self.horizontal_layout_2.setObjectName(
+ _from_utf8("horizontalLayout_2"))
+ self.sequence_label = QLabel(self)
+ self.sequence_label.setObjectName(_from_utf8("lbl_sequence"))
+ self.horizontal_layout_2.addWidget(self.sequence_label)
+ spacer_item_1 = QSpacerItem(
+ 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.horizontal_layout_2.addItem(spacer_item_1)
+ self.new_pushbutton = QPushButton(self)
+ self.new_pushbutton.setEnabled(False)
+ self.new_pushbutton.setObjectName(_from_utf8("pshbtn_new"))
+ self.new_pushbutton.setCursor(QCursor(Qt.PointingHandCursor))
+ self.horizontal_layout_2.addWidget(self.new_pushbutton)
+ self.vertical_layout_2.addLayout(self.horizontal_layout_2)
+ self.horizontal_layout_1 = QHBoxLayout()
+ self.horizontal_layout_1.setObjectName(
+ _from_utf8("horizontalLayout_1"))
+ self.vertical_layout_1 = QVBoxLayout()
+ self.vertical_layout_1.setObjectName(_from_utf8("verticalLayout_1"))
+ self.start_pushbutton = QPushButton(self)
+ self.start_pushbutton.setEnabled(True)
+ self.start_pushbutton.setObjectName(_from_utf8("pshbtn_start"))
+ self.start_pushbutton.setCursor(QCursor(Qt.PointingHandCursor))
+ self.vertical_layout_1.addWidget(self.start_pushbutton)
+ self.stop_pushbutton = QPushButton(self)
+ self.stop_pushbutton.setEnabled(False)
+ self.stop_pushbutton.setObjectName(_from_utf8("pshbtn_stop"))
+ self.stop_pushbutton.setCursor(QCursor(Qt.PointingHandCursor))
+ self.vertical_layout_1.addWidget(self.stop_pushbutton)
+ self.reset_pushbutton = QPushButton(self)
+ self.reset_pushbutton.setEnabled(False)
+ self.reset_pushbutton.setObjectName(_from_utf8("pshbtn_reset"))
+ self.reset_pushbutton.setCursor(QCursor(Qt.PointingHandCursor))
+ self.vertical_layout_1.addWidget(self.reset_pushbutton)
+ spacer_item_2 = QSpacerItem(
+ 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.vertical_layout_1.addItem(spacer_item_2)
+ self.horizontal_layout_1.addLayout(self.vertical_layout_1)
+ self.sequence_listwidget = QListWidget(self)
+ self.sequence_listwidget.setEnabled(False)
+ self.sequence_listwidget.setObjectName(_from_utf8("lstwdg_sequence"))
+ self.horizontal_layout_1.addWidget(self.sequence_listwidget)
+ self.vertical_layout_2.addLayout(self.horizontal_layout_1)
+ self.options_buttonbox = QDialogButtonBox(self)
+ self.options_buttonbox.setStandardButtons(
+ QDialogButtonBox.Cancel | QDialogButtonBox.Save)
+ self.options_buttonbox.setObjectName(_from_utf8("btnbx_options"))
+ self.options_buttonbox.setCursor(QCursor(Qt.PointingHandCursor))
+ self.vertical_layout_2.addWidget(self.options_buttonbox)
+ self.grid_layout.addLayout(self.vertical_layout_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(
+ "FormParcelDialog",
+ "Parcel Form",
+ None,
+ QApplication.UnicodeUTF8))
+ self.parcel_id_label.setText(
+ QApplication.translate(
+ "FormParcelDialog",
+ "Parcel ID",
+ None,
+ QApplication.UnicodeUTF8))
+ self.sequence_label.setText(
+ QApplication.translate(
+ "FormParcelDialog",
+ "Beacon Sequence",
+ None,
+ QApplication.UnicodeUTF8))
+ self.new_pushbutton.setText(
+ QApplication.translate(
+ "FormParcelDialog",
+ "New Beacon",
+ None,
+ QApplication.UnicodeUTF8))
+ self.start_pushbutton.setText(
+ QApplication.translate(
+ "FormParcelDialog",
+ "Start",
+ None,
+ QApplication.UnicodeUTF8))
+ self.stop_pushbutton.setText(
+ QApplication.translate(
+ "FormParcelDialog",
+ "Stop",
+ None,
+ QApplication.UnicodeUTF8))
+ self.reset_pushbutton.setText(
+ QApplication.translate(
+ "FormParcelDialog",
+ "Reset",
+ None,
+ QApplication.UnicodeUTF8))
# connect ui widgets
- self.pshbtn_new.clicked.connect(self.newBeacon)
- self.pshbtn_start.clicked.connect(self.startSeq)
- self.pshbtn_stop.clicked.connect(self.stopSeq)
- self.pshbtn_reset.clicked.connect(self.resetSeq)
- self.btnbx_options.clicked.connect(self.executeOption)
+ self.new_pushbutton.clicked.connect(self.new_beacon)
+ self.start_pushbutton.clicked.connect(self.start_sequence)
+ self.stop_pushbutton.clicked.connect(self.stop_sequence)
+ self.reset_pushbutton.clicked.connect(self.reset_sequence)
+ self.options_buttonbox.clicked.connect(self.execute_option)
QMetaObject.connectSlotsByName(self)
-class dlg_FormBearDist(QDialog):
+class BearingDistanceFormDialog(QDialog):
""" This dialog enables the user to define bearings and distances
"""
- def __init__(self, db, SQL_BEARDIST, SQL_BEACONS, requiredLayers, parent = None):
+ def __init__(
+ self,
+ database,
+ SQL_BEARDIST,
+ SQL_BEACONS,
+ required_layers,
+ parent=None):
# initialize QDialog class
- super(dlg_FormBearDist, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ super(BearingDistanceFormDialog, self).\
+ __init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
self.setupUi()
- self.db = db
+ self.database = database
self.SQL_BEARDIST = SQL_BEARDIST
self.SQL_BEACONS = SQL_BEACONS
- self.layers = requiredLayers
+ self.layers = required_layers
self.auto = {
- "SURVEYPLAN":self.db.query(
+ "SURVEYPLAN": self.database.query(
SQL_BEARDIST["AUTO_SURVEYPLAN"]
)[0][0],
- "REFERENCEBEACON":self.db.query(
+ "REFERENCEBEACON": self.database.query(
SQL_BEARDIST["AUTO_REFERENCEBEACON"]
)[0][0],
- "FROMBEACON":[]
+ "FROMBEACON": []
}
- self.surveyPlan = None
- self.referenceBeacon = None
- self.beardistChain = []
- self.beardistStr = "%s" + u"\u00B0" + " and %sm from %s to %s"
+ self.survey_plan = None
+ self.reference_beacon = None
+ self.bearing_distance_chain = []
+ self.bearing_distance_string = (
+ "%s" + u"\u00B0" + " and %sm from %s to %s")
# initialize initial step
- self.initItemSurveyPlan()
-
- def getReturn(self):
+ self.init_item_survey_plan()
+
+ def get_return(self):
""" Return intended variable(s) after the dialog has been accepted
- @returns (, , ) (tuple)
+ @returns (
+ , , ) (tuple)
"""
- return (self.surveyPlan, self.referenceBeacon, self.beardistChain)
+ return (
+ self.survey_plan,
+ self.reference_beacon,
+ self.bearing_distance_chain)
- def setCurrentItem(self, index, clear=False, enabled=False):
+ def set_current_item(self, index, clear=False, enabled=False):
""" Set the current toolbox item and disable all other toolbox items
"""
# clear editable fields if needed
if clear:
- for widget in self.tlbx.widget(index).findChildren(QWidget):
+ for widget in self.toolbox.widget(index).findChildren(QWidget):
if type(widget) in [QLineEdit]:
widget.setText("")
# enable editable fields if needed
if enabled:
- for widget in self.tlbx.widget(index).findChildren(QWidget):
+ for widget in self.toolbox.widget(index).findChildren(QWidget):
if type(widget) in [QLineEdit]:
widget.setEnabled(True)
# disable all items
- for i in range(self.count):
- self.tlbx.setItemEnabled(i, False)
+ for i in range(self.count):
+ self.toolbox.setItemEnabled(i, False)
# enable and display desired item
- self.tlbx.setCurrentIndex(index)
- self.tlbx.setItemEnabled(index, True)
+ self.toolbox.setCurrentIndex(index)
+ self.toolbox.setItemEnabled(index, True)
- def initItemSurveyPlan(self, forward=True):
+ def init_item_survey_plan(self, forward=True):
""" Initialize form elements for the survey plan item
"""
if not forward:
- if not self.confirmBack():
+ if not self.confirm_back():
return
# update autocompletion
model = QStringListModel()
model.setStringList(self.auto["SURVEYPLAN"])
- completer = QCompleter()
+ completer = QCompleter()
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setModel(model)
- self.lnedt_plan.setCompleter(completer)
+ self.plan_lineedit.setCompleter(completer)
# reset variables associated with item
- self.surveyPlan = None
+ self.survey_plan = None
# display survey plan item
- self.setCurrentItem(0)
-
- def checkItemSurveyPlan(self, forward):
+ self.set_current_item(0)
+
+ def check_item_survey_plan(self, forward):
""" Validate form elements before proceding from the survey plan item
"""
# check direction
- if forward:
+ if forward:
# check that a server plan number was specified
- surveyPlan = str(self.lnedt_plan.text()).strip()
- if surveyPlan is "":
+ survey_plan = str(self.plan_lineedit.text()).strip()
+ if survey_plan is "":
QMessageBox.information(
- self,
- "Empty Survey Plan Number",
- "Please enter a surver plan number."
- )
+ self,
+ "Empty Survey Plan Number",
+ "Please enter a surver plan number.")
return
# set survey plan number
- self.surveyPlan = surveyPlan
+ self.survey_plan = survey_plan
# display next toolbox item
- self.initItemReferenceBeacon()
- else: pass
+ self.init_item_reference_beacon()
+ else:
+ pass
- def initItemReferenceBeacon(self, forward=True):
+ def init_item_reference_beacon(self, forward=True):
""" Initialize form elements for the reference beacon item
"""
if not forward:
- if not self.confirmBack():
+ if not self.confirm_back():
return
# update autocompletion
model = QStringListModel()
model.setStringList(self.auto["REFERENCEBEACON"])
- completer = QCompleter()
+ completer = QCompleter()
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setModel(model)
- self.lnedt_ref.setCompleter(completer)
+ self.reference_lineedit.setCompleter(completer)
# reset variables associated with items
- self.referenceBeacon = None
+ self.reference_beacon = None
# check direction whence it came
if forward:
# check if survey plan number has a pre-defined reference beacon
- if self.surveyPlan in self.auto["SURVEYPLAN"]:
+ if self.survey_plan in self.auto["SURVEYPLAN"]:
# update item contents
- self.lnedt_ref.setEnabled(False)
- self.lnedt_ref.setText(str(self.db.query(
- self.SQL_BEARDIST["EXIST_REFERENCEBEACON"],
- (self.surveyPlan,)
- )[0][0]))
+ self.reference_lineedit.setEnabled(False)
+ self.reference_lineedit.setText(
+ str(self.database.query(
+ self.SQL_BEARDIST["EXIST_REFERENCEBEACON"],
+ (self.survey_plan,))[0][0]))
else:
# update item contents
- self.lnedt_ref.setEnabled(True)
- self.lnedt_ref.setText("")
+ self.reference_lineedit.setEnabled(True)
+ self.reference_lineedit.setText("")
# display reference beacon item
- self.setCurrentItem(1)
-
- def checkItemReferenceBeacon(self, forward):
+ self.set_current_item(1)
+
+ def check_item_reference_beacon(self, forward):
""" Validate form elements before proceding from the reference beacon item
"""
# check direction
- if forward:
+ if forward:
# check that a reference beacon was specified
- referenceBeacon = str(self.lnedt_ref.text()).strip()
- if referenceBeacon is "":
+ reference_beacon = str(self.reference_lineedit.text()).strip()
+ if reference_beacon is "":
QMessageBox.information(
- self,
- "Empty Reference Beacon",
- "Please enter a reference beacon."
- )
+ self,
+ "Empty Reference Beacon",
+ "Please enter a reference beacon.")
return
# check if reference beacon exists
- if referenceBeacon in self.auto["REFERENCEBEACON"]:
+ if reference_beacon in self.auto["REFERENCEBEACON"]:
# set reference beacon
- self.referenceBeacon = referenceBeacon
+ self.reference_beacon = reference_beacon
# display next toolbox item
- self.initItemBearDistChain()
+ self.init_item_bearing_distance_chain()
else:
# disable self
self.setEnabled(False)
# present beacon form
- column_index = self.db.query(
- self.SQL_BEARDIST["INDEX_REFERENCEBEACON"]
- )[0][0]
+ column_index = self.database.query(
+ self.SQL_BEARDIST["INDEX_REFERENCEBEACON"])[0][0]
# get fields
- fields = self.db.getSchema(
+ fields = self.database.get_schema(
self.layers[0].table, [
- self.layers[0].geometry_column,
- self.layers[0].primary_key
- ])
+ self.layers[0].geometry_column,
+ self.layers[0].primary_key])
# display form
- frm = dlg_FormBeacon(
- self.db,
+ form = FormBeaconDialog(
+ self.database,
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):
+ parent=self)
+ form.lnedts[column_index].setText(reference_beacon)
+ form.lnedts[column_index].setEnabled(False)
+ form.show()
+ form_ret = form.exec_()
+ if bool(form_ret):
# 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())])
+ old_values, new_values = form.get_values()
+ self.database.query(
+ self.SQL_BEACONS["INSERT"].format(
+ fields=", ".join(sorted(new_values.keys())),
+ values=", ".join(
+ ["%s" for index in new_values.keys()])),
+ [new_values[index] for index in (
+ sorted(new_values.keys()))])
# set reference beacon
- self.referenceBeacon = referenceBeacon
- self.auto["REFERENCEBEACON"].append(referenceBeacon)
+ self.reference_beacon = reference_beacon
+ self.auto["REFERENCEBEACON"].append(reference_beacon)
# enable self
self.setEnabled(True)
# display next toolbox item
- self.initItemBearDistChain()
+ self.init_item_bearing_distance_chain()
else:
# enable self
self.setEnabled(True)
else:
- self.initItemSurveyPlan(False)
+ self.init_item_survey_plan(False)
- def initItemBearDistChain(self, forward=True):
+ def init_item_bearing_distance_chain(self, forward=True):
""" Initialize form elements for the beardist chain item
"""
# reset variables associated with items
- self.beardistChain = []
+ self.bearing_distance_chain = []
self.auto["FROMBEACON"] = []
- self.lst_chain.clear()
- self.auto["FROMBEACON"].append(self.referenceBeacon)
+ self.chain_list.clear()
+ self.auto["FROMBEACON"].append(self.reference_beacon)
# perform button stuffs
- self.pshbtn_chain_edt.setEnabled(False)
- self.pshbtn_chain_del.setEnabled(False)
- self.pshbtn_chain_finish.setEnabled(False)
+ self.chain_edit_pushbutton.setEnabled(False)
+ self.chain_delete_pushbutton.setEnabled(False)
+ self.chain_finish_pushbutton.setEnabled(False)
# check if reference beacon is predefined
- if self.referenceBeacon in self.auto["REFERENCEBEACON"]:
+ if self.reference_beacon in self.auto["REFERENCEBEACON"]:
# check if survey plan number is predefined
- if self.surveyPlan in self.auto["SURVEYPLAN"]:
+ if self.survey_plan in self.auto["SURVEYPLAN"]:
# get defined bearings and distances
- records = self.db.query(
- self.SQL_BEARDIST["EXIST_BEARDISTCHAINS"],
- (self.surveyPlan,)
+ records = self.database.query(
+ self.SQL_BEARDIST["EXIST_BEARDISTCHAINS"],
+ (self.survey_plan,)
)
if records not in [None, []]:
- for oid,link in enumerate(records):
- self.beardistChain.append([list(link), "NULL", oid])
- self.updateBearDistChainDependants()
- self.pshbtn_chain_finish.setEnabled(True)
- self.pshbtn_chain_edt.setEnabled(True)
- self.pshbtn_chain_del.setEnabled(True)
+ for object_id, link in enumerate(records):
+ self.bearing_distance_chain.append(
+ [list(link), "NULL", object_id])
+ self.update_bearing_distance_chain_dependants()
+ self.chain_finish_pushbutton.setEnabled(True)
+ self.chain_edit_pushbutton.setEnabled(True)
+ self.chain_delete_pushbutton.setEnabled(True)
# display beardist chain item
- self.setCurrentItem(2)
+ self.set_current_item(2)
- def checkItemBearDistChain(self, forward):
+ def check_item_bearing_distance_chain(self, forward):
""" Validate form elements before proceding from the beardist chain item
"""
# check direction
if forward:
- if not bool(self.surveyPlan):
+ if not bool(self.survey_plan):
QMessageBox.information(
- self,
- "No Survey Plan",
- "Please specify a survey plan number"
- )
+ self,
+ "No Survey Plan",
+ "Please specify a survey plan number")
return
- if not bool(self.referenceBeacon):
+ if not bool(self.reference_beacon):
QMessageBox.information(
- self,
- "No Reference Beacon",
- "Please specify a reference beacon"
- )
+ self,
+ "No Reference Beacon",
+ "Please specify a reference beacon")
return
- if not bool(self.beardistChain):
+ if not bool(self.bearing_distance_chain):
QMessageBox.information(
- self,
- "No Bearing and Distance Chain",
- "Please capture bearings and distances"
- )
+ self,
+ "No Bearing and Distance Chain",
+ "Please capture bearings and distances")
return
self.accept()
else:
- self.initItemReferenceBeacon(False)
+ self.init_item_reference_beacon(False)
- def canFindReferenceBeacon(self, beacon_name):
+ def is_beacon_reference_exist(self, beacon_name):
while True:
beacon_to = None
- for link in self.beardistChain:
+ for link in self.bearing_distance_chain:
if beacon_name == link[0][3]:
beacon_to = link[0][2]
break
- if beacon_to is None: return False
- if beacon_to == beacon_name: return False
- if beacon_to == self.referenceBeacon: return True
-
- def isEndLink(self, index):
+ if beacon_to is None:
+ return False
+ if beacon_to == beacon_name:
+ return False
+ if beacon_to == self.reference_beacon:
+ return True
+
+ def is_end_linked(self, index):
""" Test whether or not the link is safe to edit or delete
"""
- beacon_to = self.beardistChain[index][0][3]
- for link in self.beardistChain:
+ beacon_to = self.bearing_distance_chain[index][0][3]
+ for link in self.bearing_distance_chain:
beacon_from = link[0][2]
if beacon_to == beacon_from:
return False
return True
-
- def isLastAnchorLink(self, index):
+
+ def is_last_anchor_linked(self, index):
""" Test whether or not the link is the only one using the reference beacon
"""
- beacon_ref = self.beardistChain[index][0][2]
+ beacon_reference = self.bearing_distance_chain[index][0][2]
# check if reference beacon is used
- if beacon_ref != self.referenceBeacon: return False
+ if beacon_reference != self.reference_beacon:
+ return False
# count number of reference beacon occurrences
count = 0
- for link in self.beardistChain:
+ for link in self.bearing_distance_chain:
beacon_from = link[0][2]
- if beacon_from == beacon_ref: count += 1
+ if beacon_from == beacon_reference:
+ count += 1
# check count
return True if count == 1 else False
- def getSelectedIndex(self, action):
+ def get_selected_index(self, action):
""" Captures selected link from the chain list
"""
# get list of selected items
- items = self.lst_chain.selectedItems()
+ items = self.chain_list.selectedItems()
# check list is non-empty
if len(items) == 0:
QMessageBox.information(
- self,
- "No Link Selected",
- "Please select a link to edit"
- )
+ 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:
+ if len(items) > 1:
QMessageBox.information(
- self,
- "Too Many Links Selected",
- "Please select only one link to edit"
- )
+ self,
+ "Too Many Links Selected",
+ "Please select only one link to edit")
return None
# get item index
- index = self.lst_chain.row(items[0])
+ index = self.chain_list.row(items[0])
# check that index is of an end link
- if not bool(self.isEndLink(index)):
+ if not bool(self.is_end_linked(index)):
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:
+ 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
- def updateBearDistChainDependants(self):
+ def update_bearing_distance_chain_dependants(self):
""" Reinitialize all variables defined from the beardist chain
"""
# clear dependants
- self.lst_chain.clear()
- self.auto["FROMBEACON"] = [self.referenceBeacon]
+ self.chain_list.clear()
+ self.auto["FROMBEACON"] = [self.reference_beacon]
# populate dependants
- for link in self.beardistChain:
- #QMessageBox.information(self,QString(','.join(link[0][:4])))
- self.lst_chain.addItem(self.beardistStr %tuple(link[0][:4]))
+ for link in self.bearing_distance_chain:
+ # QMessageBox.information(self,QString(','.join(link[0][:4])))
+ self.chain_list.addItem(
+ self.bearing_distance_string % tuple(link[0][:4]))
self.auto["FROMBEACON"].append(link[0][3])
- self.auto["FROMBEACON"].sort()
+ self.auto["FROMBEACON"].sort()
- def addLink(self):
+ def add_link(self):
""" Add a link to the beardist chain
"""
while True:
- 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.getValues()
- self.beardistChain.append([values, "INSERT", None])
- self.updateBearDistChainDependants()
- else: break
- if len(self.beardistChain) >= 1:
- self.pshbtn_chain_finish.setEnabled(True)
- self.pshbtn_chain_edt.setEnabled(True)
- self.pshbtn_chain_del.setEnabled(True)
-
- def editLink(self):
+ dialog = BearingDistanceLinkFormDialog(
+ self.database,
+ self.auto["FROMBEACON"],
+ self.SQL_BEACONS["UNIQUE"],
+ parent=self)
+ dialog.show()
+ dialog_ret = dialog.exec_()
+ if bool(dialog_ret):
+ values = dialog.get_values()
+ self.bearing_distance_chain.append([values, "INSERT", None])
+ self.update_bearing_distance_chain_dependants()
+ else:
+ break
+ if len(self.bearing_distance_chain) >= 1:
+ self.chain_finish_pushbutton.setEnabled(True)
+ self.chain_edit_pushbutton.setEnabled(True)
+ self.chain_delete_pushbutton.setEnabled(True)
+
+ def edit_link(self):
""" Edit a link from the beardist chain
"""
# get selected index
- index = self.getSelectedIndex("edit")
+ index = self.get_selected_index("edit")
# check selection
if index is not None:
# display dialog
- dlg = dlg_FormBearDistLink(
- self.db,
+ dialog = BearingDistanceLinkFormDialog(
+ self.database,
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.getValues()
+ values=self.bearing_distance_chain[index][0],
+ parent=self)
+ if self.is_last_anchor_linked(index):
+ dialog.line_edits[2].setEnabled(False)
+ dialog.show()
+ dialog_ret = dialog.exec_()
+ if bool(dialog_ret):
+ values = dialog.get_values()
# check if anything was changed
- if values == self.beardistChain[index][0]: return
+ if values == self.bearing_distance_chain[index][0]:
+ return
# check if reference beacon can be found
- if not self.canFindReferenceBeacon(self.beardistChain[index][0][3]):
+ if not self.is_beacon_reference_exist(
+ self.bearing_distance_chain[index][0][3]):
QMessageBox.information(None, "", "oops")
# recursively update beacon names if changed
- if self.beardistChain[index][0][3] != values[3]:
- tmp = []
- for link in self.beardistChain:
- if link[0][2] == self.beardistChain[index][0][3]:
+ if self.bearing_distance_chain[index][0][3] != values[3]:
+ temp = []
+ for link in self.bearing_distance_chain:
+ if link[0][2] == (
+ self.bearing_distance_chain[index][0][3]):
link[0][2] = values[3]
- tmp.append(link)
- self.beardistChain = tmp
+ temp.append(link)
+ self.bearing_distance_chain = temp
# update beardist chain entry
- if self.beardistChain[index][1] in ["NULL","UPDATE"]:
- self.beardistChain[index] = [values, "UPDATE", self.beardistChain[index][-1]]
- elif self.beardistChain[index][1] == "INSERT":
- self.beardistChain[index] = [values, "INSERT", None]
- self.updateBearDistChainDependants()
+ if self.bearing_distance_chain[index][1] in ["NULL", "UPDATE"]:
+ self.bearing_distance_chain[index] = [
+ values,
+ "UPDATE",
+ self.bearing_distance_chain[index][-1]]
+ elif self.bearing_distance_chain[index][1] == "INSERT":
+ self.bearing_distance_chain[index] = [
+ values,
+ "INSERT",
+ None]
+ self.update_bearing_distance_chain_dependants()
- def deleteLink(self):
+ def delete_link(self):
""" Delete a link from the beardist chain
"""
# get selected index
- index = self.getSelectedIndex("delete")
+ index = self.get_selected_index("delete")
# check selection
if index is not None:
# prevent last link to use reference beacon from being deleted
- if self.isLastAnchorLink(index):
+ if self.is_last_anchor_linked(index):
QMessageBox.warning(
- self,
- "Last Link To Reference Beacon",
- "Cannot remove last link to reference beacon"
- )
+ 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])
+ self.delete_dependant_links(
+ self.bearing_distance_chain[index][0][3])
# delete link
- del self.beardistChain[index]
- self.updateBearDistChainDependants()
- if len(self.beardistChain) == 0:
- self.pshbtn_chain_finish.setEnabled(False)
- self.pshbtn_chain_edt.setEnabled(False)
- self.pshbtn_chain_del.setEnabled(False)
-
- def deleteLinkDependants(self, beacon_to):
+ del self.bearing_distance_chain[index]
+ self.update_bearing_distance_chain_dependants()
+ if len(self.bearing_distance_chain) == 0:
+ self.chain_finish_pushbutton.setEnabled(False)
+ self.chain_edit_pushbutton.setEnabled(False)
+ self.chain_delete_pushbutton.setEnabled(False)
+
+ def delete_dependant_links(self, beacon_to):
""" Recursively delete dependant links
"""
gone = False
while not gone:
gone = True
index = -1
- for i, link in enumerate(self.beardistChain):
+ for i, link in enumerate(self.bearing_distance_chain):
if link[0][2] == beacon_to:
- if not self.isLastAnchorLink(i):
+ if not self.is_last_anchor_linked(i):
index = i
gone = False
break
if index != -1:
- if not self.isEndLink(index):
- self.deleteLinkDependants(self.beardistChain[index][0][3])
- del self.beardistChain[index]
+ if not self.is_end_linked(index):
+ self.delete_dependant_links(
+ self.bearing_distance_chain[index][0][3])
+ del self.bearing_distance_chain[index]
def setupUi(self):
""" Initialize ui
"""
# define dialog
- self.setObjectName(_fromUtf8("dlg_FormBearDist"))
+ self.setObjectName(_from_utf8("BearingDistanceFormDialog"))
self.resize(450, 540)
self.setModal(True)
- # define dialog layout manager
- self.grdlyt_dlg = QGridLayout(self)
- self.grdlyt_dlg.setSizeConstraint(QLayout.SetDefaultConstraint)
- self.grdlyt_dlg.setObjectName(_fromUtf8("grdlyt_dlg"))
+ # define dialog layout manager
+ self.dialog_gridlayout = QGridLayout(self)
+ self.dialog_gridlayout.setSizeConstraint(QLayout.SetDefaultConstraint)
+ self.dialog_gridlayout.setObjectName(_from_utf8("grdlyt_dlg"))
# define toolbox
- self.tlbx = QToolBox(self)
- self.tlbx.setFrameShape(QFrame.StyledPanel)
- self.tlbx.setObjectName(_fromUtf8("tlbx"))
+ self.toolbox = QToolBox(self)
+ self.toolbox.setFrameShape(QFrame.StyledPanel)
+ self.toolbox.setObjectName(_from_utf8("tlbx"))
self.count = 3
# define first item: survey plan
- self.itm_plan = QWidget()
- self.itm_plan.setObjectName(_fromUtf8("itm_plan"))
- self.grdlyt_plan = QGridLayout(self.itm_plan)
- self.grdlyt_plan.setObjectName(_fromUtf8("grdlyt_chain"))
- self.vrtlyt_plan = QVBoxLayout()
- self.vrtlyt_plan.setObjectName(_fromUtf8("vrtlyt_plan"))
- self.frmlyt_plan = QFormLayout()
- self.frmlyt_plan.setObjectName(_fromUtf8("frmlyt_plan"))
- self.lbl_plan = QLabel(self.itm_plan)
- self.lbl_plan.setObjectName(_fromUtf8("lbl_plan"))
- self.frmlyt_plan.setWidget(0, QFormLayout.LabelRole, self.lbl_plan)
- self.lnedt_plan = QLineEdit(self.itm_plan)
- self.lnedt_plan.setObjectName(_fromUtf8("lnedt_plan"))
- self.frmlyt_plan.setWidget(0, QFormLayout.FieldRole, self.lnedt_plan)
- self.vrtlyt_plan.addLayout(self.frmlyt_plan)
- self.vrtspc_plan = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
- self.vrtlyt_plan.addItem(self.vrtspc_plan)
- self.hrzlyt_plan = QHBoxLayout()
- self.hrzlyt_plan.setObjectName(_fromUtf8("hrzlyt_plan"))
- self.hrzspc_plan = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
- self.hrzlyt_plan.addItem(self.hrzspc_plan)
- self.pshbtn_plan_next = XQPushButton(self.itm_plan)
- self.pshbtn_plan_next.setObjectName(_fromUtf8("pshbtn_plan_next"))
- self.hrzlyt_plan.addWidget(self.pshbtn_plan_next)
- self.vrtlyt_plan.addLayout(self.hrzlyt_plan)
- self.grdlyt_plan.addLayout(self.vrtlyt_plan, 0, 0, 1, 1)
- self.tlbx.addItem(self.itm_plan, _fromUtf8(""))
+ self.plan_widget = QWidget()
+ self.plan_widget.setObjectName(_from_utf8("itm_plan"))
+ self.plan_gridlayout = QGridLayout(self.plan_widget)
+ self.plan_gridlayout.setObjectName(_from_utf8("grdlyt_chain"))
+ self.plan_verticallayout = QVBoxLayout()
+ self.plan_verticallayout.setObjectName(_from_utf8("vrtlyt_plan"))
+ self.plan_formlayout = QFormLayout()
+ self.plan_formlayout.setObjectName(_from_utf8("frmlyt_plan"))
+ self.plan_label = QLabel(self.plan_widget)
+ self.plan_label.setObjectName(_from_utf8("lbl_plan"))
+ self.plan_formlayout.setWidget(
+ 0, QFormLayout.LabelRole, self.plan_label)
+ self.plan_lineedit = QLineEdit(self.plan_widget)
+ self.plan_lineedit.setObjectName(_from_utf8("lnedt_plan"))
+ self.plan_formlayout.setWidget(
+ 0, QFormLayout.FieldRole, self.plan_lineedit)
+ self.plan_verticallayout.addLayout(self.plan_formlayout)
+ self.plan_verticalspacer = QSpacerItem(
+ 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.plan_verticallayout.addItem(self.plan_verticalspacer)
+ self.plan_horizonlayout = QHBoxLayout()
+ self.plan_horizonlayout.setObjectName(_from_utf8("hrzlyt_plan"))
+ self.plan_horizonspacer = QSpacerItem(
+ 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.plan_horizonlayout.addItem(self.plan_horizonspacer)
+ self.plan_next_pushbutton = XQPushButton(self.plan_widget)
+ self.plan_next_pushbutton.setObjectName(_from_utf8("pshbtn_plan_next"))
+ self.plan_horizonlayout.addWidget(self.plan_next_pushbutton)
+ self.plan_verticallayout.addLayout(self.plan_horizonlayout)
+ self.plan_gridlayout.addLayout(self.plan_verticallayout, 0, 0, 1, 1)
+ self.toolbox.addItem(self.plan_widget, _from_utf8(""))
# define second item: reference beacon
- self.itm_ref = QWidget()
- self.itm_ref.setObjectName(_fromUtf8("itm_ref"))
- self.grdlyt_ref = QGridLayout(self.itm_ref)
- self.grdlyt_ref.setObjectName(_fromUtf8("grdlyt_ref"))
- self.vrtlyt_ref = QVBoxLayout()
- self.vrtlyt_ref.setObjectName(_fromUtf8("vrtlyt_ref"))
- self.frmlyt_ref = QFormLayout()
- self.frmlyt_ref.setObjectName(_fromUtf8("frmlyt_ref"))
- self.lbl_ref = QLabel(self.itm_ref)
- self.lbl_ref.setObjectName(_fromUtf8("lbl_ref"))
- self.frmlyt_ref.setWidget(0, QFormLayout.LabelRole, self.lbl_ref)
- self.lnedt_ref = QLineEdit(self.itm_ref)
- self.lnedt_ref.setObjectName(_fromUtf8("lnedt_ref"))
- self.frmlyt_ref.setWidget(0, QFormLayout.FieldRole, self.lnedt_ref)
- self.vrtlyt_ref.addLayout(self.frmlyt_ref)
- self.vrtspc_ref = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
- self.vrtlyt_ref.addItem(self.vrtspc_ref)
- self.hrzlyt_ref = QHBoxLayout()
- self.hrzlyt_ref.setObjectName(_fromUtf8("hrzlyt_ref"))
- self.pshbtn_ref_back = XQPushButton(self.itm_ref)
- self.pshbtn_ref_back.setObjectName(_fromUtf8("pshbtn_ref_back"))
- self.hrzlyt_ref.addWidget(self.pshbtn_ref_back)
- self.hrzspc_ref = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
- self.hrzlyt_ref.addItem(self.hrzspc_ref)
- self.pshbtn_ref_next = XQPushButton(self.itm_ref)
- self.pshbtn_ref_next.setObjectName(_fromUtf8("pshbtn_ref_next"))
- self.hrzlyt_ref.addWidget(self.pshbtn_ref_next)
- self.vrtlyt_ref.addLayout(self.hrzlyt_ref)
- self.grdlyt_ref.addLayout(self.vrtlyt_ref, 0, 0, 1, 1)
- self.tlbx.addItem(self.itm_ref, _fromUtf8(""))
+ self.reference_widget = QWidget()
+ self.reference_widget.setObjectName(_from_utf8("itm_ref"))
+ self.reference_gridlayout = QGridLayout(self.reference_widget)
+ self.reference_gridlayout.setObjectName(_from_utf8("grdlyt_ref"))
+ self.reference_verticallayout = QVBoxLayout()
+ self.reference_verticallayout.setObjectName(_from_utf8("vrtlyt_ref"))
+ self.reference_formlayout = QFormLayout()
+ self.reference_formlayout.setObjectName(_from_utf8("frmlyt_ref"))
+ self.reference_layout = QLabel(self.reference_widget)
+ self.reference_layout.setObjectName(_from_utf8("lbl_ref"))
+ self.reference_formlayout.setWidget(
+ 0, QFormLayout.LabelRole, self.reference_layout)
+ self.reference_lineedit = QLineEdit(self.reference_widget)
+ self.reference_lineedit.setObjectName(_from_utf8("lnedt_ref"))
+ self.reference_formlayout.setWidget(
+ 0, QFormLayout.FieldRole, self.reference_lineedit)
+ self.reference_verticallayout.addLayout(self.reference_formlayout)
+ self.reference_verticalspacer = QSpacerItem(
+ 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.reference_verticallayout.addItem(self.reference_verticalspacer)
+ self.reference_horizonlayout = QHBoxLayout()
+ self.reference_horizonlayout.setObjectName(_from_utf8("hrzlyt_ref"))
+ self.reference_back_pushbutton = XQPushButton(self.reference_widget)
+ self.reference_back_pushbutton.setObjectName(
+ _from_utf8("pshbtn_ref_back"))
+ self.reference_horizonlayout.addWidget(self.reference_back_pushbutton)
+ self.reference_horizonspacer = QSpacerItem(
+ 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.reference_horizonlayout.addItem(self.reference_horizonspacer)
+ self.reference_next_pushbutton = XQPushButton(self.reference_widget)
+ self.reference_next_pushbutton.setObjectName(
+ _from_utf8("pshbtn_ref_next"))
+ self.reference_horizonlayout.addWidget(self.reference_next_pushbutton)
+ self.reference_verticallayout.addLayout(self.reference_horizonlayout)
+ self.reference_gridlayout.addLayout(
+ self.reference_verticallayout, 0, 0, 1, 1)
+ self.toolbox.addItem(self.reference_widget, _from_utf8(""))
# define third item: beardist chain
- self.itm_chain = QWidget()
- self.itm_chain.setObjectName(_fromUtf8("itm_chain"))
- self.grdlyt_chain = QGridLayout(self.itm_chain)
- self.grdlyt_chain.setObjectName(_fromUtf8("grdlyt_chain"))
- self.vrtlyt_chain = QVBoxLayout()
- self.vrtlyt_chain.setObjectName(_fromUtf8("vrtlyt_chain"))
- self.lst_chain = QListWidget(self.itm_chain)
- self.lst_chain.setObjectName(_fromUtf8("lst_chain"))
- self.vrtlyt_chain.addWidget(self.lst_chain)
- self.hrzlyt_chain_link = QHBoxLayout()
- self.hrzlyt_chain_link.setObjectName(_fromUtf8("hrzlyt_chain_link"))
- self.vrtlyt_chain_link = QVBoxLayout()
- self.vrtlyt_chain_link.setObjectName(_fromUtf8("vrtlyt_chain_link"))
- self.pshbtn_chain_add = XQPushButton(self.itm_chain)
- self.pshbtn_chain_add.setObjectName(_fromUtf8("pshbtn_chain_add"))
- self.vrtlyt_chain_link.addWidget(self.pshbtn_chain_add)
- self.pshbtn_chain_edt = XQPushButton(self.itm_chain)
- self.pshbtn_chain_edt.setObjectName(_fromUtf8("pshbtn_chain_edt"))
- self.vrtlyt_chain_link.addWidget(self.pshbtn_chain_edt)
- self.pshbtn_chain_del = XQPushButton(self.itm_chain)
- self.pshbtn_chain_del.setObjectName(_fromUtf8("pshbtn_chain_del"))
- self.vrtlyt_chain_link.addWidget(self.pshbtn_chain_del)
- self.hrzlyt_chain_link.addLayout(self.vrtlyt_chain_link)
- self.hrzspc_chain_link = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
- self.hrzlyt_chain_link.addItem(self.hrzspc_chain_link)
- self.vrtlyt_chain.addLayout(self.hrzlyt_chain_link)
- self.vrtspc_chain = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
- self.vrtlyt_chain.addItem(self.vrtspc_chain)
- self.hrzlyt_chain_step = QHBoxLayout()
- self.hrzlyt_chain_step.setObjectName(_fromUtf8("hrzlyt_chain_step"))
- self.pshbtn_chain_back = XQPushButton(self.itm_chain)
- self.pshbtn_chain_back.setObjectName(_fromUtf8("pshbtn_chain_back"))
- self.hrzlyt_chain_step.addWidget(self.pshbtn_chain_back)
- self.hrzspc_chain_step = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
- self.hrzlyt_chain_step.addItem(self.hrzspc_chain_step)
- self.pshbtn_chain_finish = XQPushButton(self.itm_chain)
- self.pshbtn_chain_finish.setObjectName(_fromUtf8("pshbtn_chain_finish"))
- self.hrzlyt_chain_step.addWidget(self.pshbtn_chain_finish)
- self.vrtlyt_chain.addLayout(self.hrzlyt_chain_step)
- self.grdlyt_chain.addLayout(self.vrtlyt_chain, 0, 0, 1, 1)
- self.tlbx.addItem(self.itm_chain, _fromUtf8(""))
+ self.chain_widget = QWidget()
+ self.chain_widget.setObjectName(_from_utf8("itm_chain"))
+ self.chain_gridlayout = QGridLayout(self.chain_widget)
+ self.chain_gridlayout.setObjectName(_from_utf8("grdlyt_chain"))
+ self.chain_verticallayout = QVBoxLayout()
+ self.chain_verticallayout.setObjectName(_from_utf8("vrtlyt_chain"))
+ self.chain_list = QListWidget(self.chain_widget)
+ self.chain_list.setObjectName(_from_utf8("lst_chain"))
+ self.chain_verticallayout.addWidget(self.chain_list)
+ self.chain_link_horizonlayout = QHBoxLayout()
+ self.chain_link_horizonlayout.setObjectName(
+ _from_utf8("hrzlyt_chain_link"))
+ self.chain_link_verticallayout = QVBoxLayout()
+ self.chain_link_verticallayout.setObjectName(
+ _from_utf8("vrtlyt_chain_link"))
+ self.chain_add_pushbutton = XQPushButton(self.chain_widget)
+ self.chain_add_pushbutton.setObjectName(_from_utf8("pshbtn_chain_add"))
+ self.chain_link_verticallayout.addWidget(self.chain_add_pushbutton)
+ self.chain_edit_pushbutton = XQPushButton(self.chain_widget)
+ self.chain_edit_pushbutton.setObjectName(
+ _from_utf8("pshbtn_chain_edt"))
+ self.chain_link_verticallayout.addWidget(self.chain_edit_pushbutton)
+ self.chain_delete_pushbutton = XQPushButton(self.chain_widget)
+ self.chain_delete_pushbutton.setObjectName(
+ _from_utf8("pshbtn_chain_del"))
+ self.chain_link_verticallayout.addWidget(self.chain_delete_pushbutton)
+ self.chain_link_horizonlayout.addLayout(self.chain_link_verticallayout)
+ self.chain_link_horizonspacer = QSpacerItem(
+ 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.chain_link_horizonlayout.addItem(self.chain_link_horizonspacer)
+ self.chain_verticallayout.addLayout(self.chain_link_horizonlayout)
+ self.chain_verticalspacer = QSpacerItem(
+ 20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
+ self.chain_verticallayout.addItem(self.chain_verticalspacer)
+ self.chain_step_horizonlayout = QHBoxLayout()
+ self.chain_step_horizonlayout.setObjectName(
+ _from_utf8("hrzlyt_chain_step"))
+ self.chain_back_pushbutton = XQPushButton(self.chain_widget)
+ self.chain_back_pushbutton.setObjectName(
+ _from_utf8("pshbtn_chain_back"))
+ self.chain_step_horizonlayout.addWidget(self.chain_back_pushbutton)
+ self.chain_step_horizonspacer = QSpacerItem(
+ 40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
+ self.chain_step_horizonlayout.addItem(self.chain_step_horizonspacer)
+ self.chain_finish_pushbutton = XQPushButton(self.chain_widget)
+ self.chain_finish_pushbutton.setObjectName(
+ _from_utf8("pshbtn_chain_finish"))
+ self.chain_step_horizonlayout.addWidget(self.chain_finish_pushbutton)
+ self.chain_verticallayout.addLayout(self.chain_step_horizonlayout)
+ self.chain_gridlayout.addLayout(self.chain_verticallayout, 0, 0, 1, 1)
+ self.toolbox.addItem(self.chain_widget, _from_utf8(""))
# finish dialog definition
- self.grdlyt_dlg.addWidget(self.tlbx, 0, 0, 1, 1)
- self.btnbx_options = XQDialogButtonBox(self)
- self.btnbx_options.setOrientation(Qt.Horizontal)
- self.btnbx_options.setStandardButtons(XQDialogButtonBox.Cancel)
- self.btnbx_options.setObjectName(_fromUtf8("btnbx_options"))
- self.grdlyt_dlg.addWidget(self.btnbx_options, 1, 0, 1, 1)
+ self.dialog_gridlayout.addWidget(self.toolbox, 0, 0, 1, 1)
+ self.options_buttonbox = XQDialogButtonBox(self)
+ self.options_buttonbox.setOrientation(Qt.Horizontal)
+ self.options_buttonbox.setStandardButtons(XQDialogButtonBox.Cancel)
+ self.options_buttonbox.setObjectName(_from_utf8("btnbx_options"))
+ self.dialog_gridlayout.addWidget(self.options_buttonbox, 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),
+ self.setWindowTitle(
QApplication.translate(
- "dlg_FormBearDist",
- "Step 1: Define Survey Plan",
- None,
+ "BearingDistanceFormDialog",
+ "Bearings and Distances Form",
+ 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),
+ self.plan_label.setText(
QApplication.translate(
- "dlg_FormBearDist",
- "Step 2: Define Reference Beacon",
- None,
+ "BearingDistanceFormDialog",
+ "Survey Plan",
+ None,
+ QApplication.UnicodeUTF8))
+ self.plan_next_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Next",
+ None,
+ QApplication.UnicodeUTF8))
+ self.toolbox.setItemText(
+ self.toolbox.indexOf(self.plan_widget),
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Step 1: Define Survey Plan",
+ None,
+ QApplication.UnicodeUTF8))
+ self.reference_layout.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Reference Beacon",
+ None,
+ QApplication.UnicodeUTF8))
+ self.reference_back_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Back",
+ None,
+ QApplication.UnicodeUTF8))
+ self.reference_next_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Next",
+ None,
+ QApplication.UnicodeUTF8))
+ self.toolbox.setItemText(
+ self.toolbox.indexOf(self.reference_widget),
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Step 2: Define Reference Beacon",
+ None,
+ QApplication.UnicodeUTF8))
+ self.chain_add_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Add Link",
+ None,
+ QApplication.UnicodeUTF8))
+ self.chain_edit_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Edit Link",
+ None,
+ QApplication.UnicodeUTF8))
+ self.chain_delete_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Delete Link",
+ None,
+ QApplication.UnicodeUTF8))
+ self.chain_back_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Back",
+ None,
+ QApplication.UnicodeUTF8))
+ self.chain_finish_pushbutton.setText(
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Finish",
+ None,
+ QApplication.UnicodeUTF8))
+ self.toolbox.setItemText(
+ self.toolbox.indexOf(self.chain_widget),
+ QApplication.translate(
+ "BearingDistanceFormDialog",
+ "Step 3: Define Bearings and Distances Chain",
+ 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)
- self.pshbtn_chain_finish.clicked.connect(lambda: self.checkItemBearDistChain(True))
- self.pshbtn_chain_back.clicked.connect(lambda: self.checkItemBearDistChain(False))
- self.pshbtn_ref_next.clicked.connect(lambda: self.checkItemReferenceBeacon(True))
- self.pshbtn_ref_back.clicked.connect(lambda: self.checkItemReferenceBeacon(False))
- self.pshbtn_plan_next.clicked.connect(lambda : self.checkItemSurveyPlan(True))
- self.pshbtn_chain_add.clicked.connect(self.addLink)
- self.pshbtn_chain_edt.clicked.connect(self.editLink)
- self.pshbtn_chain_del.clicked.connect(self.deleteLink)
+ self.options_buttonbox.accepted.connect(self.accept)
+ self.options_buttonbox.rejected.connect(self.reject)
+ self.chain_finish_pushbutton.clicked.connect(
+ lambda: self.check_item_bearing_distance_chain(True))
+ self.chain_back_pushbutton.clicked.connect(
+ lambda: self.check_item_bearing_distance_chain(False))
+ self.reference_next_pushbutton.clicked.connect(
+ lambda: self.check_item_reference_beacon(True))
+ self.reference_back_pushbutton.clicked.connect(
+ lambda: self.check_item_reference_beacon(False))
+ self.plan_next_pushbutton.clicked.connect(
+ lambda : self.check_item_survey_plan(True))
+ self.chain_add_pushbutton.clicked.connect(self.add_link)
+ self.chain_edit_pushbutton.clicked.connect(self.edit_link)
+ self.chain_delete_pushbutton.clicked.connect(self.delete_link)
QMetaObject.connectSlotsByName(self)
- def confirmBack(self):
+ def confirm_back(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
+ 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):
+class BearingDistanceLinkFormDialog(QDialog):
""" This dialog enables the user to add a bearing and distance link
"""
- def __init__(self, db, fromBeacons, query, values=[], parent = None):
+ def __init__(self, database, from_beacons, query, values=[], parent=None):
# initialize QDialog class
- super(dlg_FormBearDistLink, self).__init__(parent, Qt.WindowStaysOnTopHint)
+ super(BearingDistanceLinkFormDialog, self).\
+ __init__(parent, Qt.WindowStaysOnTopHint)
# initialize ui
- self.setupUi(fromBeacons)
+ self.setup_ui(from_beacons)
# initialize instance variables
- self.values_old = values
- self.values_new = []
- self.db = db
+ self.old_values = values
+ self.new_values = []
+ self.database = database
self.query = query
- self.fromBeacons = fromBeacons
+ self.from_beacons = from_beacons
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);",
- "UNIQUE":"background-color: rgba(107, 255, 107, 150);"
+ "EMPTY": "background-color: rgba(255, 107, 107, 150);",
+ "TYPE": "background-color: rgba(107, 107, 255, 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):
- self.populateForm(values)
+ if bool(values):
+ self.populate_form(values)
- def populateForm(self, values):
+ def populate_form(self, values):
""" Populte form with values given
"""
- for index, lnedt in enumerate(self.lnedts):
- if values[index] is not None:
- lnedt.setText(str(values[index]))
+ for index, line_edit in enumerate(self.line_edits):
+ if values[index] is not None:
+ line_edit.setText(str(values[index]))
- def getValues(self):
- return self.values_new
+ def get_values(self):
+ return self.new_values
- def executeOption(self, button):
+ def execute_option(self, button):
""" Perform validation and close the dialog
"""
- if self.btnbx_options.standardButton(button) == QDialogButtonBox.Save:
- values_new = []
- # check required fields
+ if self.options_buttonbox.standardButton(button) == \
+ QDialogButtonBox.Save:
+ new_values = []
+ # check required fields
valid = True
- for index,lnedt in enumerate(self.lnedts):
+ for index, line_edit in enumerate(self.line_edits):
if self.fields[index].required:
- if str(lnedt.text()).strip() is "":
- lnedt.setStyleSheet(self.colours["EMPTY"])
+ if str(line_edit.text()).strip() is "":
+ line_edit.setStyleSheet(self.colours["EMPTY"])
valid = False
- else: lnedt.setStyleSheet("")
- if not valid:
+ else:
+ line_edit.setStyleSheet("")
+ if not valid:
QMessageBox.information(
- self,
- "Empty Required Fields",
- "Please ensure that all required fields are completed."
- )
+ 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):
+ for index, line_edit in enumerate(self.line_edits):
try:
cast = self.fields[index].type
- txt = str(lnedt.text()).strip()
- if txt is "": tmp = None
- else: tmp = cast(txt)
- values_new.append(tmp)
- lnedt.setStyleSheet("")
+ text = str(line_edit.text()).strip()
+ if text is "":
+ temp = None
+ else:
+ temp = cast(text)
+ new_values.append(temp)
+ line_edit.setStyleSheet("")
except Exception as e:
- lnedt.setStyleSheet(self.colours["TYPE"])
+ line_edit.setStyleSheet(self.colours["TYPE"])
valid = False
- if not valid:
+ if not valid:
QMessageBox.information(
- self,
- "Invalid Field Types",
+ 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):
+ for index, line_edit in enumerate(self.line_edits):
if self.fields[index].name.lower() == "from":
- if str(lnedt.text()) not in self.fromBeacons:
- lnedt.setStyleSheet(self.colours["BEACON"])
+ if str(line_edit.text()) not in self.from_beacons:
+ line_edit.setStyleSheet(self.colours["BEACON"])
valid = False
- if not valid:
+ if not valid:
QMessageBox.information(
- self,
- "Invalid Reference",
- "Please ensure that specified beacons are valid."
- )
+ 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"])
+ for index, line_edit in enumerate(self.line_edits):
+ if self.fields[index].name.lower() == "to":
+ if bool(self.old_values):
+ if str(line_edit.text()) not in self.old_values:
+ if str(line_edit.text()) in self.from_beacons:
+ line_edit.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"])
+ elif not bool(self.old_values):
+ if str(line_edit.text()) in self.from_beacons:
+ line_edit.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"])
+ if bool(
+ int(self.database.query(
+ self.query % ('beacon', "%s"),
+ (str(line_edit.text()),))[0][0])):
+ line_edit.setStyleSheet(self.colours["UNIQUE"])
valid = False
break
- if not valid:
+ if not valid:
QMessageBox.information(
- self,
- "Invalid Reference",
- "Please ensure that the new beacon is unique."
- )
+ self,
+ "Invalid Reference",
+ "Please ensure that the new beacon is unique.")
return
# save values
- self.values_new = values_new
+ self.new_values = new_values
# accept dialog
self.accept()
else:
# reject dialog
self.reject()
- def setupUi(self, fromBeacons):
+ def setup_ui(self, from_beacons):
""" Initialize ui
"""
# define dialog
- self.gridLayout = QGridLayout(self)
+ self.grid_layout = QGridLayout(self)
self.setModal(True)
- self.gridLayout.setSizeConstraint(QLayout.SetFixedSize)
- self.formLayout = QFormLayout()
- self.lbls = []
- self.lnedts = []
+ self.grid_layout.setSizeConstraint(QLayout.SetFixedSize)
+ self.form_layout = QFormLayout()
+ self.labels = []
+ self.line_edits = []
self.fields = [
Field("Bearing", float, True, False),
Field("Distance", float, True, False),
@@ -1665,39 +2218,37 @@ def setupUi(self, fromBeacons):
Field("Location", str, False, False),
Field("Surveyor", str, False, False)
]
- for index, f in enumerate(self.fields):
- lbl = QLabel(self)
- self.formLayout.setWidget(index, QFormLayout.LabelRole, lbl)
- 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 f.name.lower() == "from":
+ for index, field in enumerate(self.fields):
+ label = QLabel(self)
+ self.form_layout.setWidget(index, QFormLayout.LabelRole, label)
+ label.setText(QApplication.translate(
+ "dlg_FormBearDistEntry",
+ ("*" if field.required else "") + field.name.title(),
+ None,
+ QApplication.UnicodeUTF8))
+ self.labels.append(label)
+ line_edit = QLineEdit(self)
+ self.form_layout.setWidget(index, QFormLayout.FieldRole, line_edit)
+ self.line_edits.append(line_edit)
+ if field.name.lower() == "from":
model = QStringListModel()
- model.setStringList(fromBeacons)
- completer = QCompleter()
+ model.setStringList(from_beacons)
+ completer = QCompleter()
completer.setCaseSensitivity(Qt.CaseInsensitive)
completer.setModel(model)
- lnedt.setCompleter(completer)
- self.gridLayout.addLayout(self.formLayout, 0, 0, 1, 1)
- self.btnbx_options = QDialogButtonBox(self)
- self.btnbx_options.setCursor(QCursor(Qt.PointingHandCursor))
- self.btnbx_options.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Save)
- self.gridLayout.addWidget(self.btnbx_options, 1, 0, 1, 1)
+ line_edit.setCompleter(completer)
+ self.grid_layout.addLayout(self.form_layout, 0, 0, 1, 1)
+ self.options_buttonbox = QDialogButtonBox(self)
+ self.options_buttonbox.setCursor(QCursor(Qt.PointingHandCursor))
+ self.options_buttonbox.setStandardButtons(
+ QDialogButtonBox.Cancel | QDialogButtonBox.Save)
+ self.grid_layout.addWidget(self.options_buttonbox, 1, 0, 1, 1)
# translate ui widgets' text
self.setWindowTitle(QApplication.translate(
- "dlg_FormBearDistEntry",
- "Link Form",
- None,
- QApplication.UnicodeUTF8
- ))
+ "dlg_FormBearDistEntry",
+ "Link Form",
+ None,
+ QApplication.UnicodeUTF8))
# connect ui widgets
- self.btnbx_options.clicked.connect(self.executeOption)
+ self.options_buttonbox.clicked.connect(self.execute_option)
QMetaObject.connectSlotsByName(self)
-
diff --git a/PyQt4Widgets.py b/PyQt4Widgets.py
old mode 100644
new mode 100755
index 59a3a4d..147e7ce
--- a/PyQt4Widgets.py
+++ b/PyQt4Widgets.py
@@ -21,13 +21,14 @@
from qgis.gui import *
from qgis.core import *
+
class XQPushButton(QPushButton):
def __init__(self, parent=None):
super(XQPushButton, self).__init__(parent)
self.setCursor(QCursor(Qt.PointingHandCursor))
+
class XQDialogButtonBox(QDialogButtonBox):
def __init__(self, parent=None):
super(XQDialogButtonBox, self).__init__(parent)
self.setCursor(QCursor(Qt.PointingHandCursor))
-
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index a225760..a283615
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
First developed by Afrispatial cc in 2012
-Sponsored by: SpatialMatrix, Lagos for Ogun State Government, Nigeria.
+Sponsored by: SpatialMatrix, Lagos for Ogun State Government, Nigeria [background presentation](https://drive.google.com/file/d/0B2pxNIZQUjL1TW5wR00zVC1aUjA/view?usp=sharing)
-Original authors: Robert Moerman, Admire Nyakudya, Gavin Fleming
+Contributors: Robert Moerman, Admire Nyakudya, Gavin Fleming, Muhammad Rohmat
Maintained by Kartoza (Pty) Ltd
@@ -18,13 +18,13 @@ Licence:
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.
+It caters for addition, modification and deletion of cadastral parcels.
## 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.
+Then properties are constructed by grouping beacons in a specific order to define a parcel polygon.
## Coordinates
@@ -32,23 +32,16 @@ Where bearings and distances are not available, coordinates can be used instead
## Dependencies
-This plugin depends on a PostGIS database with a predefined schema including tables, materialised views and triggers.
+This plugin depends on a PostGIS database with a predefined schema including tables, materialised views and triggers. This schema will be created on initial setup when first running the plugin.
-# User manual
+## Sample Data
-http://goo.gl/CY9TYn
+A sql script is provided which populates all the mandatory tables required by the plugin. In order to use the
+sample data create the database with a CRS: `26332`. A sample survey is provided as a guideline on how to capture records.
+# User manual
-# What's Next
-(Robert's old note - needs to be updated)
+[User manual](http://goo.gl/CY9TYn)
-- 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/__init__.py b/__init__.py
old mode 100644
new mode 100755
index 1e70051..3e33102
--- a/__init__.py
+++ b/__init__.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
- sml_surveyor
+ SMLSurveyor
A QGIS plugin
SML Surveyor Plugin
-------------------
@@ -25,25 +25,32 @@
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 "2.0"
+
def author():
return "AfriSpatial"
+
def email():
return "robert@afrispatial.co.za"
+
def classFactory(iface):
- # load sml_surveyor class from file sml_surveyor
- from plugin import sml_surveyor
- return sml_surveyor(iface)
+ # load SMLSurveyor class from file SMLSurveyor
+ from plugin import SMLSurveyor
+ return SMLSurveyor(iface)
diff --git a/constants.py b/constants.py
old mode 100644
new mode 100755
index 366612f..df8dbb6
--- a/constants.py
+++ b/constants.py
@@ -1,54 +1,54 @@
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 \
+ "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) \
+ "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) \
+ "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);",
"INSERT_GENERAL": "INSERT INTO parcel_def(parcel_id, beacon, sequence) \
VALUES %s;",
- "DELETE":"DELETE FROM parcel_def WHERE parcel_id = %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 \
+ "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 \
+ "EXIST_REFERENCEBEACON": "SELECT ref_beacon FROM survey where \
plan_no = %s;",
- "EXIST_BEARDISTCHAINS":"SELECT bd.bearing, bd.distance, \
+ "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 \
+ "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 \
+ "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) \
+ "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);"
+ "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/data/sample_survey.jpg b/data/sample_survey.jpg
new file mode 100644
index 0000000..e6c5ed3
Binary files /dev/null and b/data/sample_survey.jpg differ
diff --git a/data/sml_data.dump b/data/sml_data.dump
new file mode 100644
index 0000000..fc6c412
Binary files /dev/null and b/data/sml_data.dump differ
diff --git a/database.py b/database.py
old mode 100644
new mode 100755
index d9a0cea..6802432
--- a/database.py
+++ b/database.py
@@ -29,24 +29,43 @@ def __init__(self, name, type, required, unique):
class Manager:
- def __init__(self, params):
+ def __init__(self, parameters):
# test db settings
- self.params = params
- self.connect(params)
+ self.parameters = parameters
+ self.connection_name = parameters.get("CONNECTION")
+ self.connect(parameters)
self.disconnect()
- def connect(self, params):
+ def connect(self, parameters):
""" Create a backend postgres database connection
"""
try:
# 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
+ if not hasattr(self, 'conn') or self.connection is None:
+ if parameters.get("SERVICE"):
+ self.connection = psycopg2.connect(
+ "service='{SERVICE}' user='{USER}' "
+ "password='{PASSWORD}'".format(
+ SERVICE=parameters["SERVICE"],
+ USER=parameters["USER"],
+ PASSWORD=parameters["PASSWORD"]
+ ))
+ else:
+ self.connection = psycopg2.connect(
+ "host='{HOST}' dbname='{NAME}' user='{USER}' "
+ "password='{PASSWORD}' port='{PORT}'".format(
+ HOST=parameters["HOST"],
+ NAME=parameters["NAME"],
+ USER=parameters["USER"],
+ PASSWORD=parameters["PASSWORD"],
+ PORT=parameters["PORT"]))
+ # check if cursor object exists
if not hasattr(self, 'cursor') or self.cursor is None:
- self.cursor = self.conn.cursor()
+ self.cursor = self.connection.cursor()
except Exception as e:
- raise Exception('Could not connect to database!\nError raised: {error}.'.format(error = str(e)))
+ raise Exception(
+ 'Could not connect to database!\n'
+ 'Error raised: {error}.'.format(error=str(e)))
def disconnect(self):
""" Terminate a backend postgres database connection
@@ -57,18 +76,20 @@ def disconnect(self):
self.cursor.close()
self.cursor = None
# check if a connection object exists
- if hasattr(self, 'conn') and self.conn is not None:
- self.conn.close()
- self.conn = None
+ if hasattr(self, 'conn') and self.connection is not None:
+ self.connection.close()
+ self.connection = None
except Exception as e:
- raise Exception('Could not disconnect from database!\nError raised: {error}.'.format(error = str(e)))
+ raise Exception(
+ 'Could not disconnect from database!\n'
+ 'Error raised: {error}.'.format(error=str(e)))
def query(self, query, data=None):
""" Execute query using given data against connection object
@returns resultset (array structure)
"""
try:
- self.connect(self.params)
+ self.connect(self.parameters)
if data is None:
self.cursor.execute(query)
else:
@@ -78,18 +99,20 @@ def query(self, query, data=None):
records = self.cursor.fetchall()
except:
pass
- self.conn.commit()
+ self.connection.commit()
self.disconnect()
return records
except Exception as e:
- raise Exception('Backend database query failed!\nError raised: %s.' %(str(e),))
+ raise Exception(
+ 'Backend database query failed!\n'
+ 'Error raised: %s.' % (str(e),))
- def queryPreview(self, query, data=None, multi_data=False):
+ def preview_query(self, query, data=None, multi_data=False):
""" Preview query
@returns query (str)
"""
try:
- self.connect(self.params)
+ self.connect(self.parameters)
sql = ""
if data is None:
sql = self.cursor.mogrify(query)
@@ -103,25 +126,50 @@ def queryPreview(self, query, data=None, multi_data=False):
self.disconnect()
return sql
except Exception as e:
- raise Exception('Backend database mogrification failed!\nError raised: %s.' %(str(e),))
+ raise Exception(
+ 'Backend database mogrification failed!\n'
+ 'Error raised: %s.' % (str(e),))
- def getSchema(self, tbl_name, fld_ignore):
+ def get_schema(self, table_name, field_ignore):
""" Get information abot a specific table
@returns [, , ] (list)
"""
- 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))))]
+ 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=table_name,
+ ignore=", ".join(
+ "'%s'" % (i,) for i in field_ignore))))]
- def _pythonize_type(self, db_type):
+ def _pythonize_type(self, database_type):
""" Get python type
@returns type (type)
"""
- if "char" in db_type.lower(): return str
- elif "double" in db_type.lower(): return float
- elif "integer" in db_type.lower(): return int
- else: return object
-
+ if "char" in database_type.lower():
+ return str
+ elif "double" in database_type.lower():
+ return float
+ elif "integer" in database_type.lower():
+ return int
+ else:
+ return object
diff --git a/database_changes.sql b/database_changes.sql
deleted file mode 100644
index 0fc2ee5..0000000
--- a/database_changes.sql
+++ /dev/null
@@ -1,220 +0,0 @@
----- create tables
--- parcel_lookup
-CREATE TABLE parcel_lookup
-(
- id serial NOT NULL,
- parcel_id character varying(20) NOT NULL,
- available boolean NOT NULL DEFAULT true,
- CONSTRAINT parcel_lookup_pkey PRIMARY KEY (id ),
- CONSTRAINT parcel_lookup_parcel_id_key UNIQUE (parcel_id )
-)
-WITH (
- OIDS=FALSE
-);
----- create functions
-
---
-CREATE OR REPLACE FUNCTION parcel_lookup_availability_trigger()
- RETURNS trigger AS
-$BODY$
- BEGIN
- UPDATE parcel_lookup SET available = TRUE;
- UPDATE parcel_lookup SET available = FALSE WHERE parcel_id IN (SELECT DISTINCT parcel_id FROM parcel_def);
- RETURN NEW;
- END
-$BODY$
- LANGUAGE plpgsql VOLATILE
- COST 100;
---
-CREATE OR REPLACE FUNCTION parcel_lookup_define_parcel_trigger()
- RETURNS trigger AS
-$BODY$
- BEGIN
- IF (SELECT COUNT(*)::integer FROM parcel_lookup WHERE parcel_id = NEW.parcel_id) = 0 THEN
- INSERT INTO parcel_lookup (parcel_id) VALUES (NEW.parcel_id);
- END IF;
- RETURN NEW;
- END
-$BODY$
- LANGUAGE plpgsql VOLATILE
- COST 100;
----- create triggers
---
-DROP TRIGGER IF EXISTS insert_nodes_geom ON beacons;
-CREATE TRIGGER insert_nodes_geom
- BEFORE INSERT OR UPDATE
- ON beacons
- FOR EACH ROW
- EXECUTE PROCEDURE calc_point();
---
-DROP TRIGGER IF EXISTS parcel_lookup_availability ON parcel_def;
-CREATE TRIGGER parcel_lookup_availability
- AFTER INSERT OR UPDATE OR DELETE
- ON parcel_def
- FOR EACH ROW
- EXECUTE PROCEDURE parcel_lookup_availability_trigger();
---
-DROP TRIGGER IF EXISTS parcel_lookup_define_parcel ON parcel_def;
-CREATE TRIGGER parcel_lookup_define_parcel
- BEFORE INSERT OR UPDATE
- ON parcel_def
- FOR EACH ROW
- EXECUTE PROCEDURE parcel_lookup_define_parcel_trigger();
----- create database constraints
-ALTER TABLE parcel_def ADD CONSTRAINT parcel_def_beacon_fkey FOREIGN KEY (beacon)
- REFERENCES beacons (beacon) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE parcel_def ADD CONSTRAINT parcel_def_parcel_id_fkey FOREIGN KEY (parcel_id)
- REFERENCES parcel_lookup (parcel_id) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-
---insert into parcel_lookup (parcel_id,available) VALUES (8,'f'),(9,'f'),(1,'f');
-
-
----- bearing and distance stuff
---
-ALTER TABLE survey ADD UNIQUE (plan_no);
-ALTER TABLE beardist ADD FOREIGN KEY (beacon_from) REFERENCES beacons (beacon) MATCH FULL ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE beardist ADD FOREIGN KEY (beacon_to) REFERENCES beacons (beacon) MATCH FULL ON UPDATE CASCADE ON DELETE CASCADE;
-ALTER TABLE beardist ADD FOREIGN KEY (plan_no) REFERENCES survey (plan_no) ON UPDATE CASCADE ON DELETE CASCADE;
---
-CREATE OR REPLACE FUNCTION pointfrombearinganddistance(dstarte double precision, dstartn double precision, dbearing double precision, ddistance double precision, "precision" integer, srid integer)
- RETURNS geometry AS
-$BODY$
- DECLARE
- dangle1 double precision;
- dangle1rad double precision;
- ddeltan double precision;
- ddeltae double precision;
- dende double precision;
- dendn double precision;
- "precision" int;
- srid int;
-BEGIN
- precision := CASE WHEN precision IS NULL THEN 3 ELSE precision END;
- srid := CASE WHEN srid IS NULL THEN 4326 ELSE srid END;
- BEGIN
- IF
- dstarte IS NULL OR
- dstartn IS NULL OR
- dbearing IS NULL OR
- ddistance IS NULL
- THEN RETURN NULL;
- END IF;
- -- First calculate ddeltae and ddeltan
- IF dbearing < 90
- THEN
- dangle1 := 90 - dbearing;
- dangle1rad := dangle1 * PI() / 180;
- ddeltae := Cos(dangle1rad) * ddistance;
- ddeltan := Sin(dangle1rad) * ddistance;
- END if;
- IF dbearing < 180
- THEN
- dangle1 := dbearing - 90;
- dangle1rad := dangle1 * PI() / 180;
- ddeltae := Cos(dangle1rad) * ddistance;
- ddeltan := Sin(dangle1rad) * ddistance * -1;
- END if;
- IF dbearing < 270
- THEN
- dangle1 := 270 - dbearing;
- dangle1rad := dangle1 * PI() / 180;
- ddeltae := Cos(dangle1rad) * ddistance * -1;
- ddeltan := Sin(dangle1rad) * ddistance * -1;
- END if;
- IF dbearing <= 360
- THEN
- dangle1 := dbearing - 270;
- dangle1rad := dangle1 * PI() / 180;
- ddeltae := Cos(dangle1rad) * ddistance * -1;
- ddeltan := Sin(dangle1rad) * ddistance;
- END IF;
- -- Calculate the easting and northing of the end point
- dende := ddeltae + dstarte;
- dendn := ddeltan + dstartn;
- RETURN ST_SetSRID(ST_MakePoint(round(dende::numeric, precision), round(dendn::numeric, precision)), 26331);
- END;
-END;
-$BODY$
- LANGUAGE plpgsql VOLATILE
- COST 100;
---
-CREATE OR REPLACE FUNCTION public.beardistinsert(
- arg_plan_no character varying(20),
- arg_bearing double precision,
- arg_distance double precision,
- arg_beacon_from character varying(20),
- arg_beacon_to character varying(20),
- arg_location character varying(50),
- arg_name character varying(20)
-)
- RETURNS void AS
-$BODY$
- DECLARE
- the_x double precision;
- the_y double precision;
- the_geom geometry(Point,26331);
- BEGIN
- -- calculate geometry from bearing and distance
- SELECT x INTO the_x FROM beacons WHERE beacon = arg_beacon_from;
- SELECT y INTO the_y FROM beacons WHERE beacon = arg_beacon_from;
- the_geom := pointfrombearinganddistance(the_x, the_y, arg_bearing, arg_distance, 3, 26331);
- -- insert record into beacons table
- INSERT INTO beacons(beacon, y, x, "location", "name")
- VALUES(arg_beacon_to, st_y(the_geom), st_x(the_geom), arg_location, arg_name);
- -- insert record into beardist table
- INSERT INTO beardist(plan_no, bearing, distance, beacon_from, beacon_to)
- VALUES(arg_plan_no, arg_bearing, arg_distance, arg_beacon_from, arg_beacon_to);
- END
-$BODY$
- LANGUAGE plpgsql VOLATILE
- COST 100;
---
-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
- -- get id of old record in beardist table
- 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;
- -- get id of old record in beacon table
- 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;
- -- calculate geometry from bearing and distance
- 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 table record
- 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 table record
- 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;
-
diff --git a/database_changes_v2.sql b/database_changes_v2.sql
deleted file mode 100644
index 47434db..0000000
--- a/database_changes_v2.sql
+++ /dev/null
@@ -1,252 +0,0 @@
-
-
-ALTER TABLE parcel_lookup ADD COLUMN scheme integer;
-ALTER TABLE parcel_lookup ADD COLUMN block character varying;
-ALTER TABLE parcel_lookup ADD COLUMN local_govt integer;
---ALTER TABLE parcel_lookup DROP COLUMN prop_type;
-ALTER TABLE parcel_lookup ADD COLUMN prop_type character varying;
-ALTER TABLE parcel_lookup ADD COLUMN file_number character varying;
-ALTER TABLE parcel_lookup ADD COLUMN allocation integer;
-ALTER TABLE parcel_lookup ADD COLUMN manual_no character varying;
-ALTER TABLE parcel_lookup ADD COLUMN deeds_file character varying;
-
---remove any non integer parcel_id's to temporary 'manual_no' field
---'convert' parcel_id from char var to int field
-
-update parcel_lookup set manual_no = parcel_id;
-
---update parcel_def p set parcel_id = pl.parcel_id from parcel_lookup pl where pl.manual_no = p.parcel_id
-
-ALTER TABLE parcel_lookup RENAME id TO serial;
-ALTER TABLE parcel_lookup RENAME serial TO plot_sn;
-ALTER TABLE parcel_lookup
- ALTER COLUMN plot_sn DROP DEFAULT;
-COMMENT ON COLUMN parcel_lookup.plot_sn IS 'plot serial no within a block. Forms part of the parcel no';
-
-
-ALTER TABLE parcel_lookup DROP CONSTRAINT parcel_lookup_pkey;
-ALTER TABLE parcel_lookup ADD PRIMARY KEY (parcel_id);
---ALTER TABLE parcel_lookup DROP CONSTRAINT parcel_lookup_parcel_id_key;
-
-
-update parcel_lookup set parcel_id = serial;
-
----------------------
-ALTER TABLE parcel_lookup
- ADD COLUMN parcel_temp integer;
-
-update parcel_lookup set parcel_temp = parcel_id::integer;
-
-ALTER TABLE parcel_def drop constraint parcel_def_parcel_id_fkey;
-
-ALTER TABLE parcel_lookup DROP COLUMN parcel_id;
-
-ALTER TABLE parcel_lookup RENAME parcel_temp TO parcel_id;
-
-ALTER TABLE parcel_def
- ADD COLUMN parcel_temp integer;
-
- DROP TRIGGER parcel_lookup_define_parcel ON public.parcel_def;
-DROP TRIGGER parcel_lookup_availability_trigger ON public.parcel_def;
-
-update parcel_def set parcel_temp = parcel_id::integer;
-
-DROP VIEW parcels;
-
-
-
-ALTER TABLE parcel_def DROP COLUMN parcel_id;
-
-ALTER TABLE parcel_def RENAME parcel_temp TO parcel_id;
-
---create vew parcels...
---add constraint parcel_def_parcel_id_fkey
---set parcel_id as pk of parcel_lookup
---set permissions
---reinstate triggers
---refresh parcels on geoserver and 1Map
-CREATE SEQUENCE parcel_lookup_parcel_id_seq;
-ALTER TABLE parcel_lookup
- ALTER COLUMN parcel_id SET DEFAULT nextval('parcel_lookup_parcel_id_seq');
-
-ALTER SEQUENCE parcel_lookup_parcel_id_seq OWNED BY parcel_lookup.parcel_id;
---then set sequence to start in right place.
-
--------------------------------
-
-CREATE TABLE schemes
-(
- id serial NOT NULL,
- scheme_name character varying(50) NOT NULL,
- CONSTRAINT schemes_pkey PRIMARY KEY (id ),
- CONSTRAINT schemes_id_key UNIQUE (scheme_name)
-)
-WITH (
- OIDS=FALSE
-);
-
-CREATE TABLE local_govt
-(
- id serial NOT NULL,
- local_govt_name character varying(50) NOT NULL,
- CONSTRAINT local_govt_pkey PRIMARY KEY (id ),
- CONSTRAINT local_govt_id_key UNIQUE (local_govt_name )
-)
-WITH (
- OIDS=FALSE
-);
-
-
---DROP TABLE prop_types;
-CREATE TABLE prop_types
-(
- id serial NOT NULL,
- code character varying(2) NOT NULL,
- prop_type_name character varying(50) NOT NULL,
- CONSTRAINT prop_type_pkey PRIMARY KEY (id ),
- CONSTRAINT prop_type_id_key UNIQUE (prop_type_name ),
- CONSTRAINT prop_type_code_key UNIQUE (code )
-)
-WITH (
- OIDS=FALSE
-);
-
-CREATE TABLE allocation_cat
-(
- allocation_cat integer NOT NULL,
- description character varying(50) NOT NULL,
- CONSTRAINT allocation_cat_pkey PRIMARY KEY (allocation_cat )
-)
-WITH (
- OIDS=FALSE
-);
-
---int4(row_number() OVER (ORDER BY vl.parcel_id)) AS gid,
---drop view parcels;
-CREATE OR REPLACE VIEW parcels AS
- SELECT parcel.*, round(st_area(parcel.the_geom)::numeric,3)::double precision AS comp_area,
- description.official_area,description.parcel_number, description.block, description.serial_no,
- description.scheme, description.file_number,description.allocation,description.owner,
- ''||
- description.deeds_file||'' AS deeds_file FROM
- (SELECT int4(vl.parcel_id) as parcel_id,
- st_makepolygon(st_addpoint(st_makeline(vl.the_geom),st_startpoint(st_makeline(vl.the_geom))))::geometry(Polygon,26331) AS the_geom
- FROM ( SELECT pd.id, pd.parcel_id, pd.beacon, pd.sequence, b.the_geom
- FROM beacons b
- JOIN parcel_def pd ON b.beacon::text = pd.beacon::text
- ORDER BY pd.parcel_id, pd.sequence) vl
- GROUP BY vl.parcel_id
- HAVING st_npoints(st_collect(vl.the_geom)) > 1) AS parcel
- INNER JOIN
- (SELECT p.parcel_id,p.local_govt || p.prop_type || p.parcel_id AS parcel_number, p.allocation,p.block,p.plot_sn AS serial_no,p.official_area,
- s.scheme_name AS scheme,p.file_number,d.grantee AS owner,p.deeds_file
- FROM parcel_lookup p LEFT JOIN deeds d ON p.file_number=d.fileno LEFT JOIN schemes s ON p.scheme = s.id) AS description
- USING (parcel_id);
-
-GRANT SELECT ON TABLE public.parcels TO GROUP web_read;
-
-
---ALTER TABLE parcel_lookup ADD UNIQUE (parcel_id);
---instead make parcel_id the PK
-
-ALTER TABLE parcel_def ADD CONSTRAINT parcel_def_parcel_id_fkey FOREIGN KEY (parcel_id)
- REFERENCES parcel_lookup (parcel_id) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-
-
-
-
-ALTER TABLE parcel_lookup ADD CONSTRAINT parcel_lookup_scheme_id_fkey FOREIGN KEY (scheme)
- REFERENCES schemes (id) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-
-ALTER TABLE parcel_lookup ADD CONSTRAINT parcel_lookup_local_govt_id_fkey FOREIGN KEY (local_govt)
- REFERENCES local_govt (id) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-
---ALTER TABLE parcel_lookup DROP CONSTRAINT parcel_lookup_prop_type_id_fkey
-ALTER TABLE parcel_lookup ADD CONSTRAINT parcel_lookup_prop_type_id_fkey FOREIGN KEY (prop_type)
- REFERENCES prop_types (code) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-
-ALTER TABLE parcel_lookup ADD CONSTRAINT parcel_lookup_allocation_id_fkey FOREIGN KEY (allocation)
- REFERENCES allocation_cat (allocation_cat) MATCH FULL
- ON UPDATE CASCADE ON DELETE CASCADE;
-
-
-INSERT INTO allocation_cat (allocation_cat,description) VALUES
-(0 ,'free and unallocated parcel'),
-(1 ,'temporary allocation pending approval'),
-(2 ,'parcel allocated and approved');
-
-INSERT INTO schemes (scheme_name) VALUES
-('Olusegun Obasanjo Hilltop GRA Layout');
-
-INSERT INTO local_govt (local_govt_name) VALUES
-('Abeokuta South');
-
-INSERT INTO prop_types (code,prop_type_name) VALUES
-('AL','Allocation');
-
---load deeds
---shp2pgsql -n -c deeds_sample.dbf deeds | psql -d sml
-
-
-ALTER TABLE parcel_lookup
- ALTER COLUMN local_govt SET NOT NULL;
-ALTER TABLE parcel_lookup
- ALTER COLUMN prop_type SET NOT NULL;
-
-
-
---change parcel_id to integer field!
-
---fix 26331 transformation
-
-create table temp_proj as select * from spatial_ref_sys where srid = 26331;
---select * from temp_proj;
---select proj4text from temp_proj;
-update temp_proj set srid = 263310;
---delete from spatial_ref_sys where srid = 263310
-insert into spatial_ref_sys (select * from temp_proj);
-
-update spatial_ref_sys set
-proj4text = '+proj=utm +zone=31 +ellps=clrk80 +towgs84=-111.92, -87.85, 114.5, 1.875, 0.202, 0.219, 0.032 +units=m +no_defs',
-srtext = 'PROJCS["Minna / UTM zone 31N", GEOGCS["Minna", DATUM["Minna", SPHEROID["Clarke 1880 (RGS)", 6378249.145, 293.465, AUTHORITY["EPSG","7012"]], TOWGS84[-111.92, -87.85, 114.5, 1.875, 0.202, 0.219, 0.032], AUTHORITY["EPSG","6263"]], PRIMEM["Greenwich", 0.0, AUTHORITY["EPSG","8901"]], UNIT["degree", 0.017453292519943295], AXIS["Geodetic longitude", EAST], AXIS["Geodetic latitude", NORTH], AUTHORITY["EPSG","4263"]], PROJECTION["Transverse_Mercator", AUTHORITY["EPSG","9807"]], PARAMETER["central_meridian", 3.0], PARAMETER["latitude_of_origin", 0.0], PARAMETER["scale_factor", 0.9996], PARAMETER["false_easting", 500000.0], PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["Easting", EAST], AXIS["Northing", NORTH], AUTHORITY["EPSG","26331"]]'
-WHERE srid = 26331;
-
-drop table temp_proj;
-
---revert the above, back to original transformation:
-update spatial_ref_sys set
-proj4text = (select proj4text from spatial_ref_sys where srid = 263310),
-srtext = (select proj4text from spatial_ref_sys where srid = 263310)
-WHERE srid = 26331;
-DELETE from spatial_ref_sys where srid = 263310;
-
-ALTER TABLE parcel_def
- ALTER COLUMN parcel_id SET NOT NULL;
-
---add area to parcels and scheme to survey
-
-ALTER TABLE survey ADD COLUMN scheme integer;
-ALTER TABLE beacons RENAME geom TO the_geom;
-
- ALTER TABLE survey ADD FOREIGN KEY (scheme) REFERENCES schemes (id) ON UPDATE NO ACTION ON DELETE NO ACTION;
-
-ALTER TABLE parcel_lookup ADD COLUMN official_area double precision;
-
-ALTER TABLE survey ADD FOREIGN KEY (ref_beacon) REFERENCES beacons (beacon) ON UPDATE NO ACTION ON DELETE NO ACTION;
-
---convert plot_sn from integer to character_varying so ids like '7A' can be added
-
-drop view parcels;
-
-alter table parcel_lookup alter column plot_sn type character varying;
-
-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/database_params.py.tmpl b/database_params.py.tmpl
old mode 100644
new mode 100755
index ff0765a..aaf8d5b
--- a/database_params.py.tmpl
+++ b/database_params.py.tmpl
@@ -1 +1 @@
-DATABASE_PARAMS = {"USER": "gavin", "HOST": "localhost", "PASSWORD": "s0sten1ble", "PORT": "5432", "NAME": "sml"}
\ No newline at end of file
+DATABASE_PARAMS = {"USER": "xxxxxx", "HOST": "localhost", "PASSWORD": "xxxxxxx", "PORT": "5432", "NAME": "xxxxxx"}
\ No newline at end of file
diff --git a/documents/qgis_projects/Arrow_06.svg b/documents/qgis_projects/Arrow_06.svg
new file mode 100644
index 0000000..c85488c
--- /dev/null
+++ b/documents/qgis_projects/Arrow_06.svg
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/documents/qgis_projects/print_survey_utm31.qgs b/documents/qgis_projects/print_survey_utm31.qgs
new file mode 100644
index 0000000..e850b32
--- /dev/null
+++ b/documents/qgis_projects/print_survey_utm31.qgs
@@ -0,0 +1,3467 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ meters
+
+ 887781.24173864163458347
+ 1068874.17464565695263445
+ 887843.08344954997301102
+ 1068943.78000281355343759
+
+ 0
+ 1
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ 0
+
+
+
+
+ - parcels20170712111928263
+ - print_survey_details20170712112130207
+ - derived_boundaries20170712112513911
+ - boundary_labels20170712112612040
+ - boundary_labels_degrees20170712112713152
+ - boundary_labels_minutes20170712112713163
+ - beacons_intersect20170712112923776
+ - derived_boundaries_degrees20170712112923785
+ - derived_boundaries_minutes20170712112923793
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ beacons_intersect20170712112923776
+ service='sml' sslmode=disable key='beacon' srid=26331 type=Point table="public"."beacons_intersect" (the_geom) sql=
+
+
+
+ beacons_intersect
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+ 0
+ /tmp
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boundary_labels20170712112612040
+ service='sml' sslmode=disable key='id' srid=26331 type=LineString table="public"."boundary_labels" (geom) sql=
+
+
+
+ boundary_labels (distance)
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boundary_labels_degrees20170712112713152
+ service='sml' sslmode=disable key='id' srid=26331 type=Point table="public"."boundary_labels_degrees" (geom) sql=
+
+
+
+ boundary_labels (Degrees)
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boundary_labels_minutes20170712112713163
+ service='sml' sslmode=disable key='id' srid=26331 type=Point table="public"."boundary_labels_minutes" (geom) sql=
+
+
+
+ boundary_labels (Minutes)
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "id", '<NULL>' )
+
+
+ derived_boundaries20170712112513911
+ service='sml' sslmode=disable key='id' srid=26331 type=LineString table="public"."derived_boundaries" (geom) sql=
+
+
+
+ derived_boundaries (distance)
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ derived_boundaries_degrees20170712112923785
+ service='sml' sslmode=disable key='id' srid=26331 type=Point table="public"."derived_boundaries_degrees" (geom) sql=
+
+
+
+ derived_boundaries(Degrees)
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ derived_boundaries_minutes20170712112923793
+ service='sml' sslmode=disable key='id' srid=26331 type=Point table="public"."derived_boundaries_minutes" (geom) sql=
+
+
+
+ derived_boundaries(Minutes)
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "parcel_id", '<NULL>' )
+
+
+ parcels20170712111928263
+ service='sml' sslmode=disable key='parcel_id' srid=26331 type=Polygon table="public"."parcels" (the_geom) sql=
+
+
+
+ parcels
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2120
+ 26331
+ EPSG:26331
+ Minna / UTM zone 31N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ area_name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "parcel_id", '<NULL>' )
+
+
+ print_survey_details20170712112130207
+ service='sml' sslmode=disable key='id' table="public"."print_survey_details" sql=
+
+
+
+ print_survey_details
+
+
+ +proj=longlat +datum=WGS84 +no_defs
+ 3452
+ 4326
+ EPSG:4326
+ WGS 84
+ longlat
+ WGS84
+ true
+
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 2
+ true
+ MU
+
+
+
+ false
+
+
+ false
+
+ clrk80
+
+ 8
+ false
+
+
+
+
+
+
+ 0
+ 255
+ 255
+ 255
+ 255
+ 255
+ 255
+
+
+ 2
+
+ parcels20170712111928263
+ derived_boundaries20170712112513911
+ boundary_labels20170712112612040
+ boundary_labels_degrees20170712112713152
+ boundary_labels_minutes20170712112713163
+ beacons_intersect20170712112923776
+ derived_boundaries_degrees20170712112923785
+ derived_boundaries_minutes20170712112923793
+
+
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+
+ advanced
+
+
+ 2
+ 2
+ 2
+ 2
+ 2
+ 2
+ 2
+ 2
+
+
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+
+ off
+ 0
+
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+
+
+
+
+
+
+
+
+ None
+
+
+
+ +proj=utm +zone=31 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ EPSG:26331
+ 2120
+ 1
+
+
+
+
+
+ true
+ 255
+
+
+ conditions unknown
+ 90
+
+ meters
+ m2
+
+
+
+
+
diff --git a/documents/qgis_projects/print_survey_utm32.qgs b/documents/qgis_projects/print_survey_utm32.qgs
new file mode 100644
index 0000000..491e4ae
--- /dev/null
+++ b/documents/qgis_projects/print_survey_utm32.qgs
@@ -0,0 +1,3508 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ meters
+
+ 229196.64356050436617807
+ 1067862.50845500966534019
+ 229257.2320645724539645
+ 1067898.35757301794365048
+
+ 0
+ 1
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - print_survey_details20170512140852955
+ - parcels20170710213358945
+ - derived_boundaries20170710213649838
+ - boundary_labels20170710213834263
+ - beacons_intersect20170710221313411
+ - boundary_labels_degrees20170711125507457
+ - boundary_labels_minutes20170711125854538
+ - derived_boundaries_degrees20170711131221200
+ - derived_boundaries_minutes20170711131615957
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ beacons_intersect20170710221313411
+ service='sml' key='beacon' srid=26332 type=Point table="public"."beacons_intersect" (the_geom) sql=
+
+
+
+ beacons_intersect
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+ 0
+ /tmp
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boundary_labels20170710213834263
+ service='sml' key='id' srid=26332 type=LineString table="public"."boundary_labels" (geom) sql=
+
+
+
+ boundary_labels (distance)
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boundary_labels_degrees20170711125507457
+ service='sml' key='id' srid=26332 type=Point table="public"."boundary_labels_degrees" (geom) sql=
+
+
+
+ boundary_labels (Degrees)
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boundary_labels_minutes20170711125854538
+ service='sml' key='id' srid=26332 type=Point table="public"."boundary_labels_minutes" (geom) sql=
+
+
+
+ boundary_labels (Minutes)
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "id", '<NULL>' )
+
+
+ derived_boundaries20170710213649838
+ service='sml' key='id' srid=26332 type=LineString table="public"."derived_boundaries" (geom) sql=
+
+
+
+ derived_boundaries (distance)
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ derived_boundaries_degrees20170711131221200
+ service='sml' key='id' srid=26332 type=Point table="public"."derived_boundaries_degrees" (geom) sql=
+
+
+
+ derived_boundaries(Degrees)
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ derived_boundaries_minutes20170711131615957
+ service='sml' key='id' srid=26332 type=Point table="public"."derived_boundaries_minutes" (geom) sql=
+
+
+
+ derived_boundaries(Minutes)
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "parcel_id", '<NULL>' )
+
+
+
+ 229210.17499999998835847
+ 1067866.89800000004470348
+ 229239.85999999998603016
+ 1067895.83100000000558794
+
+ parcels20170710213358945
+ service='sml' key='parcel_id' srid=26332 type=Polygon table="public"."parcels" (the_geom) sql=
+
+
+
+ parcels
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ 2121
+ 26332
+ EPSG:26332
+ Minna / UTM zone 32N
+ utm
+ clrk80
+ false
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ area_name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "parcel_id", '<NULL>' )
+
+
+ print_survey_details20170512140852955
+ service='sml' key='id' table="public"."print_survey_details" sql=
+
+
+
+ print_survey_details
+
+
+ +proj=longlat +datum=WGS84 +no_defs
+ 3452
+ 4326
+ EPSG:4326
+ WGS 84
+ longlat
+ WGS84
+ true
+
+
+
+ postgres
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE("id", '<NULL>')
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+ false
+
+
+
+
+
+ 2
+ true
+ MU
+
+
+
+ false
+
+
+ false
+
+ clrk80
+
+ 8
+ false
+
+
+
+
+
+
+ 0
+ 255
+ 255
+ 255
+ 255
+ 255
+ 255
+
+
+ 2
+
+ beacons_intersect20170710221313411
+ boundary_labels20170710213834263
+ derived_boundaries20170710213649838
+ parcels20170710213358945
+ boundary_labels_degrees20170711125507457
+ boundary_labels_minutes20170711125854538
+ derived_boundaries_degrees20170711131221200
+ derived_boundaries_minutes20170711131615957
+
+
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+ disabled
+
+ advanced
+
+
+ 2
+ 2
+ 2
+ 2
+ 2
+ 2
+ 2
+ 2
+
+
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+ to_vertex_and_segment
+
+ off
+ 0
+
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+ 0.000000
+
+
+
+
+
+
+
+
+ None
+
+
+
+ +proj=utm +zone=32 +ellps=clrk80 +towgs84=-92,-93,122,0,0,0,0 +units=m +no_defs
+ EPSG:26332
+ 2121
+ 1
+
+
+
+
+
+ true
+ 255
+
+
+ conditions unknown
+ 90
+
+ meters
+ m2
+
+
+
+
+
diff --git a/documents/qgis_projects/templates/Rectification.qpt b/documents/qgis_projects/templates/Rectification.qpt
new file mode 100644
index 0000000..9591f0e
--- /dev/null
+++ b/documents/qgis_projects/templates/Rectification.qpt
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/documents/qgis_projects/templates/government_survey.qpt b/documents/qgis_projects/templates/government_survey.qpt
new file mode 100644
index 0000000..24fa51f
--- /dev/null
+++ b/documents/qgis_projects/templates/government_survey.qpt
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/documents/qgis_projects/templates/private_survey_regularization.qpt b/documents/qgis_projects/templates/private_survey_regularization.qpt
new file mode 100644
index 0000000..312200c
--- /dev/null
+++ b/documents/qgis_projects/templates/private_survey_regularization.qpt
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/documents/styles/beacons.qml b/documents/styles/beacons.qml
old mode 100644
new mode 100755
diff --git a/documents/styles/derived_boundaries_degrees.qml b/documents/styles/derived_boundaries_degrees.qml
new file mode 100644
index 0000000..df6ed6d
--- /dev/null
+++ b/documents/styles/derived_boundaries_degrees.qml
@@ -0,0 +1,150 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+ COALESCE( "parcel_id", '<NULL>' )
+ 0
+
diff --git a/documents/styles/derived_boundaries_distance.qml b/documents/styles/derived_boundaries_distance.qml
new file mode 100644
index 0000000..c17a88a
--- /dev/null
+++ b/documents/styles/derived_boundaries_distance.qml
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
diff --git a/documents/styles/derived_boundaries_minutes.qml b/documents/styles/derived_boundaries_minutes.qml
new file mode 100644
index 0000000..52a5d92
--- /dev/null
+++ b/documents/styles/derived_boundaries_minutes.qml
@@ -0,0 +1,271 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ parcel_id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
diff --git a/documents/styles/label_boundary_degrees.qml b/documents/styles/label_boundary_degrees.qml
new file mode 100644
index 0000000..3a436f9
--- /dev/null
+++ b/documents/styles/label_boundary_degrees.qml
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
diff --git a/documents/styles/label_boundary_distance.qml b/documents/styles/label_boundary_distance.qml
new file mode 100644
index 0000000..b7c2bd2
--- /dev/null
+++ b/documents/styles/label_boundary_distance.qml
@@ -0,0 +1,346 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
diff --git a/documents/styles/label_boundary_minutes.qml b/documents/styles/label_boundary_minutes.qml
new file mode 100644
index 0000000..3189d19
--- /dev/null
+++ b/documents/styles/label_boundary_minutes.qml
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 0
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .
+
+ 0
+ .
+
+ 0
+ generatedlayout
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
diff --git a/documents/styles/parcel_area_errors.qml b/documents/styles/parcel_area_errors.qml
old mode 100644
new mode 100755
diff --git a/documents/styles/parcel_lookup.qml b/documents/styles/parcel_lookup.qml
old mode 100644
new mode 100755
diff --git a/documents/styles/parcels.qml b/documents/styles/parcels.qml
old mode 100644
new mode 100755
diff --git a/documents/styles/parcels.sld b/documents/styles/parcels.sld
old mode 100644
new mode 100755
diff --git a/documents/styles/parcels_18.qml b/documents/styles/parcels_18.qml
old mode 100644
new mode 100755
diff --git a/documents/styles/survey.qml b/documents/styles/survey.qml
old mode 100644
new mode 100755
diff --git a/help/Makefile b/help/Makefile
deleted file mode 100644
index ebb0236..0000000
--- a/help/Makefile
+++ /dev/null
@@ -1,130 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = build
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
-
-help:
- @echo "Please use \`make ' where is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/templateclass.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/templateclass.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/templateclass"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/templateclass"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- make -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/help/make.bat b/help/make.bat
deleted file mode 100644
index 90dc191..0000000
--- a/help/make.bat
+++ /dev/null
@@ -1,155 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^` where ^ is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. changes to make an overview over all changed/added/deprecated items
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\templateclass.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\templateclass.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-:end
diff --git a/help/source/conf.py b/help/source/conf.py
deleted file mode 100644
index 765f798..0000000
--- a/help/source/conf.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# sml_surveyor documentation build configuration file, created by
-# sphinx-quickstart on Sun Feb 12 17:11:03 2012.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.todo', 'sphinx.ext.pngmath', 'sphinx.ext.viewcode']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'sml_surveyor'
-copyright = u'2012, AfriSpatial'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '0.1'
-# The full version, including alpha/beta/rc tags.
-release = '0.1'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = []
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# " v documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'templateclassdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'sml_surveyor.tex', u'sml_surveyor Documentation',
- u'AfriSpatial', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'templateclass', u'sml_surveyor Documentation',
- [u'AfriSpatial'], 1)
-]
diff --git a/help/source/index.rst b/help/source/index.rst
deleted file mode 100644
index 499325f..0000000
--- a/help/source/index.rst
+++ /dev/null
@@ -1,20 +0,0 @@
-.. sml_surveyor documentation master file, created by
- sphinx-quickstart on Sun Feb 12 17:11:03 2012.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to sml_surveyor's documentation!
-============================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
diff --git a/icon.png b/icon.png
old mode 100644
new mode 100755
diff --git a/images/beacon.gif b/images/beacon.gif
old mode 100644
new mode 100755
diff --git a/images/beardist.png b/images/beardist.png
old mode 100644
new mode 100755
diff --git a/images/database.png b/images/database.png
new file mode 100644
index 0000000..0547ff8
Binary files /dev/null and b/images/database.png differ
diff --git a/images/icons/add.png b/images/icons/add.png
new file mode 100644
index 0000000..78f9700
Binary files /dev/null and b/images/icons/add.png differ
diff --git a/images/icons/refresh.png b/images/icons/refresh.png
new file mode 100644
index 0000000..29a6168
Binary files /dev/null and b/images/icons/refresh.png differ
diff --git a/images/parcel.png b/images/parcel.png
old mode 100644
new mode 100755
diff --git a/metadata.txt b/metadata.txt
old mode 100644
new mode 100755
diff --git a/plugin.py b/plugin.py
index 3fbf55d..a317a67 100644
--- a/plugin.py
+++ b/plugin.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
- sml_surveyor
+ SMLSurveyor
A QGIS plugin
SML Surveyor Plugin
-------------------
@@ -33,19 +33,21 @@
import database
from constants import *
from datetime import datetime
+from utilities import validate_plugin_actions
+
class RequiredLayer:
- def __init__(self,
- name,
- name_plural,
- table,
- primary_key,
- geometry_type,
- geometry_column='the_geom',
- schema='public',
- layer=None
- ):
+ 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
@@ -63,7 +65,7 @@ def __init__(self, actor, action):
self.action = action
-class sml_surveyor:
+class SMLSurveyor:
def __init__(self, iface):
# save reference to the QGIS interface
@@ -71,551 +73,707 @@ def __init__(self, iface):
# get plugin directory
self.plugin_dir = os.path.dirname(os.path.realpath(__file__))
self.uri = None
- self.db = None
+ self.connection = None
+ self.crs = None
+ self.database = None
self.datetime = datetime.now()
- self.requiredLayers = []
+ self.required_layers = []
# 1. beacons
# 2. parcels
- self.requiredLayers.append(RequiredLayer(
+ self.required_layers.append(RequiredLayer(
'Beacon', 'Beacons', 'beacons', 'gid', 'points'
))
- self.requiredLayers.append(RequiredLayer(
+ self.required_layers.append(RequiredLayer(
'Parcel', 'Parcels', 'parcels', 'parcel_id', 'polygons'
))
-
def initGui(self):
""" Initialize gui
"""
# create plugin toolbar
- self.createPluginToolBar()
-
+ self.create_plugin_toolbar()
def unload(self):
""" Uninitialize gui
"""
# remove plugin toolbar
- self.removePluginToolBar()
+ self.remove_plugin_toolbar()
# remove layers
- self.refreshLayers()
- for l in self.requiredLayers:
+ self.refresh_layers()
+ for l in self.required_layers:
if bool(l.layer):
- QgsMapLayerRegistry.instance().removeMapLayers([l.layer.id(),])
-
+ QgsMapLayerRegistry.instance().removeMapLayers([l.layer.id()])
- def createPluginToolBar(self):
+ def create_plugin_toolbar(self):
""" Create plugin toolbar to house buttons
"""
- # create plugin toolbar
- self.pluginToolBar = QToolBar(metadata.name())
- self.pluginToolBar.setObjectName(metadata.name())
+ # create plugin toolbar
+ self.plugin_toolbar = QToolBar(metadata.name())
+ self.plugin_toolbar.setObjectName(metadata.name())
+ # create Database Selection button
+ self.select_database_action = QAction(
+ QIcon(os.path.join(self.plugin_dir, "images", "database.png")),
+ "Select Database Connection",
+ self.iface.mainWindow())
+ self.select_database_action.setWhatsThis(
+ "Select database connection")
+ self.select_database_action.setStatusTip(
+ "Select database connection")
+ self.select_database_action.triggered.connect(
+ self.manage_database_connection)
# create Beardist button
- self.actionBearDist = QAction(
+ self.bearing_distance_action = 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)
+ self.iface.mainWindow())
+ self.bearing_distance_action.setWhatsThis(
+ "Manage bearings and distances")
+ self.bearing_distance_action.setStatusTip(
+ "Manage bearings and distances")
+ self.bearing_distance_action.triggered.connect(
+ self.manage_bearing_distance)
# create Beacons button
- self.actionBeacons = QAction(
+ self.beacons_action = 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)
+ self.iface.mainWindow())
+ self.beacons_action.setWhatsThis("Manage beacons")
+ self.beacons_action.setStatusTip("Manage beacons")
+ self.beacons_action.triggered.connect(self.manage_beacons)
# create Parcels button
- self.actionParcels = QAction(
+ self.parcels_action = 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)
+ self.iface.mainWindow())
+ self.parcels_action.setWhatsThis("Manage parcels")
+ self.parcels_action.setStatusTip("Manage parcels")
+ self.parcels_action.triggered.connect(self.manage_parcels)
# populate plugin toolbar
- self.pluginToolBar.addAction(self.actionBearDist)
- self.pluginToolBar.addAction(self.actionBeacons)
- self.pluginToolBar.addAction(self.actionParcels)
+ self.plugin_toolbar.addAction(self.select_database_action)
+ self.plugin_toolbar.addAction(self.bearing_distance_action)
+ self.plugin_toolbar.addAction(self.beacons_action)
+ self.plugin_toolbar.addAction(self.parcels_action)
# add plugin toolbar to gui
- self.iface.mainWindow().addToolBar(self.pluginToolBar)
-
+ self.iface.mainWindow().addToolBar(self.plugin_toolbar)
- def removePluginToolBar(self):
+ def remove_plugin_toolbar(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()
-
+ if hasattr(self, "pluginToolBar"):
+ self.iface.mainWindow().removeToolBar(self.plugin_toolbar)
+ self.plugin_toolbar.hide()
- def setDatabaseConnection(self):
+ def set_database_connection(self, connection=None, crs=None):
""" Create a database connection
"""
# fetch settings
settings_plugin = QSettings()
settings_postgis = QSettings()
- settings_plugin.beginGroup(metadata.name().replace(" ","_"))
+ settings_plugin.beginGroup(metadata.name().replace(" ", "_"))
settings_postgis.beginGroup('PostgreSQL/connections')
- # fetch pre-chosen database connection
- conn = settings_plugin.value("DatabaseConnection", None)
+ self.connection = connection
+ if not bool(self.connection):
+ # fetch pre-chosen database connection
+ self.connection = settings_plugin.value("DatabaseConnection", None)
# check if still exists
- if bool(conn):
- if conn not in settings_postgis.childGroups():
+ if bool(self.connection):
+ if self.connection not in settings_postgis.childGroups():
settings_plugin.setValue("DatabaseConnection", "")
- conn = None
+ self.connection = 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)
+ if not bool(self.connection):
+ dialog = DatabaseConnectionDialog()
+ dialog.show()
+ if bool(dialog.exec_()):
+ self.connection = dialog.get_database_connection()
+ if dialog.get_crs():
+ self.crs = QgsCoordinateReferenceSystem(
+ dialog.get_crs().get('auth_id'))
+ settings_plugin.setValue("DatabaseConnection", self.connection)
# 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 = ''
+ if bool(self.connection):
+ db_service = settings_postgis.value(self.connection + '/service')
+ db_host = settings_postgis.value(self.connection + '/host')
+ db_port = settings_postgis.value(self.connection + '/port')
+ db_name = settings_postgis.value(self.connection + '/database')
+ db_username = settings_postgis.value(self.connection + '/username')
+ db_password = settings_postgis.value(self.connection + '/password')
+
+ max_attempts = 3
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_password)
+
+ if db_username and db_password:
+ for i in range(max_attempts):
+ error_message = self.connect_to_db(
+ db_service,
db_host,
db_port,
db_name,
db_username,
- db_password,
- )
- self.datetime = datetime.now()
- break
- except Exception as e:
- msg = "Invalid username and password."
+ db_password)
+ if error_message:
+ ok, db_username, db_password = (
+ QgsCredentials.instance().get(
+ self.uri.connectionInfo(),
+ db_username,
+ db_password,
+ error_message))
+ if not ok:
+ break
+ else:
+ break
+
+ else:
+ 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
+ error_message = self.connect_to_db(
+ db_service,
+ db_host,
+ db_port,
+ db_name,
+ db_username,
+ db_password)
+ if not error_message:
+ break
+
settings_plugin.endGroup()
settings_postgis.endGroup()
-
- def refreshLayers(self):
+ def connect_to_db(self, service, host, port, name, username, password):
+ username.replace(" ", "")
+ password.replace(" ", "")
+ try:
+ self.database = database.Manager({
+ "CONNECTION": self.connection,
+ "SERVICE": service,
+ "HOST": host,
+ "NAME": name,
+ "PORT": port,
+ "USER": username,
+ "PASSWORD": password
+ })
+ self.uri.setConnection(
+ host,
+ port,
+ name,
+ username,
+ password)
+ self.datetime = datetime.now()
+ return None
+ except Exception as e:
+ self.database = None
+ msg = "Invalid username and password."
+ return msg
+
+ def refresh_layers(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):
+ if bool(self.database):
+
+ # Comment out below section because connection name is better
+ # to be a group name than crs name IMHO.
+
+ # # first, we need to check the layer group for the crs used by
+ # # current database
+ # query = "SELECT Find_SRID('public', 'beacons', 'the_geom');"
+ # self.database.connect(self.database.parameters)
+ # cursor = self.database.cursor
+ # cursor.execute(query)
+ # crs_id = int(cursor.fetchall()[0][0])
+ # del cursor
+ # group_name = None
+ # for key, value in crs_options.iteritems():
+ # if value == crs_id:
+ # group_name = key
+
+ group_name = self.connection
+
+ root = QgsProject.instance().layerTreeRoot()
+ target_group = root.findGroup(group_name)
+ if not bool(target_group):
+ target_group = root.insertGroup(0, group_name)
+ target_group.setVisible(Qt.Checked)
+
+ for required_layer in reversed(self.required_layers):
+ for layer_node in target_group.findLayers():
+ layer = layer_node.layer()
+ if required_layer.name_plural.lower() == \
+ layer.name().lower():
+ target_group.removeLayer(layer)
+
+ for required_layer in reversed(self.required_layers):
+ self.uri.setDataSource(
+ required_layer.schema,
+ required_layer.table,
+ required_layer.geometry_column,
+ '',
+ required_layer.primary_key)
+ added_layer = QgsVectorLayer(
+ self.uri.uri(), required_layer.name_plural, "postgres")
+ QgsMapLayerRegistry.instance().addMapLayer(
+ added_layer, False)
+ target_group.addLayer(added_layer)
+ for layer_node in target_group.findLayers():
+ layer = layer_node.layer()
+ if required_layer.name_plural == layer.name():
+ required_layer.layer = layer
+ layer_node.setVisible(Qt.Checked)
+ if self.crs:
+ layer.setCrs(self.crs)
+ self.iface.zoomToActiveLayer()
+
+ def manage_beacons(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)
+ if self.datetime.date() != datetime.now().date():
+ self.database = None
+ if self.database is None:
+ self.set_database_connection()
+ if self.database is None:
+ return
+ self.refresh_layers()
+ BeaconManager(self.iface, self.database, self.required_layers)
+ validate_plugin_actions(self, self.database)
self.iface.mapCanvas().refresh()
-
- def manageParcels(self):
+ def manage_parcels(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)
+ if self.datetime.date() != datetime.now().date():
+ self.database = None
+ if self.database is None:
+ self.set_database_connection()
+ if self.database is None:
+ return
+ self.refresh_layers()
+ ParcelManager(self.iface, self.database, self.required_layers)
+ validate_plugin_actions(self, self.database)
self.iface.mapCanvas().refresh()
-
- def manageBearDist(self):
+ def manage_bearing_distance(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)
+ if self.datetime.date() != datetime.now().date():
+ self.database = None
+ if self.database is None:
+ self.set_database_connection()
+ if self.database is None:
+ return
+
+ result = validate_plugin_actions(self, self.database)
+ if not result:
+ QMessageBox.warning(
+ None,
+ "SML Surveyor",
+ ("No Beacons available in the table. "
+ "Please use Beacon Manager tool to create a Beacon."))
+ else:
+ self.refresh_layers()
+ BearDistManager(self.iface, self.database, self.required_layers)
+
self.iface.mapCanvas().refresh()
+ def manage_database_connection(self):
+ """ Action to select the db connection to work with.
+ """
+ database_manager = DatabaseManager()
+ connection = database_manager.get_current_connection()
+ crs = database_manager.get_current_crs()
+ if connection:
+ self.set_database_connection(connection=connection, crs=crs)
+ validate_plugin_actions(self, self.database)
+
+
+class DatabaseManager():
+
+ def __init__(self):
+ self.dialog = DatabaseConnectionDialog()
+ self.dialog.show()
+ self.current_connection = None
+ self.current_crs = None
+ if bool(self.dialog.exec_()):
+ self.current_connection = self.dialog.get_database_connection()
+ self.current_crs = self.dialog.get_crs()
+ settings_plugin = QSettings()
+ settings_plugin.beginGroup(metadata.name().replace(" ", "_"))
+ settings_plugin.setValue(
+ "DatabaseConnection", self.current_connection)
+
+ def get_current_connection(self):
+
+ return self.current_connection
+
+ def get_current_crs(self):
+
+ return self.current_crs
+
class BeaconManager():
- def __init__(self, iface, db, requiredLayers):
+ def __init__(self, iface, database, required_layers):
self.iface = iface
- self.db = db
- self.requiredLayers = requiredLayers
+ self.database = database
+ self.required_layers = required_layers
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):
+ manager_dialog = ManagerDialog(self.required_layers[0])
+ manager_dialog.show()
+ manager_dialog_ret = manager_dialog.exec_()
+ if bool(manager_dialog_ret):
- if mng.getOption() == 0: # create new beacon
+ if manager_dialog.get_option() == 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
+ fields = self.database.get_schema(
+ self.required_layers[0].table, [
+ self.required_layers[0].geometry_column,
+ self.required_layers[0].primary_key
])
# display form
- frm = dlg_FormBeacon(
- self.db,
+ form_dialog = FormBeaconDialog(
+ self.database,
SQL_BEACONS["UNIQUE"],
fields
)
- frm.show()
- frm_ret = frm.exec_()
- if bool(frm_ret):
+ form_dialog.show()
+ form_dialog_ret = form_dialog.exec_()
+ if bool(form_dialog_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())])
+ old_values, new_values = form_dialog.get_values()
+ self.database.query(
+ SQL_BEACONS["INSERT"].format(
+ fields=", ".join(sorted(new_values.keys())),
+ values=", ".join(
+ ["%s" for k in new_values.keys()])),
+ [new_values[k] for k in sorted(new_values.keys())])
self.iface.mapCanvas().refresh()
- else: break
+ else:
+ break
- elif mng.getOption() == 1: # edit existing beacon
+ elif manager_dialog.get_option() == 1: # edit existing beacon
# select beacon
- mode = Mode("EDITOR","EDIT")
+ mode = Mode("EDITOR", "EDIT")
query = SQL_BEACONS["SELECT"]
- slc = dlg_Selector(
- self.db,
+ selector_dialog = SelectorDialog(
+ self.database,
self.iface,
- self.requiredLayers[0],
+ self.required_layers[0],
mode,
query,
- preserve = True
- )
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
- featID = slc.getFeatureId()
+ preserve=True)
+ selector_dialog.show()
+ selector_dialog_ret = selector_dialog.exec_()
+ self.iface.mapCanvas().setMapTool(selector_dialog.tool)
+ if bool(selector_dialog_ret):
+ feat_id = selector_dialog.get_feature_id()
# check if defined by a bearing and distance
- if self.db.query(SQL_BEACONS["BEARDIST"], (featID,))[0][0]:
+ if self.database.query(
+ SQL_BEACONS["BEARDIST"], (feat_id,))[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()
+ "Cannot edit beacon defined by distance "
+ "and bearing via this tool")
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
return
# get fields
- fields = self.db.getSchema(
- self.requiredLayers[0].table, [
- self.requiredLayers[0].geometry_column,
- self.requiredLayers[0].primary_key
+ fields = self.database.get_schema(
+ self.required_layers[0].table, [
+ self.required_layers[0].geometry_column,
+ self.required_layers[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]]
+ values = [value for value in self.database.query(
+ SQL_BEACONS["EDIT"].format(
+ fields=",".join([field.name for field in fields])),
+ (feat_id,))[0]]
# display form
- frm = dlg_FormBeacon(
- self.db,
+ form_dialog = FormBeaconDialog(
+ self.database,
SQL_BEACONS["UNIQUE"],
fields,
- values
- )
- frm.show()
- frm_ret = frm.exec_()
- if bool(frm_ret):
+ values)
+ form_dialog.show()
+ form_dialog_ret = form_dialog.exec_()
+ if bool(form_dialog_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(
+ old_values = []
+ new_values = []
+ for field in fields:
+ if form_dialog.get_values()[0][field.name] \
+ is not None:
+ fields_old.append(field.name)
+ old_values.append(
+ form_dialog.get_values()[0][field.name])
+ fields_new.append(field.name)
+ new_values.append(
+ form_dialog.get_values()[1][field.name])
+ set = ", ".join(
+ ["{field} = %s".format(field=field)
+ for field in fields_new])
+ where = " AND ".join(
+ ["{field} = %s".format(field=field)
+ for field in fields_old])
+ self.database.query(
SQL_BEACONS["UPDATE"].format(
- set = set,
- where = where
- ), values_new + values_old
- )
- for l in self.requiredLayers: l.layer.removeSelection()
+ set=set,
+ where=where),
+ new_values + old_values)
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
- elif mng.getOption() == 2: # delete existing beacon
+ elif manager_dialog.get_option() == 2: # delete existing beacon
# select beacon
- mode = Mode("REMOVER","REMOVE")
+ mode = Mode("REMOVER", "REMOVE")
query = SQL_BEACONS["SELECT"]
- slc = dlg_Selector(
- self.db,
+ selector_dialog = SelectorDialog(
+ self.database,
self.iface,
- self.requiredLayers[0],
+ self.required_layers[0],
mode,
query,
- preserve = True
- )
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
- featID = slc.getFeatureId()
+ preserve=True)
+ selector_dialog.show()
+ selector_dialog_ret = selector_dialog.exec_()
+ self.iface.mapCanvas().setMapTool(selector_dialog.tool)
+ if bool(selector_dialog_ret):
+ feat_id = selector_dialog.get_feature_id()
# 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()
+ if self.database.query(
+ SQL_BEACONS["BEARDIST"],
+ (feat_id,))[0][0]:
+ QMessageBox.warning(
+ None,
+ "Bearing and Distance Definition",
+ "Cannot delete beacon defined by distance "
+ "and bearing via this tool")
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
return
# delete beacon from database
- self.db.query(SQL_BEACONS["DELETE"], (featID,))
- for l in self.requiredLayers: l.layer.removeSelection()
+ self.database.query(SQL_BEACONS["DELETE"], (feat_id,))
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
class ParcelManager():
- def __init__(self, iface, db, requiredLayers):
+ def __init__(self, iface, database, required_layers):
self.iface = iface
- self.db = db
- self.requiredLayers = requiredLayers
+ self.database = database
+ self.required_layers = required_layers
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):
+ manager_dialog = ManagerDialog(self.required_layers[1])
+ manager_dialog.show()
+ manager_dialog_ret = manager_dialog.exec_()
+ if bool(manager_dialog_ret):
- if mng.getOption() == 0: # create new parcel
+ if manager_dialog.get_option() == 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,
+ auto_complete = [
+ str(i[0]) for i in self.database.query(
+ SQL_PARCELS["AUTOCOMPLETE"])]
+ form_dialog = FormParcelDialog(
+ self.database,
self.iface,
- self.requiredLayers,
+ self.required_layers,
SQL_BEACONS,
SQL_PARCELS,
- autocomplete
- )
- frm.show()
- frm_ret = frm.exec_()
- self.iface.mapCanvas().setMapTool(frm.tool)
- if bool(frm_ret):
+ auto_complete)
+ form_dialog.show()
+ form_dialog_ret = form_dialog.exec_()
+ self.iface.mapCanvas().setMapTool(form_dialog.tool)
+ if bool(form_dialog_ret):
# add parcel to database
points = []
- for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
- points.append(
- (frm.getValues()[1]["parcel_id"], beacon, i))
- sql = self.db.queryPreview(
+ for i, beacon in enumerate(
+ form_dialog.get_values()[1]["sequence"]):
+ points.append((
+ form_dialog.get_values()[1]["parcel_id"],
+ beacon,
+ i))
+ sql = self.database.preview_query(
SQL_PARCELS["INSERT_GENERAL"],
data=points,
- multi_data=True
- )
- self.db.query(sql)
+ multi_data=True)
+ self.database.query(sql)
self.iface.mapCanvas().refresh()
else:
break
- for l in self.requiredLayers: l.layer.removeSelection()
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
- elif mng.getOption() == 1: # edit existing parcel
+ elif manager_dialog.get_option() == 1: # edit existing parcel
# select parcel
- mode = Mode("EDITOR","EDIT")
+ mode = Mode("EDITOR", "EDIT")
query = SQL_PARCELS["SELECT"]
- slc = dlg_Selector(
- self.db,
+ selector_dialog = SelectorDialog(
+ self.database,
self.iface,
- self.requiredLayers[1],
+ self.required_layers[1],
mode,
query,
- preserve = True
- )
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
+ preserve=True)
+ selector_dialog.show()
+ selector_dialog_ret = selector_dialog.exec_()
+ self.iface.mapCanvas().setMapTool(selector_dialog.tool)
+ if bool(selector_dialog_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,
+ auto_complete = [
+ str(i[0]) for i in self.database.query(
+ SQL_PARCELS["AUTOCOMPLETE"])]
+ data = (lambda t: {"parcel_id": t[0], "sequence": t[1]})(
+ self.database.query(
+ SQL_PARCELS["EDIT"],
+ (selector_dialog.get_feature_id(),))[0])
+ form_dialog = FormParcelDialog(
+ self.database,
self.iface,
- self.requiredLayers,
+ self.required_layers,
SQL_BEACONS,
SQL_PARCELS,
- autocomplete,
- data
- )
- frm.show()
- frm_ret = frm.exec_()
- self.iface.mapCanvas().setMapTool(frm.tool)
- if bool(frm_ret):
+ auto_complete,
+ data)
+ form_dialog.show()
+ form_dialog_ret = form_dialog.exec_()
+ self.iface.mapCanvas().setMapTool(form_dialog.tool)
+ if bool(form_dialog_ret):
# edit parcel in database
- self.db.query(
+ self.database.query(
SQL_PARCELS["DELETE"],
- (frm.getValues()[0]["parcel_id"],)
- )
+ (form_dialog.get_values()[0]["parcel_id"],))
points = []
- for i, beacon in enumerate(frm.getValues()[1]["sequence"]):
- points.append(
- (frm.getValues()[1]["parcel_id"], beacon, i))
- sql = self.db.queryPreview(
+ for i, beacon in enumerate(
+ form_dialog.get_values()[1]["sequence"]):
+ points.append((
+ form_dialog.get_values()[1]["parcel_id"],
+ beacon,
+ i))
+ sql = self.database.preview_query(
SQL_PARCELS["INSERT_GENERAL"],
data=points,
- multi_data=True
- )
- self.db.query(sql)
- for l in self.requiredLayers: l.layer.removeSelection()
+ multi_data=True)
+ self.database.query(sql)
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
- elif mng.getOption() == 2: # delete existing parcel
+ elif manager_dialog.get_option() == 2: # delete existing parcel
# select parcel
- mode = Mode("REMOVER","REMOVE")
+ mode = Mode("REMOVER", "REMOVE")
query = SQL_PARCELS["SELECT"]
- slc = dlg_Selector(
- self.db,
+ selector_dialog = SelectorDialog(
+ self.database,
self.iface,
- self.requiredLayers[1],
+ self.required_layers[1],
mode,
query,
- preserve = True
- )
- slc.show()
- slc_ret = slc.exec_()
- self.iface.mapCanvas().setMapTool(slc.tool)
- if bool(slc_ret):
+ preserve=True)
+ selector_dialog.show()
+ selector_dialog_ret = selector_dialog.exec_()
+ self.iface.mapCanvas().setMapTool(selector_dialog.tool)
+ if bool(selector_dialog_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()
+ feat_id = selector_dialog.get_feature_id()
+ self.database.query(
+ SQL_PARCELS["DELETE"],
+ (self.database.query(
+ SQL_PARCELS["SELECT"],
+ (feat_id,))[0][0],))
+ for required_layer in self.required_layers:
+ required_layer.layer.removeSelection()
class BearDistManager():
- def __init__(self, iface, db, requiredLayers):
+ def __init__(self, iface, database, required_layers):
self.iface = iface
- self.db = db
- self.requiredLayers = requiredLayers
+ self.database = database
+ self.required_layers = required_layers
self.run()
def run(self):
""" Main method
"""
- dlg = dlg_FormBearDist(
- self.db,
+ dialog = BearingDistanceFormDialog(
+ self.database,
SQL_BEARDIST,
SQL_BEACONS,
- self.requiredLayers
- )
- dlg.show()
- dlg_ret = dlg.exec_()
- if bool(dlg_ret):
- surveyPlan, referenceBeacon, beardistChain = dlg.getReturn()
+ self.required_layers)
+ dialog.show()
+ dialog_ret = dialog.exec_()
+ if bool(dialog_ret):
+ survey_plan, reference_beacon, beardist_chain = dialog.get_return()
# check whether survey plan is defined otherwise define it
- if not self.db.query(
+ if not self.database.query(
SQL_BEARDIST["IS_SURVEYPLAN"],
- (surveyPlan,)
- )[0][0]:
- self.db.query(
+ (survey_plan,))[0][0]:
+ self.database.query(
SQL_BEARDIST["INSERT_SURVEYPLAN"],
- (surveyPlan, referenceBeacon)
- )
+ (survey_plan, reference_beacon))
# 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])
+ existing_chain = []
+ for index, link in enumerate(
+ self.database.query(
+ SQL_BEARDIST["EXIST_BEARDISTCHAINS"],
+ (survey_plan,))):
+ existing_chain.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)
+ for link in beardist_chain:
+ 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(
+ temp = list(existing_chain)
+ for existing_link in existing_chain:
+ for old_link in old:
+ if existing_link[2] == old_link[2]:
+ if old_link[1] == "NULL":
+ temp.remove(existing_link)
+ break
+ self.database.query(
SQL_BEARDIST["UPDATE_LINK"],
- [surveyPlan] + olink[0] + [olink[2]]
- )
- tmp.remove(elink)
- break;
- beardistChainExisting = tmp
- for elink in beardistChainExisting:
- self.db.query(
+ [survey_plan] + old_link[0] + [old_link[2]])
+ temp.remove(existing_link)
+ break
+ existing_chain = temp
+ for existing_link in existing_chain:
+ self.database.query(
SQL_BEARDIST["DELETE_LINK"],
- (elink[0][3],)
- )
+ (existing_link[0][3],))
# sort out new links
- for nlink in new:
- self.db.query(
+ for new_link in new:
+ self.database.query(
SQL_BEARDIST["INSERT_LINK"],
- [surveyPlan] + nlink[0]
- )
+ [survey_plan] + new_link[0])
diff --git a/plugin_upload.py b/plugin_upload.py
index 4d2d384..5106560 100755
--- a/plugin_upload.py
+++ b/plugin_upload.py
@@ -3,31 +3,39 @@
#
# Author: A. Pasotti, V. Picavet
-import xmlrpclib, sys, os
+import xmlrpclib
+import sys
import getpass
from optparse import OptionParser
# Configuration
-PROTOCOL='http'
-SERVER='plugins.qgis.org'
-PORT='80'
-ENDPOINT='/plugins/RPC2/'
-VERBOSE=False
+PROTOCOL = 'http'
+SERVER = 'plugins.qgis.org'
+PORT = '80'
+ENDPOINT = '/plugins/RPC2/'
+VERBOSE = False
+
def main(options, args):
- address = "%s://%s:%s@%s:%s%s" % (PROTOCOL, options.username, options.password,
- options.server, options.port, ENDPOINT)
- print "Connecting to: %s" % hidepassword(address)
-
+ address = "%s://%s:%s@%s:%s%s" % (
+ PROTOCOL,
+ options.username,
+ options.password,
+ options.server,
+ options.port,
+ ENDPOINT)
+ print "Connecting to: %s" % hide_password(address)
+
server = xmlrpclib.ServerProxy(address, verbose=VERBOSE)
-
+
try:
- plugin_id, version_id = server.plugin.upload(xmlrpclib.Binary(open(args[0]).read()))
+ plugin_id, version_id = server.plugin.upload(
+ xmlrpclib.Binary(open(args[0]).read()))
print "Plugin ID: %s" % plugin_id
print "Version ID: %s" % version_id
except xmlrpclib.ProtocolError, err:
print "A protocol error occurred"
- print "URL: %s" % hidepassword(err.url, 0)
+ print "URL: %s" % hide_password(err.url, 0)
print "HTTP/HTTPS headers: %s" % err.headers
print "Error code: %d" % err.errcode
print "Error message: %s" % err.errmsg
@@ -36,7 +44,8 @@ def main(options, args):
print "Fault code: %d" % err.faultCode
print "Fault string: %s" % err.faultString
-def hidepassword(url, start = 6):
+
+def hide_password(url, start=6):
"""Returns the http url with password part replaced with '*'."""
passdeb = url.find(':', start) + 1
passend = url.find('@')
@@ -45,14 +54,30 @@ def hidepassword(url, start = 6):
if __name__ == "__main__":
parser = OptionParser(usage="%prog [options] plugin.zip")
- parser.add_option("-w", "--password", dest="password",
- help="Password for plugin site", metavar="******")
- parser.add_option("-u", "--username", dest="username",
- help="Username of plugin site", metavar="user")
- parser.add_option("-p", "--port", dest="port",
- help="Server port to connect to", metavar="80")
- parser.add_option("-s", "--server", dest="server",
- help="Specify server name", metavar="plugins.qgis.org")
+ parser.add_option(
+ "-w",
+ "--password",
+ dest="password",
+ help="Password for plugin site",
+ metavar="******")
+ parser.add_option(
+ "-u",
+ "--username",
+ dest="username",
+ help="Username of plugin site",
+ metavar="user")
+ parser.add_option(
+ "-p",
+ "--port",
+ dest="port",
+ help="Server port to connect to",
+ metavar="80")
+ parser.add_option(
+ "-s",
+ "--server",
+ dest="server",
+ help="Specify server name",
+ metavar="plugins.qgis.org")
(options, args) = parser.parse_args()
if len(args) != 1:
print "Please specify zip file.\n"
@@ -65,7 +90,7 @@ def hidepassword(url, start = 6):
if not options.username:
# interactive mode
username = getpass.getuser()
- print "Please enter user name [%s] :"%username,
+ print "Please enter user name [%s] :" % username,
res = raw_input()
if res != "":
options.username = res
@@ -75,4 +100,3 @@ def hidepassword(url, start = 6):
# interactive mode
options.password = getpass.getpass()
main(options, args)
-
diff --git a/portqgis2_db_changes.sql b/portqgis2_db_changes.sql
deleted file mode 100644
index 488b5a4..0000000
--- a/portqgis2_db_changes.sql
+++ /dev/null
@@ -1,41 +0,0 @@
-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
old mode 100644
new mode 100755
index c44437a..7e8e269
--- a/qgisToolbox.py
+++ b/qgisToolbox.py
@@ -21,74 +21,80 @@
from qgis.gui import *
from qgis.core import*
-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
+
+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):
# initialize instance valiables
- self.parent = parent # assume parent has a captured method
+ self.parent = parent # assume parent has a captured method
self.iface = iface
self.layer = layer
self.capturing = capturing
self.selected = []
# remember current tool
- self.parentTool = self.iface.mapCanvas().mapTool()
+ self.parent_tool = self.iface.mapCanvas().mapTool()
# create selection tool
- self.selectTool = QgsMapToolEmitPoint(self.iface.mapCanvas())
- self.selectTool.canvasClicked.connect(self.capture)
- # enable capturing if allowed
- if capturing: self.iface.mapCanvas().setMapTool(self.selectTool)
+ self.select_tool = QgsMapToolEmitPoint(self.iface.mapCanvas())
+ self.select_tool.canvasClicked.connect(self.capture)
+ # enable capturing if allowed
+ if capturing:
+ self.iface.mapCanvas().setMapTool(self.select_tool)
# clear selection
self.layer.removeSelection()
- def enableCapturing(self):
+ def enable_capturing(self):
""" Enable feature selection
"""
self.capturing = True
- self.iface.mapCanvas().setMapTool(self.selectTool)
+ self.iface.mapCanvas().setMapTool(self.select_tool)
- def disableCapturing(self):
+ def disable_capturing(self):
""" Disable feature selection
"""
self.capturing = False
- self.iface.mapCanvas().setMapTool(self.parentTool)
+ self.iface.mapCanvas().setMapTool(self.parent_tool)
- def clearSelection(self):
+ def clear_selection(self):
""" Clear feature selection
"""
self.selected = []
self.layer.removeSelection()
- def appendSelection(self, id):
+ def append_selection(self, id):
""" Append a feature to the list of currently selected features
"""
- # toggle selection
+ # toggle selection
self.selected.append(id)
# notify parent of changed selection
self.parent.captured(self.selected)
-
+
def capture(self, point, button):
""" Capture id of feature selected by the selector tool
"""
# check that capturing has been enabled
if self.capturing:
- pnt_geom = QgsGeometry.fromPoint(point)
- pnt_buffer = pnt_geom.buffer((self.iface.mapCanvas().mapUnitsPerPixel()*4),0)
- pnt_rect = pnt_buffer.boundingBox()
- self.layer.invertSelectionInRectangle(pnt_rect)
+ point_geometry = QgsGeometry.fromPoint(point)
+ point_buffer = point_geometry.buffer(
+ (self.iface.mapCanvas().mapUnitsPerPixel() * 4), 0)
+ point_rectangle = point_buffer.boundingBox()
+ self.layer.invertSelectionInRectangle(point_rectangle)
if bool(self.layer.selectedFeaturesIds()):
for id in self.layer.selectedFeaturesIds():
- if id not in self.selected:
+ 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
+
+ # self.layer.select([], point_rectangle, True, True)
+ # feat = QgsFeature()
+ # while self.layer.nextFeature(feat):
+ # self.append_selection(feat.id())
+ # break
diff --git a/resources.qrc b/resources.qrc
old mode 100644
new mode 100755
diff --git a/scripts/sample_data.sql b/scripts/sample_data.sql
new file mode 100644
index 0000000..5161d2f
--- /dev/null
+++ b/scripts/sample_data.sql
@@ -0,0 +1,33 @@
+INSERT INTO beacons(beacon, y, x, location, name)
+VALUES ('MB1416',735460.877,488470.404,'East','Test');
+
+
+INSERT INTO allocation_cat(description)
+VALUES ('free and unallocated parcel'),('temporary allocation pending approval'),
+('parcel allocated and approved'),('Private Survey pending approval'),('Private Survey approved');
+
+INSERT INTO instrument_cat(description)
+VALUES('Deed of Assignment'),('Certificate Of Occupancy'),('Deed of Conveyance');
+
+INSERT INTO local_govt( local_govt_name)
+VALUES ('Test province');
+
+INSERT INTO status_cat(description)
+VALUES ('application'),('approved'),('rejected');
+
+INSERT INTO prop_types(code, prop_type_name)
+VALUES ('AR','Allocation Residential'),('AC','Allocation Commercial');
+
+INSERT INTO schemes(scheme_name) VALUES ('Garage');
+
+INSERT INTO deeds(fileno, planno, instrument, grantor, grantee, block, plot, location)
+VALUES ('IF SLR184','BC5 OG','COFO','OGSG','Olawale Olusoga Olubi',38,23,'RIVERVIEW');
+
+INSERT INTO survey(plan_no, ref_beacon, scheme)
+VALUES ('BC5 OG', 'MB1416', 1);
+
+INSERT INTO public.parcel_lookup(plot_sn, available, scheme, block, local_govt, prop_type, file_number,
+allocation, manual_no, deeds_file,official_area, private, status)
+VALUES ('1', true, 1, '0', 1, 1, 'HOC/PL/123', 1, '0', '', 680.510999999999967, true, 1);
+
+
diff --git a/test_suite.py b/test_suite.py
new file mode 100644
index 0000000..26bf42a
--- /dev/null
+++ b/test_suite.py
@@ -0,0 +1,50 @@
+# coding=utf-8
+"""
+Test Suite for Cadasta.
+Contact : etienne at kartoza dot com
+.. note:: This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+"""
+
+import sys
+import os
+import unittest
+from osgeo import gdal
+from qgis.core import QGis
+
+__author__ = 'etiennetrimaille'
+__revision__ = '$Format:%H$'
+__date__ = '14/06/2016'
+__copyright__ = 'Copyright 2016, Cadasta'
+
+
+def _run_tests(test_suite, package_name):
+ """Core function to test a test suite."""
+ count = test_suite.countTestCases()
+ print '########'
+ print '%s tests has been discovered in %s' % (count, package_name)
+ print 'QGIS : %s' % unicode(QGis.QGIS_VERSION_INT)
+ print 'Python GDAL : %s' % gdal.VersionInfo('VERSION_NUM')
+ print 'Run slow tests : %s' % (not os.environ.get('ON_TRAVIS', False))
+ print '########'
+ unittest.TextTestRunner(verbosity=3, stream=sys.stdout).run(test_suite)
+
+
+def test_package(package='parcel_plugin'):
+ """Test package.
+ This function is called by travis without arguments.
+ :param package: The package to test.
+ :type package: str
+ """
+ test_loader = unittest.defaultTestLoader
+ try:
+ test_suite = test_loader.discover(package)
+ except ImportError:
+ test_suite = unittest.TestSuite()
+ _run_tests(test_suite, package)
+
+
+if __name__ == '__main__':
+ test_package()
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..9bad579
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+# coding=utf-8
diff --git a/tests/test_database.py b/tests/test_database.py
new file mode 100644
index 0000000..35a4d62
--- /dev/null
+++ b/tests/test_database.py
@@ -0,0 +1,39 @@
+# coding=utf-8
+"""Test database."""
+
+import unittest
+import psycopg2
+from database import Manager
+
+
+class TestDatabase(unittest.TestCase):
+
+ def test_connection(self):
+ """Test connection using database manager class."""
+ parameters = {
+ 'HOST': 'localhost',
+ 'NAME': 'sml',
+ 'USER': 'postgres',
+ 'PASSWORD': 'postgres',
+ 'PORT': '5432'
+ }
+
+ expected_connection = psycopg2.connect(
+ "host='{HOST}' dbname='{NAME}' user='{USER}' "
+ "password='{PASSWORD}' port='{PORT}'".format(
+ HOST=parameters["HOST"],
+ NAME=parameters["NAME"],
+ USER=parameters["USER"],
+ PASSWORD=parameters["PASSWORD"],
+ PORT=parameters["PORT"]))
+
+ # connect to postgresql instance
+
+ connection_manager = Manager(parameters)
+ connection_manager.connect(parameters)
+
+ actual_connection = connection_manager.connection
+
+ self.assertIsNotNone(actual_connection)
+
+ self.assertEqual(expected_connection.dsn, actual_connection.dsn)
diff --git a/tests/test_pep8.py b/tests/test_pep8.py
new file mode 100644
index 0000000..850b3b3
--- /dev/null
+++ b/tests/test_pep8.py
@@ -0,0 +1,28 @@
+# coding=utf-8
+"""Tests Pep8."""
+
+import unittest
+import os
+from subprocess import Popen, PIPE
+
+
+class TestPep8(unittest.TestCase):
+ """Test that the plugin is PEP8 compliant."""
+
+ def test_pep8(self):
+ """Test if the code is PEP8 compliant."""
+
+ if os.environ.get('ON_TRAVIS', False):
+ root = './'
+ else:
+ root = '../'
+
+ command = ['make', 'pep8']
+ output = Popen(command, stdout=PIPE, cwd=root).communicate()[0]
+
+ # make pep8 produces some extra lines by default.
+ default_number_lines = 5
+ lines = len(output.splitlines()) - default_number_lines
+
+ message = 'Hey mate, go back to your keyboard :)'
+ self.assertEquals(lines, 0, message)
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
new file mode 100644
index 0000000..915725f
--- /dev/null
+++ b/tests/test_plugin.py
@@ -0,0 +1,21 @@
+# coding=utf-8
+"""Test plugin."""
+
+import unittest
+from plugin import SMLSurveyor
+from tests.utilities import qgis_iface
+
+IFACE = qgis_iface()
+
+
+class TestPlugin(unittest.TestCase):
+
+ def test_toolbar(self):
+ """Test SML Surveyor toolbar functionality."""
+ sml_plugin = SMLSurveyor(IFACE)
+ sml_plugin.create_plugin_toolbar()
+
+ self.assertIsNotNone(sml_plugin.plugin_toolbar)
+ self.assertIsNotNone(sml_plugin.bearing_distance_action)
+ self.assertIsNotNone(sml_plugin.beacons_action)
+ self.assertIsNotNone(sml_plugin.parcels_action)
diff --git a/tests/test_qgis_environment.py b/tests/test_qgis_environment.py
new file mode 100644
index 0000000..04ae19f
--- /dev/null
+++ b/tests/test_qgis_environment.py
@@ -0,0 +1,50 @@
+# coding=utf-8
+"""Tests for QGIS functionality.
+.. note:: This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+"""
+
+import os
+import unittest
+from qgis.core import (
+ QgsProviderRegistry,
+ QgsCoordinateReferenceSystem,
+ QgsRasterLayer)
+
+
+class QGISTest(unittest.TestCase):
+ """Test the QGIS Environment"""
+
+ def test_qgis_environment(self):
+ """QGIS environment has the expected providers"""
+
+ r = QgsProviderRegistry.instance()
+ self.assertIn('gdal', r.providerList())
+ self.assertIn('ogr', r.providerList())
+ self.assertIn('postgres', r.providerList())
+
+ def test_projection(self):
+ """Test that QGIS properly parses a wkt string.
+ """
+ crs = QgsCoordinateReferenceSystem()
+ wkt = (
+ 'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",'
+ 'SPHEROID["WGS_1984",6378137.0,298.257223563]],'
+ 'PRIMEM["Greenwich",0.0],UNIT["Degree",'
+ '0.0174532925199433]]')
+ crs.createFromWkt(wkt)
+ auth_id = crs.authid()
+ expected_auth_id = 'EPSG:4326'
+ self.assertEqual(auth_id, expected_auth_id)
+
+ # now test for a loaded layer
+ path = os.path.join(os.path.dirname(__file__), 'tenbytenraster.asc')
+ title = 'TestRaster'
+ layer = QgsRasterLayer(path, title)
+ auth_id = layer.crs().authid()
+ self.assertEqual(auth_id, expected_auth_id)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/testing.sql b/tests/testing.sql
old mode 100644
new mode 100755
similarity index 100%
rename from testing.sql
rename to tests/testing.sql
diff --git a/tests/utilities.py b/tests/utilities.py
new file mode 100644
index 0000000..3dff35e
--- /dev/null
+++ b/tests/utilities.py
@@ -0,0 +1,20 @@
+# coding=utf-8
+"""Helper module for gui test suite."""
+
+QGIS_APP = None # Static variable used to hold hand to running QGIS app
+CANVAS = None
+PARENT = None
+IFACE = None
+
+
+def qgis_iface():
+ """Helper method to get the iface for testing.
+ :return: The QGIS interface.
+ :rtype: QgsInterface
+ """
+ from qgis.utils import iface
+ if iface is not None:
+ return iface
+ else:
+ from qgis.testing.mocked import get_iface
+ return get_iface()
diff --git a/ui_crsselector.ui b/ui_crsselector.ui
new file mode 100644
index 0000000..d57886d
--- /dev/null
+++ b/ui_crsselector.ui
@@ -0,0 +1,269 @@
+
+
+ CrsSelectorDialogBase
+
+
+
+ 0
+ 0
+ 474
+ 337
+
+
+
+ CRS Selector
+
+
+ -
+
+
+ 1
+
+
+
+
+ 0
+
+
-
+
+
+
+ about:blank
+
+
+
+
+
+
+
+
+
+ 0
+
+ -
+
+
+ Choose a CRS definition
+
+
+
-
+
+
+ -
+
+
+
+ 16777215
+ 30
+
+
+
+ <html><head/><body><p>Insert your custom PostGIS spatial_ref_sys specification (for more information, visit http://www.spatialreference.org)</p></body></html>
+
+
+ true
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+ Choose available PostGIS spatial_ref_sys
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ srid
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ auth_name
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ proj4text
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ srtext
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+
+ 60
+ 0
+
+
+
+ auth_srid
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::LeftToRight
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+ false
+
+
+
+
+
+
+
+ QWebView
+ QWidget
+
+
+
+
+
+
+ button_box
+ accepted()
+ CrsSelectorDialogBase
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ button_box
+ rejected()
+ CrsSelectorDialogBase
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/ui_pgnewconnection.ui b/ui_pgnewconnection.ui
new file mode 100644
index 0000000..240e061
--- /dev/null
+++ b/ui_pgnewconnection.ui
@@ -0,0 +1,355 @@
+
+
+ QgsPgNewConnectionBase
+
+
+
+ 0
+ 0
+ 447
+ 599
+
+
+
+
+ 0
+ 0
+
+
+
+ Create a New PostGIS connection
+
+
+ true
+
+
+ true
+
+
+ -
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Connection Information
+
+
+
+ 5
+
+
+ 5
+
+
+ 0
+
+
+ 5
+
+
-
+
+
+ Don't resolve type of unrestricted columns (GEOMETRY)
+
+
+
+ -
+
+
+ Restrict the search to the public schema for spatial tables not in the geometry_columns table
+
+
+ When searching for spatial tables that are not in the geometry_columns tables, restrict the search to tables that are in the public schema (for some databases this can save lots of time)
+
+
+ Only look in the 'public' schema
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 0
+
+
+
+
+ -
+
+
+ Also list tables with no geometry
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ 0
+
+
+
+ Authentication
+
+
+
+ 5
+
+
-
+
+
+ Username
+
+
+ txtUsername
+
+
+
+ -
+
+
+ QLineEdit::Password
+
+
+
+ -
+
+
+ Save
+
+
+
+ -
+
+
+ Password
+
+
+ txtPassword
+
+
+
+ -
+
+
+ -
+
+
+ Save
+
+
+
+
+
+
+
+ -
+
+
+ Use estimated table statistics for the layer metadata.
+
+
+ <html>
+<body>
+<p>When the layer is setup various metadata is required for the PostGIS table. This includes information such as the table row count, geometry type and spatial extents of the data in the geometry column. If the table contains a large number of rows determining this metadata is time consuming.</p>
+<p>By activating this option the following fast table metadata operations are done:</p>
+<p>1) Row count is determined from table statistics obtained from running the PostgreSQL table analyse function.</p>
+<p>2) Table extents are always determined with the estimated_extent PostGIS function even if a layer filter is applied.</p>
+<p>3) If the table geometry type is unknown and is not exclusively taken from the geometry_columns table, then it is determined from the first 100 non-null geometry rows in the table.</p>
+</body>
+</html>
+
+
+ Use estimated table metadata
+
+
+
+ -
+
+
+ Restrict the displayed tables to those that are in the layer registries.
+
+
+ Restricts the displayed tables to those that are found in the layer registries (geometry_columns, geography_columns, topology.layer). This can speed up the initial display of spatial tables.
+
+
+ Only show layers in the layer registries
+
+
+
+ -
+
+
-
+
+
-
+
+
+ Name
+
+
+ txtName
+
+
+
+ -
+
+
+ Service
+
+
+ txtService
+
+
+
+ -
+
+
+ Host
+
+
+ txtHost
+
+
+
+ -
+
+
+ Port
+
+
+ txtPort
+
+
+
+ -
+
+
+ Database
+
+
+ txtDatabase
+
+
+
+ -
+
+
+ SSL mode
+
+
+ cbxSSLmode
+
+
+
+
+
+ -
+
+
-
+
+
+ Name of the new connection
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ 5432
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ txtName
+ txtService
+ txtHost
+ txtPort
+ txtDatabase
+ cbxSSLmode
+ tabAuthentication
+ txtUsername
+ chkStoreUsername
+ txtPassword
+ chkStorePassword
+ cb_geometryColumnsOnly
+ cb_dontResolveType
+ cb_publicSchemaOnly
+ cb_allowGeometrylessTables
+ cb_useEstimatedMetadata
+
+
+
+
+ buttonBox
+ rejected()
+ QgsPgNewConnectionBase
+ reject()
+
+
+ 313
+ 501
+
+
+ 451
+ 312
+
+
+
+
+ buttonBox
+ accepted()
+ QgsPgNewConnectionBase
+ accept()
+
+
+ 395
+ 501
+
+
+ 450
+ 287
+
+
+
+
+
diff --git a/ui_sml_surveyor.ui b/ui_sml_surveyor.ui
old mode 100644
new mode 100755
diff --git a/utilities.py b/utilities.py
new file mode 100644
index 0000000..14fb499
--- /dev/null
+++ b/utilities.py
@@ -0,0 +1,146 @@
+# coding=utf-8
+"""This module contains utilities."""
+
+import os
+from PyQt4 import uic
+from PyQt4.QtCore import Qt
+from PyQt4.QtGui import QCompleter, QComboBox, QSortFilterProxyModel
+
+
+crs_options = {
+ 'Minna / UTM zone 31N': 26331,
+ 'Minna / UTM zone 32N': 26332
+}
+
+def images_path(*args):
+ """Get the path to our resources folder.
+
+ .. versionadded:: 3.0
+
+ Note that in version 3.0 we removed the use of Qt Resource files in
+ favour of directly accessing on-disk resources.
+
+ :param args List of path elements e.g. ['img', 'logos', 'image.png']
+ :type args: list[str]
+
+ :return: Absolute path to the resources folder.
+ :rtype: str
+ """
+ path = os.path.dirname(__file__)
+ path = os.path.abspath(
+ os.path.join(path, 'images'))
+ for item in args:
+ path = os.path.abspath(os.path.join(path, item))
+
+ return path
+
+def get_path(*args):
+ """Get the path to our specific folder from plugin folder.
+
+ .. versionadded:: 3.0
+
+ Note that in version 3.0 we removed the use of Qt Resource files in
+ favour of directly accessing on-disk resources.
+
+ :param args List of path elements e.g. ['img', 'logos', 'image.png']
+ :type args: list[str]
+
+ :return: Absolute path to the resources folder.
+ :rtype: str
+ """
+ path = os.path.dirname(__file__)
+ for item in args:
+ path = os.path.abspath(os.path.join(path, item))
+
+ return path
+
+def get_ui_class(ui_file):
+ """Get UI Python class from .ui file.
+
+ Can be filename.ui or subdirectory/filename.ui
+
+ :param ui_file: The file of the ui in safe.gui.ui
+ :type ui_file: str
+ """
+ os.path.sep.join(ui_file.split('/'))
+ ui_file_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ ui_file
+ )
+ )
+ return uic.loadUiType(ui_file_path, from_imports=True)[0]
+
+def validate_plugin_actions(toolbar, database):
+ """Check DB schema for actions availability. eg: Manage bearing and
+ distance action needs Beacon to be created first.
+
+ :param database: Database instance
+ :type database: database.Manager
+
+ :param toolbar: plugin toolbar
+ :type toolbar: SMLSurveyor
+
+ :return: Query result
+ :rtype: tuple
+ """
+ query = "select * from survey limit 1;"
+ try:
+ result = database.query(query=query)
+ except Exception as e:
+ raise Exception(
+ 'Backend database query failed!\nError raised: %s.' % (str(e),))
+ if result:
+ toolbar.bearing_distance_action.setEnabled(True)
+ else:
+ toolbar.bearing_distance_action.setEnabled(False)
+ return result
+
+
+class ExtendedComboBox(QComboBox):
+ """Extended class of QComboBox so we can perform a filtering of items.
+ """
+ def __init__(self, parent=None):
+ super(ExtendedComboBox, self).__init__(parent)
+
+ self.setFocusPolicy(Qt.StrongFocus)
+ self.setEditable(True)
+
+ # add a filter model to filter matching items
+ self.pFilterModel = QSortFilterProxyModel(self)
+ self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
+ self.pFilterModel.setSourceModel(self.model())
+
+ # add a completer, which uses the filter model
+ self.completer = QCompleter(self.pFilterModel, self)
+ # always show all (filtered) completions
+ self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
+ self.setCompleter(self.completer)
+
+ # connect signals
+ self.lineEdit().textEdited[unicode].connect(
+ self.pFilterModel.setFilterFixedString)
+ self.completer.activated.connect(self.on_completer_activated)
+
+
+ # on selection of an item from the completer,
+ # select the corresponding item from combobox
+ def on_completer_activated(self, text):
+ if text:
+ index = self.findText(text)
+ self.setCurrentIndex(index)
+
+
+ # on model change, update the models of the filter and completer as well
+ def setModel(self, model):
+ super(ExtendedComboBox, self).setModel(model)
+ self.pFilterModel.setSourceModel(model)
+ self.completer.setModel(self.pFilterModel)
+
+
+ # on model column change, update the model column of
+ # the filter and completer as well
+ def setModelColumn(self, column):
+ self.completer.setCompletionColumn(column)
+ self.pFilterModel.setFilterKeyColumn(column)
+ super(ExtendedComboBox, self).setModelColumn(column)