uTextFilter
- uCRSCombo
uBtnImport
uListOptions
uTableView
diff --git a/linz-data-importer/gui/__init__.py b/linz-data-importer/gui/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/linz-data-importer/linz_data_importer.py b/linz-data-importer/linz_data_importer.py
index 9e57c47..3b37b7a 100644
--- a/linz-data-importer/linz_data_importer.py
+++ b/linz-data-importer/linz_data_importer.py
@@ -14,7 +14,6 @@
* see the LICENSE file for more information *
***************************************************************************/
"""
-from __future__ import absolute_import
# This program is released under the terms of the 3 clause BSD license. See the
# LICENSE file for more information.
@@ -24,12 +23,12 @@
from qgis.PyQt.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, Qt, QRegExp, QSize, Qt
from qgis.PyQt.QtWidgets import QAction, QListWidgetItem, QHeaderView, QMenu, QToolButton
-from qgis.PyQt.QtGui import QIcon, QPixmap, QImage
+from qgis.PyQt.QtGui import QIcon, QPixmap, QImage, QStandardItemModel, QStandardItem
from qgis.PyQt.QtCore import QSortFilterProxyModel
from qgis.core import (QgsRasterLayer, QgsVectorLayer, QgsProject,
QgsCoordinateReferenceSystem, Qgis)
from qgis.gui import QgsMessageBar
-from .tablemodel import TableModel, TableView
+from .tablemodel import TableModel
from .service_data import ServiceData, Localstore, ApiKey
import re
@@ -238,7 +237,9 @@ def initGui(self):
self.dlg.uListOptions.itemClicked.connect(self.showSelectedOption)
self.dlg.uListOptions.itemClicked.emit(self.dlg.uListOptions.item(0))
self.curr_list_wid_index=0
-
+
+ model = QStandardItemModel()
+ self.dlg.uCRSCombo.setModel(model)
self.dlg.uCRSCombo.currentIndexChanged.connect(self.layerCrsSelected)
self.dlg.uLabelWarning.setStyleSheet('color:red')
@@ -541,9 +542,17 @@ def showSelectedOption(self, item):
self.dlg.uStackedWidget.setCurrentIndex(2)
def layerCrsSelected(self):
- self.selected_crs = str(self.dlg.uCRSCombo.currentText())
- if self.selected_crs:
- self.selected_crs_int = int(self.selected_crs.strip('EPSG:'))
+ """
+ Track the user selected crs. Check validity to
+ ensure only well formed crs are provided.
+ """
+
+ valid = re.compile('^EPSG\:\d+')
+ crs_text = self.dlg.uCRSCombo.currentText()
+ if valid.match(crs_text):
+ self.selected_crs = str(self.dlg.uCRSCombo.currentText())
+ if self.selected_crs:
+ self.selected_crs_int = int(self.selected_crs.strip('EPSG:'))
def getPreview(self, res, res_timeout):
"""
@@ -706,7 +715,7 @@ def importDataset(self):
Import the selected dataset to QGIS
"""
- if not self.layers_loaded:
+ if not self.layers_loaded and not self.data_type == 'table':
self.setProjectSRID()
if self.service == "WFS":
diff --git a/linz-data-importer/metadata.txt b/linz-data-importer/metadata.txt
index 2e6933d..eac4d2c 100644
--- a/linz-data-importer/metadata.txt
+++ b/linz-data-importer/metadata.txt
@@ -18,7 +18,11 @@ repository=https://github.com/linz/linz-data-importer
# Recommended items:
# Uncomment the following line and add your changelog:
-changelog= Upgraded to QGIS3 API
+changelog= v2.0.1-beta:
+ - #48 mac osx text encoding
+ - #47 crs filtering
+v2.0.0-beta:
+ - Upgrade to QGIS3 API
# Tags are comma separated with spaces allowed
tags=wmts, wms, wfs, webservice, web, LINZ, LDS, MFE, Stats NZ, LRIS, NZDF
diff --git a/linz-data-importer/plugin_upload.py b/linz-data-importer/plugin_upload.py
deleted file mode 100644
index 8a23854..0000000
--- a/linz-data-importer/plugin_upload.py
+++ /dev/null
@@ -1,107 +0,0 @@
-#!/usr/bin/env python
-# coding=utf-8
-"""This script uploads a plugin package on the server.
- Authors: A. Pasotti, V. Picavet
- git sha : $TemplateVCSFormat
-"""
-
-import sys
-import getpass
-import xmlrpclib
-from optparse import OptionParser
-
-# Configuration
-PROTOCOL = 'http'
-SERVER = 'plugins.qgis.org'
-PORT = '80'
-ENDPOINT = '/plugins/RPC2/'
-VERBOSE = False
-
-
-def main(parameters, arguments):
- """Main entry point.
-
- :param parameters: Command line parameters.
- :param arguments: Command line arguments.
- """
- address = "%s://%s:%s@%s:%s%s" % (
- PROTOCOL,
- parameters.username,
- parameters.password,
- parameters.server,
- parameters.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(arguments[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" % hide_password(err.url, 0)
- print "HTTP/HTTPS headers: %s" % err.headers
- print "Error code: %d" % err.errcode
- print "Error message: %s" % err.errmsg
- except xmlrpclib.Fault, err:
- print "A fault occurred"
- print "Fault code: %d" % err.faultCode
- print "Fault string: %s" % err.faultString
-
-
-def hide_password(url, start=6):
- """Returns the http url with password part replaced with '*'.
-
- :param url: URL to upload the plugin to.
- :type url: str
-
- :param start: Position of start of password.
- :type start: int
- """
- start_position = url.find(':', start) + 1
- end_position = url.find('@')
- return "%s%s%s" % (
- url[:start_position],
- '*' * (end_position - start_position),
- url[end_position:])
-
-
-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")
- options, args = parser.parse_args()
- if len(args) != 1:
- print "Please specify zip file.\n"
- parser.print_help()
- sys.exit(1)
- if not options.server:
- options.server = SERVER
- if not options.port:
- options.port = PORT
- if not options.username:
- # interactive mode
- username = getpass.getuser()
- print "Please enter user name [%s] :" % username,
- res = raw_input()
- if res != "":
- options.username = res
- else:
- options.username = username
- if not options.password:
- # interactive mode
- options.password = getpass.getpass()
- main(options, args)
diff --git a/linz-data-importer/service_data.py b/linz-data-importer/service_data.py
index 22de951..92577d5 100644
--- a/linz-data-importer/service_data.py
+++ b/linz-data-importer/service_data.py
@@ -24,7 +24,7 @@
from owslib.wmts import WebMapTileService
from owslib.util import ServiceException
-from qgis.core import QgsMessageLog, QgsApplication
+from qgis.core import QgsApplication
import os.path
@@ -40,6 +40,7 @@
from qgis.PyQt.QtCore import QSettings
+
class ApiKey(object):
"""
Store API Keys for each domain. Required to
@@ -166,8 +167,6 @@ def delAllLocalServiceXML(self, services=['wms','wfs','wmts']):
file = os.path.join(dir, f)
self.delLocalSeviceXML(file)
-
-
def purgeCache(self):
"""
Delete all cached documents but the
@@ -281,7 +280,7 @@ def isEnabled(self):
def getServiceData(self):
"""
- Select method to obtain capbilties doc.
+ Select method to obtain capabilities doc.
Either via localstore or internet
"""
@@ -381,30 +380,45 @@ def getServiceXml(self):
elif hasattr(e, 'code'):
self.err = 'Error: ({0}) {1}'.format(self.domain, e.reason)
-
+
+ def sortCrs(self):
+ # wms returns some no valid crs values
+ valid = re.compile('^EPSG\:\d+')
+ self.crs = [s for s in self.crs if valid.match(s)]
+ # sort
+ self.crs.sort(key = lambda x: int(x.split(':')[1]))
+
def formatForUI(self):
"""
Format the service data to display in the UI
"""
-
+
+ wms_crs = []
service_data = []
cont = self.obj.contents
-
for dataset_id, dataset_obj in cont.items():
- crs=[]
+ self.crs=[]
full_id = re.search(r'([aA-zZ]+\\.[aA-zZ]+\\.[aA-zZ]+\\.[aA-zZ]+\\:)?(?P[aA-zZ]+)-(?P[0-9]+)', dataset_obj.id)
type = full_id.group('type')
id = full_id.group('id')
# Get and standarise espg codes
if self.service == 'wmts':
- crs = dataset_obj.tilematrixsets
+ self.crs = dataset_obj.tilematrixsets
+ self.sortCrs()
elif self.service in ('wfs'):
- crs = dataset_obj.crsOptions
- crs = ['EPSG:{0}'.format(item.code) for item in crs]
+ self.crs = dataset_obj.crsOptions
+ self.crs = ['EPSG:{0}'.format(item.code) for item in self.crs]
+ self.sortCrs()
elif self.service in ('wms'):
- crs = dataset_obj.crsOptions
- crs = ['EPSG:{0}'.format(item.strip('urn:ogc:def:crs:EPSG::')) for item in crs]
+ if wms_crs:
+ self.crs = wms_crs
+ else:
+ self.crs = dataset_obj.crsOptions
+ self.sortCrs()
+ wms_crs = self.crs
+
service_data.append([self.domain, type, self.service.upper(), id,
- dataset_obj.title, dataset_obj.abstract, crs])
+ dataset_obj.title, dataset_obj.abstract, self.crs])
self.info = service_data
+
diff --git a/linz-data-importer/tablemodel.py b/linz-data-importer/tablemodel.py
index f1f8c2c..f51f1dd 100644
--- a/linz-data-importer/tablemodel.py
+++ b/linz-data-importer/tablemodel.py
@@ -16,36 +16,37 @@
"""
from builtins import str
-from qgis.PyQt.QtCore import QAbstractTableModel, Qt
-from qgis.PyQt.QtWidgets import QTableView
+from qgis.PyQt.QtCore import QAbstractTableModel, Qt, QSortFilterProxyModel
+from qgis.PyQt.QtWidgets import QComboBox, QApplication, QCompleter
+from qgis.PyQt.QtGui import QStandardItem
import sys
-
-class TableView(QTableView):
-
- """
- :param QTableView: Inherits from QtGui.QWidget
- :param QTableView: QtGui.QTableView()
- """
-
- def __init__( self, parent=None ):
- """
- Initialise View for AIMS Queues
-
- :param parent: QModelIndex
- :param parent: PyQt4.QtCore.QModelIndex
- """
-
- QTableView.__init__( self, parent )
- # Change default settings
- self.setSelectionBehavior(QAbstractItemView.SelectRows)
- self.horizontalHeader().setStretchLastSection(True)
- self.horizontalHeader().setHighlightSections(False)
-
- self.verticalHeader().setVisible(False)
- self.verticalHeader().setDefaultSectionSize(17)
- self.setSortingEnabled(True)
- self.setEditTriggers(QAbstractItemView.AllEditTriggers)
+## Below model not currently in-use
+# class TableView(QTableView):
+#
+# """
+# :param QTableView: Inherits from QtGui.QWidget
+# :param QTableView: QtGui.QTableView()
+# """
+#
+# def __init__( self, parent=None ):
+# """
+# Initialise View for AIMS Queues
+#
+# :param parent: QModelIndex
+# :param parent: PyQt4.QtCore.QModelIndex
+# """
+#
+# QTableView.__init__( self, parent )
+# # Change default settings
+# self.setSelectionBehavior(QAbstractItemView.SelectRows)
+# self.horizontalHeader().setStretchLastSection(True)
+# self.horizontalHeader().setHighlightSections(False)
+#
+# self.verticalHeader().setVisible(False)
+# self.verticalHeader().setDefaultSectionSize(17)
+# self.setSortingEnabled(True)
+# self.setEditTriggers(QAbstractItemView.AllEditTriggers)
class TableModel(QAbstractTableModel):
"""
@@ -62,6 +63,7 @@ def __init__(self, data = [[]], headers = [], parent=None):
:param parent: None
:param parent: None
"""
+
QAbstractTableModel.__init__(self, parent)
self.arraydata = data
self.header = headers
@@ -166,3 +168,90 @@ def flags(self, index):
"""
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
+
+class ExtendedCombobox( QComboBox ):
+ """
+ Overwrite combobox to provide text filtering of
+ combobox list.
+ """
+
+ def __init__(self, parent):
+ """
+ Initialise ExtendedCombobox
+
+ :param parent: Parent of combobox
+ :type parent: PyQt5.QtWidgets.QWidget
+ """
+
+ super(ExtendedCombobox, self).__init__(parent)
+
+ self.setFocusPolicy(Qt.StrongFocus)
+ self.setEditable(True)
+ self.completer = QCompleter(self)
+
+ # always show all completions
+ self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
+ self.pFilterModel = QSortFilterProxyModel(self)
+ self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
+ self.completer.setPopup(self.view())
+ self.setCompleter(self.completer)
+ self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
+ self.completer.activated.connect(self.setTextIfCompleterIsClicked)
+
+ def setModel(self, model):
+ """
+ Set the model to use the Filter model
+
+ :param model: The model to be used by the combobox
+ :type model: PyQt5.QtGui.QStandardItemModel
+ """
+
+ super(ExtendedCombobox, self).setModel(model)
+ self.pFilterModel.setSourceModel(model)
+ self.completer.setModel(self.pFilterModel)
+
+ def setModelColumn(self, column):
+ """
+ :param model: The model to be used by the combobox
+ :type model: PyQt5.QtGui.QStandardItemModel
+ """
+
+ self.completer.setCompletionColumn(column)
+ self.pFilterModel.setFilterKeyColumn(column)
+ super(ExtendedCombobox, self).setModelColumn(column)
+
+ def view(self):
+ """
+ A QListView of items stored in the model
+
+ :return: items stored in the model
+ :rtype: PyQt5.QtWidgets.QListView
+ """
+
+
+ return self.completer.popup()
+
+ def index(self):
+ """
+ Index of the current item in the combobox.
+
+ :return: index of the current item
+ :rtype: int
+ """
+
+ return self.currentIndex()
+
+ def setTextIfCompleterIsClicked(self, text):
+ """
+ :param text: The current text of the qlineedit
+ :type text: str
+
+ If the combobx lineedit is clicked, set the lineedits
+ current item as the combobox's current item
+ """
+
+ if text:
+ index = self.findText(text)
+ self.setCurrentIndex(index)
+
+
diff --git a/linz-data-importer/tests/test_ldi_integration.py b/linz-data-importer/tests/test_ldi_integration.py
index 4f6867e..618a16d 100644
--- a/linz-data-importer/tests/test_ldi_integration.py
+++ b/linz-data-importer/tests/test_ldi_integration.py
@@ -227,7 +227,7 @@ def tearDownClass(cls):
"""
# Runs at TestCase teardown.
- QSettings().setValue('linz_data_importer/apikey', cls.testers_keys)
+ QSettings().setValue('linz_data_importer/apikeys', cls.testers_keys)
def setUp(self):
"""
@@ -333,6 +333,17 @@ def test_all_services(self):
self.assertEqual(sorted([u'WMS', u'WFS', u'WMTS']),
sorted(list(data_types)))
+
+ def test_crs_combo_filter(self):
+ """
+ Test the importing of WMS layers into QGIS
+ """
+
+ #set text
+ self.ldi.dlg.uCRSCombo.lineEdit().setText('ESPG:2193')
+ #check that the lineEdit set the correct item in combobox
+ self.assertEqual('ESPG:2193', self.ldi.dlg.uCRSCombo.currentText())
+
# def suite():
# suite = unittest.TestSuite()
# suite.addTests(unittest.makeSuite(ApiKeyTest, 'test'))
diff --git a/linz-data-importer/tests/test_ldi_plugin.py b/linz-data-importer/tests/test_ldi_plugin.py
index 6b472e3..fcb94f3 100644
--- a/linz-data-importer/tests/test_ldi_plugin.py
+++ b/linz-data-importer/tests/test_ldi_plugin.py
@@ -44,12 +44,12 @@ def setUpClass(cls):
# """Runs at TestCase init."""
# Get the test executors current key so that
# We can revert back to when tests are complete
- cls.testers_keys = QSettings().value('linz-data-importer/apikeys')
+ cls.testers_keys = QSettings().value('linz_data_importer/apikeys')
@classmethod
def tearDownClass(cls):
# Runs at TestCase teardown.
- QSettings().setValue('linz-data-importer/apikey', cls.testers_keys)
+ QSettings().setValue('linz_data_importer/apikeys', cls.testers_keys)
def copyTestData(self):
"""