from AimsUI.AimsClient.Address import Address from AimsUI.LayerManager import LayerManager from NewAddressDialog import NewAddressDialog from AimsUI.DelAddressTool import DelAddressTool from AimsUI.MoveAddressTool import MoveAddressTool from AimsUI.CreateNewAddressTool import CreateNewAddressTool from AimsUI.UpdateAddressTool import UpdateAddressTool from AimsUI.LineageTool import LineageTool from AimsUI.GetRclTool import GetRcl from AimsUI.AimsClient.AimsApi import AimsApi from AimsQueueWidget import AimsQueueWidget from AddressList import AddressList from AimsUI import AimsLogging from AimsUI.AimsLogging import Logger aimslog = Logger.setup() class Controller(QObject): '''For future use with multiple objects requesting address/layers etc''' _instance = None def __init__(self, iface): QObject.__init__(self) self._iface = iface self.api = AimsApi() self.user = self.api.user self._alist = AddressList() self._currentAddress = None self._queues = None self._currentMapTool = None self.actions = []
import re #from Test._QGisInterface import QgisInterface from AimsService_Mock import ASM from AimsUI.LayerManager import LayerManager, InvalidParameterException from AimsUI.AimsClient.Gui.Controller import Controller from AimsUI.AimsLogging import Logger from Database_Test import DCONF from AimsUI.AimsClient import Database from mock import Mock, patch QtCore.QCoreApplication.setOrganizationName('QGIS') QtCore.QCoreApplication.setApplicationName('QGIS2') testlog = Logger.setup('test') LM_QMLR = 'AimsUI.LayerManager.QgsMapLayerRegistry' LM_QDSU = 'AimsUI.LayerManager.QgsDataSourceURI' LM_QVL = 'AimsUI.LayerManager.QgsVectorLayer' LM_QDP = 'AimsUI.LayerManager.QgsDataProvider' LM_QF = 'AimsUI.LayerManager.QgsFeature' LM_QG = 'AimsUI.LayerManager.QgsGeometry' LM_QP = 'AimsUI.LayerManager.QgsPoint' LM_coords = 'AimsUI.LayerManager.createFeaturesLayers.coords' LCONF = {'id':'rcl', 'schema':'aims_schema', 'table':'aims_table', 'key':'id', 'estimated':True, 'where':'', 'displayname':'aims_layer'} def getLConf(replace=None): if replace and isinstance(replace,dict):
class LayerManager(QObject): """ Managers the loading and updating of AIMS data as served by the API and LINZ reference data. """ # logging global uilog uilog = Logger.setup(lf='uiLog') _propBaseName = 'AimsClient.' _styledir = join(dirname(abspath(__file__)), 'styles') _addressLayerId = 'adr' addressLayerAdded = pyqtSignal(QgsMapLayer, name="addressLayerAdded") addressLayerRemoved = pyqtSignal(name="addressLayerRemoved") def __init__(self, iface, controller): """ Register the Layer manager with the Data Manager observer pattern therefore to be notified of new Data Manager data. """ QObject.__init__(self) self._iface = iface self._controller = controller self._canvas = self._iface.mapCanvas() self._statusBar = iface.mainWindow().statusBar() self._controller.uidm.register(self) self.rData = None self.prevExt = None self.prevRdata = None self._adrLayer = None self._rclLayer = None self._parLayer = None self._pprLayer = None # pending parcel self._lprLayer = None # labels, parcels self._appLayer = None # view = par_id, appellation self._locLayer = None self._revLayer = None self._extEvent = False QgsMapLayerRegistry.instance().layerWillBeRemoved.connect( self.checkRemovedLayer) QgsMapLayerRegistry.instance().layerWasAdded.connect( self.checkNewLayer) def initialiseExtentEvent(self): """ When the plugin is enabled (Via the .Controller()) QGIS extentChanged signal connected to setbbox method """ self._canvas.extentsChanged.connect(self.setbbox) self._extEvent = True def disconnectExtentEvent(self): """ At plugin unload, disconnect the extent changed / bbox event """ #receiversCount = self.receivers(SIGNAL("self._canvas.extentsChanged()")) #if receiversCount > 0: if self._extEvent: self._canvas.extentsChanged.disconnect(self.setbbox) self._extEvent = False def layerId(self, layer): """ Takes a layer as Vector Layer as a parameter and returns the layers Id as used by the plugin to refer to the layers @param layer: AIMS qgis layer @type layer: qgis._core.QgsVectorLayer @return: AIMS layer Id as used to refered to AIMS layers @rtype: string """ idprop = self._propBaseName + 'Id' res = layer.customProperty(idprop) if isinstance(res, QVariant): res = res.toPyObject() return str(res) def setLayerId(self, layer, id): """ Set id property for layer @param layer: AIMS qgis layer @type layer: qgis._core.QgsVectorLayer @param id: AIMS Layer id @type id: string """ if id and isinstance(id, str): idprop = self._propBaseName + 'Id' layer.setCustomProperty(idprop, id) def layers(self): """ Iterates over and yeilds all QGIS Map Layers @yield: qgis._core.QgsVectorLayer """ for layer in QgsMapLayerRegistry.instance().mapLayers().values(): yield layer def addressLayer(self): """ Return the AIMS Address Layer @return: AIMS Address Layer @rtype: qgis._core.QgsVectorLayer """ return self._adrLayer def rclLayer(self): """ Return the Road Centre Line Layer @return: Road Centre Line Layer @rtype: qgis._core.QgsVectorLayer """ return self._rclLayer def revLayer(self): """ Return the Review Layer @return: AIMS Review Layer @rtype: qgis._core.QgsVectorLayer """ return self._revLayer def appLayer(self): """ Return the Appellation View Layer @return: AIMS Review Layer @rtype: qgis._core.QgsVectorLayer """ return self._appLayer def lprLayer(self): """ Return the (Label) Parcel Layer @return: AIMS Review Layer @rtype: qgis._core.QgsVectorLayer """ return self._lprLayer def checkRemovedLayer(self, id): """ If layer removed set layer references to None @param id: The id of the layer removed @type id: string """ if self._adrLayer and self._adrLayer.id() == id: self._adrLayer = None self.addressLayerRemoved.emit() if self._rclLayer and self._rclLayer.id() == id: self._rclLayer = None if self._parLayer and self._parLayer.id() == id: self._parLayer = None if self._pprLayer and self._pprLayer.id() == id: self._pprLayer = None if self._lprLayer and self._lprLayer.id() == id: self._lprLayer = None if self._revLayer and self._revLayer.id() == id: self._revLayer = None def checkNewLayer(self, layer): """ Assign an AIMS QgsVectorLayer to a LayerManager layer property @param layer: New AIMS vector layer @type layer: qgis._core.QgsVectorLayer """ layerId = self.layerId(layer) if not layerId: return if layerId == self._addressLayerId: newlayer = self._adrLayer == None self._adrLayer = layer if newlayer: self.addressLayerAdded.emit(layer) elif layerId == 'rcl': self._rclLayer = layer elif layerId == 'par': self._parLayer = layer elif layerId == 'ppr': self._pprLayer = layer elif layerId == 'lpr': self._lprLayer = layer elif layerId == 'rev': self._revLayer = layer elif layerId == 'app': self._appLayer = layer def findLayer(self, name): """ Assign an AIMS QgsVectorLayer to a LayerManager layer property @param name: layer name @type name: string @return: Layer that matches id @rtype: qgis._core.QgsVectorLayer """ for layer in self.layers(): if self.layerId(layer) == name: return layer return None def styleLayer(self, layer, id): """ Set layer style as per the relevant .qml file @param layer: AIMS Layer @type layer: qgis._core.QgsVectorLayer @param id: Layer Id @type id: string """ try: layer.loadNamedStyle(join(self._styledir, id + '_style.qml')) except: pass def installLayer(self, id, schema, table, geom, key, estimated, where, displayname): """ Install AIMS postgres reference layers @param id: Layer id @type id: string @param schema: Database Schema Name @type schema: string @param table: Database Table Name @type table: string @param key: Primary Key @type key: string @param estimated: Estimate Meta Data @type estimated: boolean @param where: SQL where cluase @type where: string @param displayname: Layer Label @type displayname: string @return: AIMS Layer @rtype: qgis._core.QgsVectorLayer """ layer = self.findLayer(id) if layer: legend = self._iface.legendInterface() if not legend.isLayerVisible(layer): legend.setLayerVisible(layer, True) return layer self._statusBar.showMessage("Loading layer " + displayname) try: uri = QgsDataSourceURI() uri.setConnection(Database.host(), str(Database.port()), Database.database(), Database.user(), Database.password()) uri.setDataSource(schema, table, geom, where, key) uri.setUseEstimatedMetadata(True) layer = QgsVectorLayer(uri.uri(), displayname, "postgres") self.setLayerId(layer, id) self.styleLayer(layer, id) QgsMapLayerRegistry.instance().addMapLayer(layer) finally: self._statusBar.showMessage("") return layer def installRefLayers(self): """ Install AIMS postgres reference layers """ parQuery = """ST_GeometryType(shape) IN ('ST_MultiPolygon', 'ST_Polygon') AND status = 'CURR' AND toc_code = 'PRIM'""" pendParQuery = """ST_GeometryType(shape) IN ('ST_MultiPolygon', 'ST_Polygon') AND status = 'PEND' AND toc_code = 'PRIM'""" refLayers = { 'par': ('par', 'bde', 'crs_parcel', 'shape', 'id', True, parQuery, 'Parcels'), 'lpr': ('lpr', 'bde', 'crs_parcel', 'shape', 'id', True, parQuery, 'Parcels (Labels)'), 'app': ('app', 'bde', 'parcel_appellation_view', None, 'par_id', True, "", 'Appellation View'), 'rcl': ('rcl', 'roads', 'simple_road_name_view', 'shape', 'gid', True, "", 'Roads'), 'ppr': ('ppr', 'bde', 'crs_parcel', 'shape', 'id', True, pendParQuery, 'Pending Parcels') } for layerId, layerProps in refLayers.items(): if not self.findLayer(layerId): self.installLayer(*layerProps) # A Relation is required to label # parcels with an appellation if self.lprLayer() and self.appLayer(): self.parRelation() def parRelation(self): """ Parcel labeling requires a relation between app (view = parcel ids and their associated appellation) and lpr (crs parcel layer for labeling purposes) """ rel = QgsRelation() rel.setReferencingLayer(self.lprLayer().id()) rel.setReferencedLayer(self.appLayer().id()) rel.addFieldPair('id', 'par_id') rel.setRelationId(self._propBaseName + 'appellation_rel') rel.setRelationName('Appellation Relation') QgsProject.instance().relationManager().addRelation(rel) def addLayerFields(self, layer, provider, id, fields): """ Add fields to a layer @param layer: AIMS vector layer @type layer: qgis._core.QgsVectorLayer @param provider: Data Provider @type provider: qgis._core.QgsVectorDataProvider @param id: Layer id @type id: string @param fields: list of field names @type fields: list """ provider.addAttributes(fields) layer.updateFields() self.styleLayer(layer, id) QgsMapLayerRegistry.instance().addMapLayer(layer) def installAimsLayer(self, id, displayname): """ Install AIMS feature and review layers @param id: Layer id @type id: string @param displayname: displayname @type displayname: string """ if id == 'adr' and not self._adrLayer: layer = QgsVectorLayer("Point?crs=EPSG:4167", displayname, "memory") self.setLayerId(layer, id) provider = layer.dataProvider() self.addLayerFields(layer, provider, id, [ QgsField(layerAttName, QVariant.String) for layerAttName in Mapping.adrLayerObjMappings.keys() ]) elif id == 'rev' and not self._revLayer: layer = QgsVectorLayer("Point?crs=EPSG:4167", displayname, "memory") self.setLayerId(layer, id) provider = layer.dataProvider() self.addLayerFields(layer, provider, id, [ QgsField('AimsId', QVariant.String), QgsField('AddressNumber', QVariant.String), QgsField('Action', QVariant.String) ]) else: return layer.updateFields() QgsMapLayerRegistry.instance().addMapLayer(layer) def isVisible(self, layer): """ Test if a layer is visible @param layer: AIMS vector layer @type layer: qgis._core.QgsVectorLayer @return: True if Layer is visible @rtype: boolean """ if layer.hasScaleBasedVisibility(): if layer.maximumScale() > self._canvas.scale( ) and layer.minimumScale() < self._canvas.scale(): return True else: return False else: return True def removeFeatures(self, layer): """ Remove all features from a layer @param layer: AIMS vector layer @type layer: qgis._core.QgsVectorLayer """ ids = [f.id() for f in layer.getFeatures()] layer.startEditing() for fid in ids: layer.deleteFeature(fid) layer.commitChanges() def addToLayer(self, rData, layer): """ Add aims review features to reveiw layer @param rData: Review Data @type rData: dictionary @param layer: AIMS vector layer @type layer: qgis._core.QgsVectorLayer """ provider = layer.dataProvider() for k, reviewItem in rData.items(): if hasattr(reviewItem, '_queueStatus'): if reviewItem._queueStatus in ('Declined, Accepted'): continue fet = QgsFeature() if reviewItem._changeType in ('Update', 'Add') or reviewItem.meta.requestId: point = reviewItem.getAddressPositions( )[0]._position_coordinates else: try: point = reviewItem.meta.entities[0].getAddressPositions( )[0]._position_coordinates except: uilog.error(' *** ERROR *** ') fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(point[0], point[1]))) fet.setAttributes( [k, reviewItem.getFullNumber(), reviewItem._changeType]) provider.addFeatures([fet]) layer.updateExtents() def updateReviewLayer(self): """ Update review layer """ id = 'rev' layer = self.findLayer(id) if not layer: return self.removeFeatures(layer) rData = self._controller.uidm.combinedReviewData() uilog.info(' *** DATA *** {} review items being loaded '.format( len(rData))) self.addToLayer(rData, layer) def bboxWithPrevious(self, ext): """ Test if the emitted canvas extent is within the previous extent @param ext: canvas extent @type ext: gis._core.QgsRectangle @rtype: boolean """ if not self.prevExt: return False elif QgsRectangle.contains(self.prevExt, ext): return True else: return False def setbbox(self): """ Triggered by extent change - Set BBox in UIDataManager """ id = self._addressLayerId layer = self.findLayer(id) ext = self._canvas.extent() if self._canvas.scale() > layer.maximumScale( ) or self.bboxWithPrevious(ext): return uilog.info(' *** BBOX *** {} '.format(ext.toString())) self._controller.uidm.setBbox(sw=(ext.xMinimum(), ext.yMinimum()), ne=(ext.xMaximum(), ext.yMaximum())) self.prevExt = ext def getAimsFeatures(self): """ Retrieve most AIMS Features as held in UiDataManager """ featureData = self._controller.uidm.featureData() if featureData: uilog.info(' *** DATA *** {} AIMS features received '.format( len(featureData))) self.updateFeaturesLayer(featureData) def notify(self, feedType): """ Notify registered to UiDataManager @param feedType: Type of AIMS API @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ uilog.info('*** NOTIFY *** Notify A[{}]'.format(feedType)) if feedType == FEEDS['AF']: self.getAimsFeatures() elif feedType == FEEDS['AR'] or feedType == FEEDS['GR']: self.updateReviewLayer() def updateFeaturesLayer(self, featureData): """ Add features to AIMS Address feature layer @param featureData: feature feed data @type featureData: dictionary """ id = self._addressLayerId layer = self.findLayer(id) # ensure the user has not removed the layer if not layer: self.installAimsLayer(id, 'AIMS Features') layer = self.findLayer(id) if not self.isVisible(layer): return legend = self._iface.legendInterface() if not legend.isLayerVisible(layer): legend.setLayerVisible(layer, True) # remove current features self.removeFeatures(layer) uilog.info(' *** CANVAS *** Adding Features') for feature in featureData.itervalues(): fet = QgsFeature() point = feature.getAddressPositions()[0]._position_coordinates fet.setGeometry(QgsGeometry.fromPoint(QgsPoint(point[0], point[1]))) fet.setAttributes([ getattr(feature, v[0]) if hasattr(feature, v[0]) else '' for v in Mapping.adrLayerObjMappings.values() ]) if hasattr( getattr(feature, '_addressedObject_addressPositions')[0], '_positionType'): # If positionType update field index 30. Would rather use the explicit name... fet.setAttribute( 30, feature._addressedObject_addressPositions[0]._positionType) layer.dataProvider().addFeatures([fet]) layer.updateExtents() uilog.info(' *** CANVAS *** FEATURES ADDED') @qgsfunction(0, 'QGIS-AIMS-Plugin', register=False) def get_par_app(values, feature, parent): """ Custom labeling function. For labeling parcels with appellation """ layer = None for lyr in QgsMapLayerRegistry.instance().mapLayers().values(): if lyr.name() == "Parcels (Labels)": layer = lyr break rel = layer.referencingRelations(0)[0] feat_rel = rel.getReferencedFeature(feature) if feat_rel: return feat_rel.attribute('appellation') @qgsfunction(0, 'QGIS-AIMS-Plugin', register=False) def get_par_app(values, feature, parent): """ Custom labeling function. For labeling parcels with appellation """ layer = None for lyr in QgsMapLayerRegistry.instance().mapLayers().values(): if lyr.name() == "Parcels (Labels)": layer = lyr break rel = layer.referencingRelations(0)[0] feat_rel = rel.getReferencedFeature(feature) if feat_rel: return feat_rel.attribute('appellation') def registerFunctions(self): """ Register custom function (for labeling) """ QgsExpression.registerFunction(self.get_par_app) def unregisterFunctions(self): """ Unregister custom function (for labeling) """ QgsExpression.unregisterFunction(self.get_par_app.name())
################################################################################ from os.path import dirname, abspath, join from PyQt4.QtCore import * from PyQt4.QtGui import * from qgis.core import * from qgis.gui import * from qgis.utils import qgsfunction import sip from AimsClient import Database from AimsUI.AimsLogging import Logger from AIMSDataManager.AimsUtility import FEEDS from collections import OrderedDict aimslog = Logger.setup() uilog = None sip.setapi('QVariant', 2) class Mapping(): """ The mappings between UI components and AIMS object properties """ adrLayerObjMappings = OrderedDict([ ('addressType', ['_components_addressType', None]), ('fullAddress', ['_components_fullAddress', None]), ('fullAddressNumber', ['_components_fullAddressNumber', None]), ('fullRoadName', ['_components_fullRoadName', None]), ('suburbLocality', ['_components_suburbLocality', None]), ('townCity', ['_components_townCity', None]),
from Test.Database_Test import Test_0_DatabaseSelfTest as DT0 from Test.Database_Test import Test_1_DatabaseTestSetters as DT1 from Test.Database_Test import Test_2_DatabaseConnectivity as DT2 if BM & IMASK['lm']: from Test.LayerManager_Test import Test_0_LayerManagerSelfTest as LMT0 from Test.LayerManager_Test import Test_1_LayerManagerSetters as LMT1 from Test.LayerManager_Test import Test_2_LayerManagerConnection as LMT2 if BM & IMASK['con']: from Test.Controller_Test import Test_0_ControllerSelfTest as CT0 from Test.Controller_Test import Test_1_ControllerTestSetupFunction as CT1 from AimsUI.AimsLogging import Logger testlog = Logger.setup() class FullSuite(unittest.TestSuite): def __init__(self): pass def suite(self): return unittest.TestSuite() def main(): suite = FullSuite().suite() runner = unittest.TextTestRunner() runner.run(suite)
@author: jramsay """ import unittest import inspect import sys import re from AimsService_Mock import ASM # from Test._QGisInterface import QgisInterface from AimsUI.AimsClient.Gui.Controller import Controller from AimsUI.AimsClient.Address import Address from AimsUI.AimsLogging import Logger testlog = Logger.setup("test") class Test_0_ControllerSelfTest(unittest.TestCase): def setUp(self): pass def tearDown(self): pass def test10_selfTest(self): # assertIsNotNone added in 3.1 testlog.debug("Test_0.10 Controller_Test Log") self.assertNotEqual(testlog, None, "Testlog not instantiated") def test20_controllerTest(self):
from Test.Database_Test import Test_0_DatabaseSelfTest as DT0 from Test.Database_Test import Test_1_DatabaseTestSetters as DT1 from Test.Database_Test import Test_2_DatabaseConnectivity as DT2 if BM & IMASK['lm']: from Test.LayerManager_Test import Test_0_LayerManagerSelfTest as LMT0 from Test.LayerManager_Test import Test_1_LayerManagerSetters as LMT1 from Test.LayerManager_Test import Test_2_LayerManagerConnection as LMT2 if BM & IMASK['con']: from Test.Controller_Test import Test_0_ControllerSelfTest as CT0 from Test.Controller_Test import Test_1_ControllerTestSetupFunction as CT1 from AimsUI.AimsLogging import Logger testlog = Logger.setup() class FullSuite(unittest.TestSuite): def __init__(self): pass def suite(self): return unittest.TestSuite() def main(): suite = FullSuite().suite() runner = unittest.TextTestRunner()