Exemplo n.º 1
0
    def __init__(self, iface):
        # QGIS
        self.iface = iface
        self.settings = MySettings()
        self.linkRubber = QgsRubberBand(self.iface.mapCanvas())
        self.featureHighlight = None
        # Relation management
        self.relationManager = QgsProject.instance().relationManager()
        self.relationManager.changed.connect(self.loadRelations)
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationWidgetWrapper = None
        self.editorContext = QgsAttributeEditorContext()
        self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools())

        # GUI
        QDockWidget.__init__(self)
        self.setupUi(self)
        SettingDialog.__init__(self, MySettings(), False, True)
        self.drawButton.setChecked(self.settings.value("drawEnabled"))

        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas())
        self.mapTool.setButton(self.identifyReferencingFeatureButton)

        # Connect signal/slot
        self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged)
        self.mapTool.featureIdentified.connect(self.setReferencingFeature)

        # load relations at start
        self.loadRelations()
Exemplo n.º 2
0
 def loadRelations(self):
     self.deleteWrapper()
     self.disconnectLayer()
     self.relation = QgsRelation()
     self.referencingFeature = QgsFeature()
     self.relationComboBox.currentIndexChanged.disconnect(self.currentRelationChanged)
     self.relationComboBox.clear()
     for relation in self.relationManager.referencedRelations():
         if relation.referencingLayer().hasGeometryType():
             self.relationComboBox.addItem(relation.name(), relation.id())
     self.relationComboBox.setCurrentIndex(-1)
     self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged)
     self.currentRelationChanged(-1)
Exemplo n.º 3
0
    def test_getRelatedFeatures(self):
        referencedLayer = createReferencedLayer()
        referencingLayer = createReferencingLayer()
        QgsMapLayerRegistry.instance().addMapLayers([referencedLayer,referencingLayer])

        rel = QgsRelation()

        rel.setRelationId( 'rel1' )
        rel.setRelationName( 'Relation Number One' )
        rel.setReferencingLayer( referencingLayer.id() )
        rel.setReferencedLayer( referencedLayer.id() )
        rel.addFieldPair( 'foreignkey', 'y' )

        feat = referencedLayer.getFeatures().next()

        it = rel.getRelatedFeatures( feat )

        [ a.attributes() for a in it ] == [[u'test1', 123], [u'test2', 123]]

        QgsMapLayerRegistry.instance().removeAllMapLayers()
Exemplo n.º 4
0
class LinkerDock(QDockWidget, Ui_linker, SettingDialog):
    def __init__(self, iface):
        # QGIS
        self.iface = iface
        self.settings = MySettings()
        self.linkRubber = QgsRubberBand(self.iface.mapCanvas())
        self.featureHighlight = None
        # Relation management
        self.relationManager = QgsProject.instance().relationManager()
        self.relationManager.changed.connect(self.loadRelations)
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationWidgetWrapper = None
        self.editorContext = QgsAttributeEditorContext()
        self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools())

        # GUI
        QDockWidget.__init__(self)
        self.setupUi(self)
        SettingDialog.__init__(self, MySettings(), False, True)
        self.drawButton.setChecked(self.settings.value("drawEnabled"))

        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas())
        self.mapTool.setButton(self.identifyReferencingFeatureButton)

        # Connect signal/slot
        self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged)
        self.mapTool.featureIdentified.connect(self.setReferencingFeature)

        # load relations at start
        self.loadRelations()

    def showEvent(self, QShowEvent):
        self.drawLink()

    def closeEvent(self, e):
        self.iface.mapCanvas().unsetMapTool(self.mapTool)
        self.linkRubber.reset()
        self.deleteHighlight()
        self.deleteWrapper()
        self.disconnectLayer()

    def disconnectLayer(self):
        if self.relation.isValid():
            self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged)
            self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged)
            self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside)

    def runForFeature(self, relationId, layer, feature):
        index = self.relationComboBox.findData(relationId)
        self.relationComboBox.setCurrentIndex(index)
        self.setReferencingFeature(feature)
        self.show()
        if not layer.isEditable():
            self.iface.messageBar().pushMessage("Link It", "Cannot set a new related feature since %s is not editable" % layer.name(), QgsMessageBar.WARNING, 4)
        else:
            self.relationReferenceWidget.mapIdentification()

    @pyqtSlot(name="on_identifyReferencingFeatureButton_clicked")
    def activateMapTool(self):
        self.iface.mapCanvas().setMapTool(self.mapTool)

    def deactivateMapTool(self):
        self.iface.mapCanvas().unsetMapTool(self.mapTool)

    def loadRelations(self):
        self.deleteWrapper()
        self.disconnectLayer()
        self.relation = QgsRelation()
        self.referencingFeature = QgsFeature()
        self.relationComboBox.currentIndexChanged.disconnect(self.currentRelationChanged)
        self.relationComboBox.clear()
        for relation in self.relationManager.referencedRelations():
            if relation.referencingLayer().hasGeometryType():
                self.relationComboBox.addItem(relation.name(), relation.id())
        self.relationComboBox.setCurrentIndex(-1)
        self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged)
        self.currentRelationChanged(-1)

    def currentRelationChanged(self, index):
        # disconnect previous relation
        if self.relation.isValid():
            try:
                self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged)
                self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged)
                self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside)
            except TypeError:
                pass

        self.referencingFeatureLayout.setEnabled(index >= 0)
        relationId = self.relationComboBox.itemData(index)
        self.relation = self.relationManager.relation(relationId)
        self.mapTool.setLayer(self.relation.referencingLayer())
        self.setReferencingFeature()
        # connect
        if self.relation.isValid():
            self.relation.referencingLayer().editingStarted.connect(self.relationEditableChanged)
            self.relation.referencingLayer().editingStopped.connect(self.relationEditableChanged)
            self.relation.referencingLayer().attributeValueChanged.connect(self.layerValueChangedOutside)

    def setReferencingFeature(self, feature=QgsFeature()):
        self.deactivateMapTool()
        self.referencingFeature = QgsFeature(feature)
        self.deleteWrapper()

        # disable relation reference widget if no referencing feature
        self.referencedFeatureLayout.setEnabled(feature.isValid())

        # set line edit
        if not self.relation.isValid() or not feature.isValid():
            self.referencingFeatureLineEdit.clear()
            return
        self.referencingFeatureLineEdit.setText("%s" % feature.id())

        fieldIdx = self.referencingFieldIndex()
        widgetConfig = self.relation.referencingLayer().editorWidgetV2Config(fieldIdx)
        self.relationWidgetWrapper = QgsEditorWidgetRegistry.instance().create("RelationReference",
                                                                               self.relation.referencingLayer(),
                                                                               fieldIdx,
                                                                               widgetConfig,
                                                                               self.relationReferenceWidget,
                                                                               self,
                                                                               self.editorContext)

        self.relationWidgetWrapper.setEnabled(self.relation.referencingLayer().isEditable())
        self.relationWidgetWrapper.setValue(feature[fieldIdx])
        self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged)
        # override field definition to allow map identification
        self.relationReferenceWidget.setAllowMapIdentification(True)
        self.relationReferenceWidget.setEmbedForm(False)

        # update drawn link
        self.highlightReferencingFeature()
        self.drawLink()

    def deleteWrapper(self):
        if self.relationWidgetWrapper is not None:
            self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged)
            self.relationWidgetWrapper.setValue(None)
            del self.relationWidgetWrapper
            self.relationWidgetWrapper = None

    def foreignKeyChanged(self, newKey):
        if not self.relation.isValid() or not self.relation.referencingLayer().isEditable() or not self.referencingFeature.isValid():
            self.drawLink()
            return
        if not self.relation.referencingLayer().editBuffer().changeAttributeValue(self.referencingFeature.id(), self.referencingFieldIndex(), newKey):
            self.iface.messageBar().pushMessage("Link It", "Cannot change attribute value.", QgsMessageBar.CRITICAL)
        self.drawLink()

    def relationEditableChanged(self):
        if self.relationWidgetWrapper is not None:
            self.relationWidgetWrapper.setEnabled(self.relation.isValid() and self.relation.referencingLayer().isEditable())

    def layerValueChangedOutside(self, fid, fieldIdx, value):
        if not self.relation.isValid() or not self.referencingFeature.isValid() or self.relationWidgetWrapper is None:
            return
        # not the correct feature
        if fid != self.referencingFeature.id():
            return
        # not the correct field
        if fieldIdx != self.referencingFieldIndex():
            return
        # widget already has this value
        if value == self.relationWidgetWrapper.value():
            return
        self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged)
        self.relationWidgetWrapper.setValue(value)
        self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged)

    def referencingFieldIndex(self):
        if not self.relation.isValid():
            return -1
        fieldName = self.relation.fieldPairs().keys()[0]
        fieldIdx = self.relation.referencingLayer().fieldNameIndex(fieldName)
        return fieldIdx

    @pyqtSlot(bool, name="on_drawButton_toggled")
    def drawLink(self):
        self.settings.setValue("drawEnabled", self.drawButton.isChecked())
        self.linkRubber.reset()
        if not self.drawButton.isChecked() or not self.referencingFeature.isValid() or not self.relation.isValid():
            return

        referencedFeature = self.relationReferenceWidget.referencedFeature()
        if not referencedFeature.isValid():
            return

        p1 = self.centroid(self.relation.referencedLayer(), referencedFeature)
        p2 = self.centroid(self.relation.referencingLayer(), self.referencingFeature)
        geom = arc(p1, p2)

        self.linkRubber.setToGeometry(geom, None)
        self.linkRubber.setWidth(self.settings.value("rubberWidth"))
        self.linkRubber.setColor(self.settings.value("rubberColor"))
        self.linkRubber.setLineStyle(Qt.DashLine)

    def centroid(self, layer, feature):
        geom = feature.geometry()
        if geom.type() == QGis.Line:
            geom = geom.interpolate(geom.length()/2)
        else:
            geom = geom.centroid()
        return self.iface.mapCanvas().mapSettings().layerToMapCoordinates(layer, geom.asPoint())

    @pyqtSlot(name="on_highlightReferencingFeatureButton_clicked")
    def highlightReferencingFeature(self):
        self.deleteHighlight()
        if not self.relation.isValid() or not self.referencingFeature.isValid():
            return
        
        self.featureHighlight = QgsHighlight(self.iface.mapCanvas(), self.referencingFeature.geometry(), self.relation.referencingLayer())
        settings = QSettings()
        color = QColor( settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name()))
        alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha()))
        bbuffer = float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM))
        minWidth = float(settings.value("/Map/highlight/minWidth", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM))
        
        self.featureHighlight.setColor(color)
        color.setAlpha(alpha)
        self.featureHighlight.setFillColor(color)
        self.featureHighlight.setBuffer(bbuffer)
        self.featureHighlight.setMinWidth(minWidth)
        self.featureHighlight.show()

        timer = QTimer(self)
        timer.setSingleShot(True)
        timer.timeout.connect(self.deleteHighlight)
        timer.start(3000)

    def deleteHighlight(self):
        if self.featureHighlight:
            del self.featureHighlight
        self.featureHighlight = None
Exemplo n.º 5
0
 def create(self):
     relation = QgsRelation()
     relation.setId(self.name)
     relation.setName(self.name)
     relation.setReferencingLayer(self.referencing_layer.create().id())
     relation.setReferencedLayer(self.referenced_layer.create().id())
     relation.addFieldPair(self.referencing_field, self.referenced_field)
     self.qgis_relation = relation
     return relation
Exemplo n.º 6
0
    def test_getRelatedFeaturesWithQuote(self):
        rel = QgsRelation()

        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('fldtxt', 'x')

        feat = self.referencedLayer.getFeature(3)

        it = rel.getRelatedFeatures(feat)
        assert next(it).attributes() == ["foobar'bar", 124]
Exemplo n.º 7
0
    def testDuplicateFeature(self):
        """ test duplicating a feature """

        project = QgsProject().instance()

        # LAYERS
        # - add first layer (parent)
        layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer",
                                "parentlayer", "memory")
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # -  set the value for the copy
        layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)"))
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # - add second layer (child)
        layer2 = QgsVectorLayer("Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer",
                                "childlayer", "memory")
        # > check second layer (child)
        self.assertTrue(layer2.isValid())
        # - add layers
        project.addMapLayers([layer1, layer2])

        # FEATURES
        # - add 2 features on layer1 (parent)
        l1f1orig = QgsFeature()
        l1f1orig.setFields(layer1.fields())
        l1f1orig.setAttributes(["F_l1f1", 100])
        l1f2orig = QgsFeature()
        l1f2orig.setFields(layer1.fields())
        l1f2orig.setAttributes(["F_l1f2", 101])
        # > check by adding features
        self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig, l1f2orig]))
        # add 4 features on layer2 (child)
        l2f1orig = QgsFeature()
        l2f1orig.setFields(layer2.fields())
        l2f1orig.setAttributes(["F_l2f1", 201, 100])
        l2f2orig = QgsFeature()
        l2f2orig.setFields(layer2.fields())
        l2f2orig.setAttributes(["F_l2f2", 202, 100])
        l2f3orig = QgsFeature()
        l2f3orig.setFields(layer2.fields())
        l2f3orig.setAttributes(["F_l2f3", 203, 100])
        l2f4orig = QgsFeature()
        l2f4orig.setFields(layer2.fields())
        l2f4orig.setAttributes(["F_l2f4", 204, 101])
        # > check by adding features
        self.assertTrue(layer2.dataProvider().addFeatures([l2f1orig, l2f2orig, l2f3orig, l2f4orig]))

        # RELATION
        # - create the relationmanager
        relMgr = project.relationManager()
        # - create the relation
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('childrel')
        rel.setReferencingLayer(layer2.id())
        rel.setReferencedLayer(layer1.id())
        rel.addFieldPair('foreign_key', 'pkid')
        rel.setStrength(QgsRelation.Composition)
        # > check relation
        self.assertTrue(rel.isValid())
        # - add relation
        relMgr.addRelation(rel)
        # > check if referencedLayer is layer1
        self.assertEqual(rel.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(rel.referencingLayer(), layer2)
        # > check if the layers are correct in relation when loading from relationManager
        relations = project.relationManager().relations()
        relation = relations[list(relations.keys())[0]]
        # > check if referencedLayer is layer1
        self.assertEqual(relation.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(relation.referencingLayer(), layer2)
        # > check the relatedfeatures

        '''
        # testoutput 1
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        print( "\n--------------------------")

        print( "\nFeatures on layer1")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2")
        for f in layer2.getFeatures():
            print( f.attributes() )
        '''

        # DUPLICATION
        # - duplicate feature l1f1orig with children
        layer1.startEditing()
        results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig, project, 0)

        # > check if name is name of duplicated (pk is different)
        result_feature = results[0]
        self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt'))
        # > check duplicated child layer
        result_layer = results[1].layers()[0]
        self.assertEqual(result_layer, layer2)
        #  > check duplicated child features
        self.assertTrue(results[1].duplicatedFeatures(result_layer))

        '''
        # testoutput 2
        print( "\nFeatures on layer1 (after duplication)")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2 (after duplication)")
        for f in layer2.getFeatures():
            print( f.attributes() )
            
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        '''

        # > compare text of parent feature
        self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt'))

        # - create copyValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(result_feature)
        copyValueList = []
        while relfeatit.nextFeature(childFeature):
            copyValueList.append(childFeature.attribute('fldtxt'))
        # - create origValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(l1f1orig)
        origValueList = []
        while relfeatit.nextFeature(childFeature):
            origValueList.append(childFeature.attribute('fldtxt'))

        # - check if the ids are still the same
        self.assertEqual(copyValueList, origValueList)
Exemplo n.º 8
0
    def test_fieldPairs(self):
        rel = QgsRelation()

        rel.setRelationId("rel1")
        rel.setRelationName("Relation Number One")
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair("foreignkey", "y")

        assert rel.fieldPairs() == {"foreignkey": "y"}
def import_in_qgis(gmlas_uri, provider, schema=None):
    """Imports layers from a GMLAS file in QGIS with relations and editor widgets

    @param gmlas_uri connection parameters
    @param provider name of the QGIS provider that handles gmlas_uri parameters (postgresql or spatialite)
    @param schema name of the PostgreSQL schema where tables and metadata tables are
    """
    if schema is not None:
        schema_s = schema + "."
    else:
        schema_s = ""

    ogr.UseExceptions()
    drv = ogr.GetDriverByName(provider)
    ds = drv.Open(gmlas_uri)
    if ds is None:
        raise RuntimeError("Problem opening {}".format(gmlas_uri))

    # get list of layers
    sql = "select o.*, g.f_geometry_column, g.srid from {}_ogr_layers_metadata o left join geometry_columns g on g.f_table_name = o.layer_name".format(
        schema_s)

    l = ds.ExecuteSQL(sql)
    layers = {}
    for f in l:
        ln = f.GetField("layer_name")
        if ln not in layers:
            layers[ln] = {
                'uid': f.GetField("layer_pkid_name"),
                'category': f.GetField("layer_category"),
                'xpath': f.GetField("layer_xpath"),
                'parent_pkid': f.GetField("layer_parent_pkid_name"),
                'srid': f.GetField("srid"),
                'geometry_column': f.GetField("f_geometry_column"),
                '1_n': [],  # 1:N relations
                'layer_id': None,
                'layer_name': ln,
                'layer': None,
                'fields': []
            }
        else:
            # additional geometry columns
            g = f.GetField("f_geometry_column")
            k = "{} ({})".format(ln, g)
            layers[k] = dict(layers[ln])
            layers[k]["geometry_column"] = g

    crs = QgsCoordinateReferenceSystem("EPSG:4326")
    for ln in sorted(layers.keys()):
        lyr = layers[ln]
        g_column = lyr["geometry_column"] or None
        l = _qgis_layer(gmlas_uri,
                        schema,
                        lyr["layer_name"],
                        g_column,
                        provider,
                        qgis_layer_name=ln)
        if not l.isValid():
            raise RuntimeError("Problem loading layer {} with {}".format(
                ln, l.source()))
        if lyr["srid"]:
            crs = QgsCoordinateReferenceSystem("EPSG:{}".format(lyr["srid"]))
        l.setCrs(crs)
        QgsProject.instance().addMapLayer(l)
        layers[ln]['layer_id'] = l.id()
        layers[ln]['layer'] = l

    # add 1:1 relations
    relations_1_1 = []
    sql = """
select
  layer_name, field_name, field_related_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.parent_element_name = f.field_name
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs=1
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            rel = QgsRelation()
            rel.setId('1_1_' + f.GetField('layer_name') + '_' +
                      f.GetField('field_name'))
            rel.setName('1_1_' + f.GetField('layer_name') + '_' +
                        f.GetField('field_name'))
            # parent layer
            rel.setReferencingLayer(
                layers[f.GetField('layer_name')]['layer_id'])
            # child layer
            rel.setReferencedLayer(
                layers[f.GetField('field_related_layer')]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('field_name'),
                             f.GetField('child_pkid'))
            #rel.generateId()
            if rel.isValid():
                relations_1_1.append(rel)

    # add 1:N relations
    relations_1_n = []
    sql = """
select
  layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs>1
-- junctions - 1st way
union all
select
  layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
-- junctions - 2nd way
union all
select
  field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            rel = QgsRelation()
            rel.setId('1_n_' + f.GetField('layer_name') + '_' +
                      f.GetField('child_layer') + '_' +
                      f.GetField('parent_pkid') + '_' +
                      f.GetField('child_pkid'))
            rel.setName(f.GetField('child_layer'))
            # parent layer
            rel.setReferencedLayer(
                layers[f.GetField('layer_name')]['layer_id'])
            # child layer
            rel.setReferencingLayer(
                layers[f.GetField('child_layer')]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('child_pkid'),
                             f.GetField('parent_pkid'))
            #rel.addFieldPair(f.GetField('child_pkid'), 'ogc_fid')
            if rel.isValid():
                relations_1_n.append(rel)
                # add relation to layer
                layers[f.GetField('layer_name')]['1_n'].append(rel)

    QgsProject.instance().relationManager().setRelations(relations_1_1 +
                                                         relations_1_n)

    # add "show form" option to 1:1 relations
    for rel in relations_1_1:
        l = rel.referencingLayer()
        idx = rel.referencingFields()[0]
        s = QgsEditorWidgetSetup(
            "RelationReference", {
                'AllowNULL': False,
                'ReadOnly': True,
                'Relation': rel.id(),
                'OrderByValue': False,
                'MapIdentification': False,
                'AllowAddFeatures': False,
                'ShowForm': True
            })
        l.setEditorWidgetSetup(idx, s)

    # setup form for layers
    for layer, lyr in layers.items():
        l = lyr['layer']
        fc = l.editFormConfig()
        fc.clearTabs()
        fc.setLayout(QgsEditFormConfig.TabLayout)
        # Add fields
        c = QgsAttributeEditorContainer("Main", fc.invisibleRootContainer())
        c.setIsGroupBox(False)  # a tab
        for idx, f in enumerate(l.fields()):
            c.addChildElement(QgsAttributeEditorField(f.name(), idx, c))
        fc.addTab(c)

        # Add 1:N relations
        c_1_n = QgsAttributeEditorContainer("1:N links",
                                            fc.invisibleRootContainer())
        c_1_n.setIsGroupBox(False)  # a tab
        fc.addTab(c_1_n)

        for rel in lyr['1_n']:
            c_1_n.addChildElement(
                QgsAttributeEditorRelation(rel.name(), rel, c_1_n))

        l.setEditFormConfig(fc)
Exemplo n.º 10
0
    def test_add_feature_geometry(self):
        """
        Test to add a feature with a geometry
        """
        vl_pipes = QgsVectorLayer(
            self.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."pipes" (geom) sql=',
            'pipes', 'postgres')
        vl_leaks = QgsVectorLayer(
            self.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."leaks" (geom) sql=',
            'leaks', 'postgres')
        vl_leaks.startEditing()

        QgsProject.instance().addMapLayer(vl_pipes)
        QgsProject.instance().addMapLayer(vl_leaks)

        self.assertEqual(vl_pipes.featureCount(), 2)
        self.assertEqual(vl_leaks.featureCount(), 3)

        rel = QgsRelation()
        rel.setReferencingLayer(vl_leaks.id())
        rel.setReferencedLayer(vl_pipes.id())
        rel.addFieldPair('pipe', 'id')
        rel.setId('rel_pipe_leak')
        self.assertTrue(rel.isValid())
        self.relMgr.addRelation(rel)

        # Mock vector layer tool to just set default value on created feature
        class DummyVlTools(QgsVectorLayerTools):
            def addFeature(self,
                           layer,
                           defaultValues,
                           defaultGeometry,
                           parentWidget=None,
                           showModal=True,
                           hideParent=False):
                f = QgsFeature(layer.fields())
                for idx, value in defaultValues.items():
                    f.setAttribute(idx, value)
                f.setGeometry(defaultGeometry)
                ok = layer.addFeature(f)

                return ok, f

        wrapper = QgsRelationWidgetWrapper(vl_leaks, rel)
        context = QgsAttributeEditorContext()
        vltool = DummyVlTools()
        context.setVectorLayerTools(vltool)
        context.setMapCanvas(self.mapCanvas)
        cadDockWidget = QgsAdvancedDigitizingDockWidget(self.mapCanvas)
        context.setCadDockWidget(cadDockWidget)
        wrapper.setContext(context)
        widget = wrapper.widget()
        widget.show()
        pipe = next(vl_pipes.getFeatures())
        self.assertEqual(pipe.id(), 1)
        wrapper.setFeature(pipe)
        table_view = widget.findChild(QTableView)
        self.assertEqual(table_view.model().rowCount(), 1)

        btn = widget.findChild(QToolButton, 'mAddFeatureGeometryButton')
        self.assertTrue(btn.isVisible())
        self.assertTrue(btn.isEnabled())
        btn.click()
        self.assertTrue(self.mapCanvas.mapTool())
        feature = QgsFeature(vl_leaks.fields())
        feature.setGeometry(QgsGeometry.fromWkt('POINT(0 0.8)'))
        self.mapCanvas.mapTool().digitizingCompleted.emit(feature)
        self.assertEqual(table_view.model().rowCount(), 2)
        self.assertEqual(vl_leaks.featureCount(), 4)
        request = QgsFeatureRequest()
        request.addOrderBy("id", False)

        # get new created feature
        feat = next(vl_leaks.getFeatures('"id" is NULL'))
        self.assertTrue(feat.isValid())
        self.assertTrue(feat.geometry().equals(
            QgsGeometry.fromWkt('POINT(0 0.8)')))

        vl_leaks.rollBack()
Exemplo n.º 11
0
class TestQgsRelationEditWidget(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        """
        Setup the involved layers and relations for a n:m relation
        :return:
        """
        cls.mapCanvas = QgsMapCanvas()
        QgsGui.editorWidgetRegistry().initEditors(cls.mapCanvas)
        cls.dbconn = 'service=\'qgis_test\''
        if 'QGIS_PGTEST_DB' in os.environ:
            cls.dbconn = os.environ['QGIS_PGTEST_DB']
        # Create test layer
        cls.vl_books = QgsVectorLayer(
            cls.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."books" sql=',
            'books', 'postgres')
        cls.vl_authors = QgsVectorLayer(
            cls.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."authors" sql=',
            'authors', 'postgres')
        cls.vl_editors = QgsVectorLayer(
            cls.dbconn +
            ' sslmode=disable key=\'fk_book,fk_author\' table="qgis_test"."editors" sql=',
            'editors', 'postgres')
        cls.vl_link_books_authors = QgsVectorLayer(
            cls.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."books_authors" sql=',
            'books_authors', 'postgres')

        QgsProject.instance().addMapLayer(cls.vl_books)
        QgsProject.instance().addMapLayer(cls.vl_authors)
        QgsProject.instance().addMapLayer(cls.vl_editors)
        QgsProject.instance().addMapLayer(cls.vl_link_books_authors)

        cls.relMgr = QgsProject.instance().relationManager()

        # Our mock QgsVectorLayerTools, that allow injecting data where user input is expected
        cls.vltools = VlTools()
        cls.layers = {cls.vl_authors, cls.vl_books, cls.vl_link_books_authors}

        assert (cls.vl_authors.isValid())
        assert (cls.vl_books.isValid())
        assert (cls.vl_editors.isValid())
        assert (cls.vl_link_books_authors.isValid())

    @classmethod
    def tearDownClass(cls):
        QgsProject.instance().removeAllMapLayers()
        cls.vl_books = None
        cls.vl_authors = None
        cls.vl_editors = None
        cls.vl_link_books_authors = None
        cls.layers = None
        cls.mapCanvas = None
        cls.vltools = None
        cls.relMgr = None

    def setUp(self):
        self.rel_a = QgsRelation()
        self.rel_a.setReferencingLayer(self.vl_link_books_authors.id())
        self.rel_a.setReferencedLayer(self.vl_authors.id())
        self.rel_a.addFieldPair('fk_author', 'pk')
        self.rel_a.setId('rel_a')
        assert (self.rel_a.isValid())
        self.relMgr.addRelation(self.rel_a)

        self.rel_b = QgsRelation()
        self.rel_b.setReferencingLayer(self.vl_link_books_authors.id())
        self.rel_b.setReferencedLayer(self.vl_books.id())
        self.rel_b.addFieldPair('fk_book', 'pk')
        self.rel_b.setId('rel_b')
        assert (self.rel_b.isValid())
        self.relMgr.addRelation(self.rel_b)

        self.startTransaction()

    def tearDown(self):
        self.rollbackTransaction()
        del self.transaction
        self.relMgr.clear()

    def startTransaction(self):
        """
        Start a new transaction and set all layers into transaction mode.

        :return: None
        """
        self.transaction = QgsTransaction.create(self.layers)
        self.transaction.begin()
        for layer in self.layers:
            layer.startEditing()

    def rollbackTransaction(self):
        """
        Rollback all changes done in this transaction.
        We always rollback and never commit to have the database in a pristine
        state at the end of each test.

        :return: None
        """
        for layer in self.layers:
            layer.commitChanges()
        self.transaction.rollback()

    def test_delete_feature(self):
        """
        Check if a feature can be deleted properly
        """
        self.createWrapper(self.vl_authors, '"name"=\'Erich Gamma\'')

        self.assertEqual(self.table_view.model().rowCount(), 1)

        self.assertEqual(1, len([f for f in self.vl_books.getFeatures()]))

        fid = next(
            self.vl_books.getFeatures(QgsFeatureRequest().setFilterExpression(
                '"name"=\'Design Patterns. Elements of Reusable Object-Oriented Software\''
            ))).id()

        self.widget.featureSelectionManager().select([fid])

        btn = self.widget.findChild(QToolButton, 'mDeleteFeatureButton')

        def clickOk():
            # Click the "Delete features" button on the confirmation message
            # box
            widget = self.widget.findChild(QMessageBox)
            buttonBox = widget.findChild(QDialogButtonBox)
            deleteButton = next(
                (b for b in buttonBox.buttons()
                 if buttonBox.buttonRole(b) == QDialogButtonBox.AcceptRole))
            deleteButton.click()

        QTimer.singleShot(1, clickOk)
        btn.click()

        # This is the important check that the feature is deleted
        self.assertEqual(0, len([f for f in self.vl_books.getFeatures()]))

        # This is actually more checking that the database on delete action is properly set on the relation
        self.assertEqual(
            0, len([f for f in self.vl_link_books_authors.getFeatures()]))

        self.assertEqual(self.table_view.model().rowCount(), 0)

    def test_list(self):
        """
        Simple check if several related items are shown
        """
        wrapper = self.createWrapper(self.vl_books)  # NOQA

        self.assertEqual(self.table_view.model().rowCount(), 4)

    def test_add_feature(self):
        """
        Check if a new related feature is added
        """
        self.createWrapper(self.vl_authors, '"name"=\'Douglas Adams\'')

        self.assertEqual(self.table_view.model().rowCount(), 0)

        self.vltools.setValues([
            None, 'The Hitchhiker\'s Guide to the Galaxy', 'Sputnik Editions',
            1961
        ])
        btn = self.widget.findChild(QToolButton, 'mAddFeatureButton')
        btn.click()

        # Book entry has been created
        self.assertEqual(2, len([f for f in self.vl_books.getFeatures()]))

        # Link entry has been created
        self.assertEqual(
            5, len([f for f in self.vl_link_books_authors.getFeatures()]))

        self.assertEqual(self.table_view.model().rowCount(), 1)

    def test_link_feature(self):
        """
        Check if an existing feature can be linked
        """
        wrapper = self.createWrapper(self.vl_authors,
                                     '"name"=\'Douglas Adams\'')  # NOQA

        f = QgsFeature(self.vl_books.fields())
        f.setAttributes([
            self.vl_books.dataProvider().defaultValueClause(0),
            'The Hitchhiker\'s Guide to the Galaxy', 'Sputnik Editions', 1961
        ])
        self.vl_books.addFeature(f)

        btn = self.widget.findChild(QToolButton, 'mLinkFeatureButton')
        btn.click()

        dlg = self.widget.findChild(QDialog)
        dlg.setSelectedFeatures([f.id()])
        dlg.accept()

        # magically the above code selects the feature here...
        link_feature = next(
            self.vl_link_books_authors.getFeatures(
                QgsFeatureRequest().setFilterExpression('"fk_book"={}'.format(
                    f[0]))))
        self.assertIsNotNone(link_feature[0])

        self.assertEqual(self.table_view.model().rowCount(), 1)

    def test_unlink_feature(self):
        """
        Check if a linked feature can be unlinked
        """
        wrapper = self.createWrapper(self.vl_books)  # NOQA

        # All authors are listed
        self.assertEqual(self.table_view.model().rowCount(), 4)

        it = self.vl_authors.getFeatures(
            QgsFeatureRequest().setFilterExpression(
                '"name" IN (\'Richard Helm\', \'Ralph Johnson\')'))

        self.widget.featureSelectionManager().select([f.id() for f in it])

        self.assertEqual(
            2,
            self.widget.featureSelectionManager().selectedFeatureCount())

        btn = self.widget.findChild(QToolButton, 'mUnlinkFeatureButton')
        btn.click()

        # This is actually more checking that the database on delete action is properly set on the relation
        self.assertEqual(
            2, len([f for f in self.vl_link_books_authors.getFeatures()]))

        self.assertEqual(2, self.table_view.model().rowCount())

    def test_discover_relations(self):
        """
        Test the automatic discovery of relations
        """
        relations = self.relMgr.discoverRelations(
            [], [self.vl_authors, self.vl_books, self.vl_link_books_authors])
        relations = {r.name(): r for r in relations}
        self.assertEqual(
            {'books_authors_fk_book_fkey', 'books_authors_fk_author_fkey'},
            set(relations.keys()))

        ba2b = relations['books_authors_fk_book_fkey']
        self.assertTrue(ba2b.isValid())
        self.assertEqual('books_authors', ba2b.referencingLayer().name())
        self.assertEqual('books', ba2b.referencedLayer().name())
        self.assertEqual([0], ba2b.referencingFields())
        self.assertEqual([0], ba2b.referencedFields())

        ba2a = relations['books_authors_fk_author_fkey']
        self.assertTrue(ba2a.isValid())
        self.assertEqual('books_authors', ba2a.referencingLayer().name())
        self.assertEqual('authors', ba2a.referencedLayer().name())
        self.assertEqual([1], ba2a.referencingFields())
        self.assertEqual([0], ba2a.referencedFields())

        self.assertEqual(
            [],
            self.relMgr.discoverRelations(
                [self.rel_a, self.rel_b],
                [self.vl_authors, self.vl_books, self.vl_link_books_authors]))
        self.assertEqual(
            1,
            len(
                self.relMgr.discoverRelations(
                    [], [self.vl_authors, self.vl_link_books_authors])))

        # composite keys relation
        relations = self.relMgr.discoverRelations(
            [], [self.vl_books, self.vl_editors])
        self.assertEqual(len(relations), 1)
        relation = relations[0]
        self.assertEqual('books_fk_editor_fkey', relation.name())
        self.assertTrue(relation.isValid())
        self.assertEqual('books', relation.referencingLayer().name())
        self.assertEqual('editors', relation.referencedLayer().name())
        self.assertEqual([2, 3], relation.referencingFields())
        self.assertEqual([0, 1], relation.referencedFields())

    def test_selection(self):

        fbook = QgsFeature(self.vl_books.fields())
        fbook.setAttributes([
            self.vl_books.dataProvider().defaultValueClause(0),
            'The Hitchhiker\'s Guide to the Galaxy', 'Sputnik Editions', 1961
        ])
        self.vl_books.addFeature(fbook)

        flink = QgsFeature(self.vl_link_books_authors.fields())
        flink.setAttributes([fbook.id(), 5])
        self.vl_link_books_authors.addFeature(flink)

        self.createWrapper(self.vl_authors, '"name"=\'Douglas Adams\'')

        self.zoomToButton = self.widget.findChild(QToolButton,
                                                  "mDeleteFeatureButton")
        self.assertTrue(self.zoomToButton)
        self.assertTrue(not self.zoomToButton.isEnabled())

        selectionMgr = self.widget.featureSelectionManager()
        self.assertTrue(selectionMgr)

        self.vl_books.select(fbook.id())
        self.assertEqual([fbook.id()], selectionMgr.selectedFeatureIds())
        self.assertTrue(self.zoomToButton.isEnabled())

        selectionMgr.deselect([fbook.id()])
        self.assertEqual([], selectionMgr.selectedFeatureIds())
        self.assertTrue(not self.zoomToButton.isEnabled())

        self.vl_books.select([1, fbook.id()])
        self.assertEqual([fbook.id()], selectionMgr.selectedFeatureIds())
        self.assertTrue(self.zoomToButton.isEnabled())

    def test_add_feature_geometry(self):
        """
        Test to add a feature with a geometry
        """
        vl_pipes = QgsVectorLayer(
            self.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."pipes" (geom) sql=',
            'pipes', 'postgres')
        vl_leaks = QgsVectorLayer(
            self.dbconn +
            ' sslmode=disable key=\'pk\' table="qgis_test"."leaks" (geom) sql=',
            'leaks', 'postgres')
        vl_leaks.startEditing()

        QgsProject.instance().addMapLayer(vl_pipes)
        QgsProject.instance().addMapLayer(vl_leaks)

        self.assertEqual(vl_pipes.featureCount(), 2)
        self.assertEqual(vl_leaks.featureCount(), 3)

        rel = QgsRelation()
        rel.setReferencingLayer(vl_leaks.id())
        rel.setReferencedLayer(vl_pipes.id())
        rel.addFieldPair('pipe', 'id')
        rel.setId('rel_pipe_leak')
        self.assertTrue(rel.isValid())
        self.relMgr.addRelation(rel)

        # Mock vector layer tool to just set default value on created feature
        class DummyVlTools(QgsVectorLayerTools):
            def addFeature(self,
                           layer,
                           defaultValues,
                           defaultGeometry,
                           parentWidget=None,
                           showModal=True,
                           hideParent=False):
                f = QgsFeature(layer.fields())
                for idx, value in defaultValues.items():
                    f.setAttribute(idx, value)
                f.setGeometry(defaultGeometry)
                ok = layer.addFeature(f)

                return ok, f

        wrapper = QgsRelationWidgetWrapper(vl_leaks, rel)
        context = QgsAttributeEditorContext()
        vltool = DummyVlTools()
        context.setVectorLayerTools(vltool)
        context.setMapCanvas(self.mapCanvas)
        cadDockWidget = QgsAdvancedDigitizingDockWidget(self.mapCanvas)
        context.setCadDockWidget(cadDockWidget)
        wrapper.setContext(context)
        widget = wrapper.widget()
        widget.show()
        pipe = next(vl_pipes.getFeatures())
        self.assertEqual(pipe.id(), 1)
        wrapper.setFeature(pipe)
        table_view = widget.findChild(QTableView)
        self.assertEqual(table_view.model().rowCount(), 1)

        btn = widget.findChild(QToolButton, 'mAddFeatureGeometryButton')
        self.assertTrue(btn.isVisible())
        self.assertTrue(btn.isEnabled())
        btn.click()
        self.assertTrue(self.mapCanvas.mapTool())
        feature = QgsFeature(vl_leaks.fields())
        feature.setGeometry(QgsGeometry.fromWkt('POINT(0 0.8)'))
        self.mapCanvas.mapTool().digitizingCompleted.emit(feature)
        self.assertEqual(table_view.model().rowCount(), 2)
        self.assertEqual(vl_leaks.featureCount(), 4)
        request = QgsFeatureRequest()
        request.addOrderBy("id", False)

        # get new created feature
        feat = next(vl_leaks.getFeatures('"id" is NULL'))
        self.assertTrue(feat.isValid())
        self.assertTrue(feat.geometry().equals(
            QgsGeometry.fromWkt('POINT(0 0.8)')))

        vl_leaks.rollBack()

    def createWrapper(self, layer, filter=None):
        """
        Basic setup of a relation widget wrapper.
        Will create a new wrapper and set its feature to the one and only book
        in the table.
        It will also assign some instance variables to help

         * self.widget The created widget
         * self.table_view The table view of the widget

        :return: The created wrapper
        """
        if layer == self.vl_books:
            relation = self.rel_b
            nmrel = self.rel_a
        else:
            relation = self.rel_a
            nmrel = self.rel_b

        self.wrapper = QgsRelationWidgetWrapper(layer, relation)
        self.wrapper.setConfig({'nm-rel': nmrel.id()})
        context = QgsAttributeEditorContext()
        context.setMapCanvas(self.mapCanvas)
        context.setVectorLayerTools(self.vltools)
        self.wrapper.setContext(context)

        self.widget = self.wrapper.widget()
        self.widget.show()

        request = QgsFeatureRequest()
        if filter:
            request.setFilterExpression(filter)
        book = next(layer.getFeatures(request))
        self.wrapper.setFeature(book)

        self.table_view = self.widget.findChild(QTableView)
        return self.wrapper
Exemplo n.º 12
0
    def setUpClass(cls):
        """Run before all tests"""
        QCoreApplication.setOrganizationName("QGIS_Test")
        QCoreApplication.setOrganizationDomain(
            "QGIS_TestPyQgsPackageLayers.com")
        QCoreApplication.setApplicationName("QGIS_TestPyQgsPackageLayers")
        QgsSettings().clear()
        Processing.initialize()
        QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())
        cls.registry = QgsApplication.instance().processingRegistry()
        cls.tmp_dir = QTemporaryDir()
        cls.temp_path = os.path.join(cls.tmp_dir.path(), 'package_layers.gpkg')
        cls.temp_export_path = os.path.join(cls.tmp_dir.path(), 'package_layers_export.gpkg')

        # Create test DB

        """
        Test data:

        Region 1
            Province 1
                City 1
                City 2
            Province 2
                City 3
        Region 2
            Province 3
            Province 4
                City 4
        """

        ds = ogr.GetDriverByName('GPKG').CreateDataSource(cls.temp_path)
        lyr = ds.CreateLayer('region', geom_type=ogr.wkbNone)
        lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString))
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'region one'
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'region two'
        lyr.CreateFeature(f)

        lyr = ds.CreateLayer('province', geom_type=ogr.wkbNone)
        lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString))
        lyr.CreateField(ogr.FieldDefn('region', ogr.OFTInteger))
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'province one'
        f['region'] = 1
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'province two'
        f['region'] = 1
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'province three'
        f['region'] = 2
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'province four'
        f['region'] = 2
        lyr.CreateFeature(f)

        lyr = ds.CreateLayer('city', geom_type=ogr.wkbNone)
        lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString))
        lyr.CreateField(ogr.FieldDefn('province', ogr.OFTInteger))
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'city one'
        f['province'] = 1
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'city two'
        f['province'] = 1
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'city three'
        f['province'] = 2
        lyr.CreateFeature(f)
        f = ogr.Feature(lyr.GetLayerDefn())
        f['name'] = 'city four'
        f['province'] = 4
        lyr.CreateFeature(f)

        f = None
        ds = None

        region = QgsVectorLayer(cls.temp_path + '|layername=region', 'region')
        province = QgsVectorLayer(cls.temp_path + '|layername=province', 'province')
        city = QgsVectorLayer(cls.temp_path + '|layername=city', 'city')

        QgsProject.instance().addMapLayers([region, province, city])

        relMgr = QgsProject.instance().relationManager()

        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('province -> region')
        rel.setReferencingLayer(province.id())
        rel.setReferencedLayer(region.id())
        rel.addFieldPair('region', 'fid')
        assert rel.isValid()

        relMgr.addRelation(rel)

        rel = QgsRelation()
        rel.setId('rel2')
        rel.setName('city -> province')
        rel.setReferencingLayer(city.id())
        rel.setReferencedLayer(province.id())
        rel.addFieldPair('province', 'fid')
        assert rel.isValid()

        relMgr.addRelation(rel)
Exemplo n.º 13
0
 def create_relations(
     self
 ):  #CURRENTLY NOT IN USE (NOT WORKING AS EXPECTED, ONLY RANDOMLY CREATING BOTH RELATIONS!!!  ALSO, SOMETIMES QGIS CRASH WHEN OPENING FORMS)
     """
     1. create project relations obs_points-comments and obs_points-stratigraphy
     2. add 2 tabs to the obs_points form and fill those tabs with related layers comments and stratigraphy
     """
     # create relations
     i = 0
     layers = default_layers()
     rel_tuples = relations()
     for lyr in list(layers.keys()):
         rel = QgsRelation()
         rel.setReferencingLayer(utils.find_layer(layers[lyr]).id())
         rel.setReferencedLayer(utils.find_layer(lyr).id())
         rel.addFieldPair('typ', 'typ')
         rel.setId(rel_tuples[i][1])
         rel.setName(rel_tuples[i][0])
         if rel.isValid(
         ):  # It will only be added if it is valid. If not, check the ids and field names
             QgsProject.instance().relationManager().addRelation(rel)
             #validate
             for key in QgsProject.instance().relationManager().relations(
             ).keys():
                 #print(key)
                 if str(key) == rel.id():
                     print(('added relation %s' % str(lyr)))
         else:
             qgis.utils.iface.messageBar().pushMessage(
                 "Error", """Failed to create relation %s!""" % str(lyr), 2)
             print(("""Failed to create relation %s!""" % str(lyr)))
         i += 1
     """
Exemplo n.º 14
0
 def applyJoin(self):
     self.dlg.show()
     selectedFields = self.getSelFieldList() or []
     if self.dlg.targetLayerCombo.currentText(
     )[:6] != 'Select' and self.dlg.joinLayerCombo.currentText(
     )[:6] != 'Select' and self.dlg.spatialTypeCombo.currentText(
     )[:6] != 'Select':
         targetLayer = self.layerSet[
             self.dlg.targetLayerCombo.currentText()]
         joinLayer = self.layerSet[self.dlg.joinLayerCombo.currentText()]
         joinLayerFields = [
             field.name() for field in joinLayer.pendingFields()
         ]
         #targetLayerFields = [field.name() for field in targetLayer.pendingFields()]
         targetLayerFields = []
         for field in targetLayer.pendingFields():
             if field.name()[0:7] == 'spjoin_':
                 idx = targetLayer.pendingFields().fieldNameIndex(
                     field.name())
                 self.tra.ce("removing:" + field.name() + str(idx))
                 targetLayer.dataProvider().deleteAttributes([idx])
                 targetLayer.removeExpressionField(idx)
             else:
                 targetLayerFields.append(field.name())
         self.tra.ce(targetLayerFields)
         targetLayer.updateFields()
         joinField = 'spjoin_rif'
         exp = "geom" + self.dlg.spatialTypeCombo.currentText(
         ) + "('" + joinLayer.name() + "','$id')"
         self.tra.ce(exp)
         #add a rif field if build relation requested
         if self.dlg.checkBuildRelation.checkState() == Qt.Checked:
             #joinLayer.addExpressionField('$id', QgsField('spjoin_rif', QVariant.Int))
             joinLayer.dataProvider().addAttributes(
                 [QgsField(joinField, QVariant.Int)])
             #joinLayer.updateFields()
             idx = joinLayer.dataProvider().fields().fieldNameIndex(
                 joinField)
             expObj = QgsExpression('$id')
             changes = {}
             for feature in joinLayer.getFeatures():
                 value = expObj.evaluate(feature)
                 #joinLayer.dataProvider().changeAttributeValues({feature.id():{idx:value}})
                 changes[feature.id()] = {idx: value}
             joinLayer.dataProvider().changeAttributeValues(changes)
         if self.dlg.checkDynamicJoin.checkState() == Qt.Checked:
             targetLayer.addExpressionField(
                 exp, QgsField(joinField, QVariant.Int))
         else:
             #Create static rif field
             targetLayer.dataProvider().addAttributes(
                 [QgsField(joinField, QVariant.Int)])
             #targetLayer.updateFields()
             F = [
                 field.name()
                 for field in targetLayer.dataProvider().fields()
             ]
             self.tra.ce(F)
             #Compile spatial expression to get feature rifs
             expObj = QgsExpression(exp)
             expObj.prepare(targetLayer.pendingFields())
             idx = targetLayer.dataProvider().fields().fieldNameIndex(
                 joinField)
             self.tra.ce("new " + joinField + str(idx))
             changes = {}
             #init progress bar
             self.dlg.progressBar.setMinimum(0)
             self.dlg.progressBar.setMaximum(targetLayer.featureCount())
             #cicle into feature to build mod vector
             for count, feature in enumerate(targetLayer.getFeatures()):
                 self.dlg.progressBar.setValue(count)
                 value = expObj.evaluate(feature)
                 changes[feature.id()] = {idx: value}
             self.tra.ce(changes)
             #apply mod vector
             targetLayer.dataProvider().changeAttributeValues(changes)
         targetLayer.updateFields()
         #build expression fields to connect to join field rif
         for f in selectedFields:
             fieldType = joinLayer.pendingFields().field(f).type()
             exp = """dbvaluebyid('%s','%s',"%s")""" % (joinLayer.name(), f,
                                                        joinField)
             self.tra.ce(exp)
             targetLayer.addExpressionField(
                 exp, QgsField('spjoin_' + f, fieldType))
         targetLayer.updateFields()
         if self.dlg.checkBuildRelation.checkState() == Qt.Checked:
             jRel = QgsRelation()
             jRel.setRelationId(targetLayer.name() + "_" +
                                str(uuid.uuid1()))
             jRel.setRelationName("%s_%s-rif_%s" %
                                  (targetLayer.name(),
                                   self.dlg.spatialTypeCombo.currentText(),
                                   joinLayer.name()))
             jRel.setReferencedLayer(joinLayer.id())
             jRel.setReferencingLayer(targetLayer.id())
             jRel.addFieldPair('spjoin_rif', 'spjoin_rif')
             QgsProject.instance().relationManager().addRelation(jRel)
         self.dlg.hide()
Exemplo n.º 15
0
    def create(self, qgis_project, relations):
        relation = QgsRelation()
        project_ids = qgis_project.relationManager().relations().keys()
        base_id = self.name

        suffix = 1
        self._id = base_id

        while self._id in project_ids:
            self._id = '{}_{}'.format(base_id, suffix)
            suffix += 1

        while self._id in [rel.id() for rel in relations]:
            self._id = '{}_{}'.format(base_id, suffix)
            suffix += 1

        relation.setId(self._id)
        relation.setName(self.name)
        relation.setReferencingLayer(self.referencing_layer.create().id())
        relation.setReferencedLayer(self.referenced_layer.create().id())
        relation.addFieldPair(self.referencing_field, self.referenced_field)
        relation.setStrength(self.strength)
        self.qgis_relation = relation
        return relation
    def open_db(self):
        edb_filename = self.open_db_lineedit.text()
        edb_name, ext = os.path.splitext(os.path.basename(str(edb_filename)))
        QgsMessageLog.logMessage(
            "Loading edb %s" % edb_filename,
            'AirviroOfflineEdb',
            QgsMessageLog.INFO
        )

        self.db_uri.setDatabase(edb_filename)
        self.con, self.cur = connect(str(self.db_uri.database()))
        self.epsg = get_epsg(self.con)

        root = QgsProject.instance().layerTreeRoot()

        edb_increment = 1
        while root.findGroup(edb_name) is not None:
            edb_name = edb_name + unicode(edb_increment)
            edb_increment += 1

        QgsMessageLog.logMessage(
            "Adding edb layers in %s" % edb_name,
            'AirviroOfflineEdb',
            QgsMessageLog.INFO
        )
        edb_group = root.addGroup(edb_name)

        point_group = edb_group.addGroup('Point sources')
        area_group = edb_group.addGroup('Area sources')
        grid_group = edb_group.addGroup('Grid sources')
        road_group = edb_group.addGroup('Road sources')
        subtable_group = edb_group.addGroup('Subtables')
        company_group = edb_group.addGroup('Companies')
        facility_group = edb_group.addGroup('Facilities')
        emis_group = edb_group.addGroup('Emissions')

        point_support_group = point_group.addGroup('Support tables')
        area_support_group = area_group.addGroup('Support tables')
        grid_support_group = grid_group.addGroup('Support tables')
        road_support_group = road_group.addGroup('Support tables')
        facility_support_group = facility_group.addGroup('Support tables')
        company_support_group = company_group.addGroup('Support tables')

        unit_group = subtable_group.addGroup('Units')
        road_vehicle_group = subtable_group.addGroup('Road vehicles')
        road_vehicle_support_group = road_vehicle_group.addGroup(
            'Support tables'
        )
        roadtype_group = subtable_group.addGroup('Roadtypes')
        emis_func_group = subtable_group.addGroup('Emission functions')
        searchkey_group = subtable_group.addGroup('Searchkeys')
        timevar_group = subtable_group.addGroup('Time variations')
        subgrp_group = subtable_group.addGroup('Substance groups')
        
        self.layers = {}
        schema = ''
        geom_table_column_dict = dict(GEOMETRY_TABLES_COLUMNS)
        for table in TABLES:
            if not table_in_db(self.cur, table):
                iface.messageBar().pushMessage(
                    "Warning",
                    "Table %s not found in edb" % table,
                    level=QgsMessageBar.WARNING,
                    duration=3
                )
                continue
            geom_col = geom_table_column_dict.get(table, None)
            self.db_uri.setDataSource(
                schema,
                table,
                geom_col or ''
            )
            layer_uri = self.db_uri.uri()  # + "&crs=EPSG:4326"
            layer = QgsVectorLayer(layer_uri, table, 'spatialite')
            layer.setCrs(QgsCoordinateReferenceSystem(
                self.epsg,
                QgsCoordinateReferenceSystem.EpsgCrsId)
            )
            if not layer.isValid():
                raise ValueError(edb_filename)
            map_layer = QgsMapLayerRegistry.instance().addMapLayer(
                layer, False
            )
                        
            if 'timevar' in table:
                group = timevar_group
            elif 'emission_function' in table:
                group = emis_func_group
            elif 'searchkey' in table:
                group = searchkey_group
            elif 'unit' in table:
                group = unit_group
            elif 'subgrp' in table:
                group = subgrp_group
            elif table == 'substances':
                group = subtable_group
            elif table.endswith('_emis'):
                group = emis_group
            elif table == 'points':
                group = point_group
            elif 'point_' in table:
                group = point_support_group
            elif table == 'areas':
                group = area_group
            elif 'area_' in table:
                group = area_support_group
            elif table == 'roads':
                group = road_group
            elif table in ('road_vehicle_link', 'road_alobs'):
                group = road_support_group
            elif 'road_' in table:
                group = road_vehicle_group
            elif 'roadtype' in table:
                group = roadtype_group
            elif table == 'facilties':
                group = facility_group
            elif 'facility' in table:
                group = facility_support_group
            elif 'companies' == table:
                group = company_group
            elif 'company' in table:
                group = company_support_group
            elif 'traffic_situation' in table:
                group = road_vehicle_support_group

            group.setVisible(False)
            group.setExpanded(False)
            group.addLayer(map_layer)
            self.layers[table] = map_layer.id()

        for table in TABLES:
            foreign_keys = get_foreign_keys(self.con, table)
            referencing_layer = self.layers[table]
            for row in foreign_keys:
                referenced_layer = self.layers[row['table']]
                from_column = row['from']
                to_column = row['to']
                
                rel = QgsRelation()
                rel.setReferencingLayer(referencing_layer)
                rel.setReferencedLayer(referenced_layer)
                rel.addFieldPair(from_column, to_column)
                rel_name = 'fk_%s_%s-%s_%s' % (
                    table, from_column, row['table'], to_column
                )
                rel.setRelationId(rel_name)
                rel.setRelationName(
                    'fk_%s_%s-%s_%s' % (
                        table, from_column, row['table'], to_column)
                )
                
                if not rel.isValid():
                    raise ValueError(
                        'Reference %s is invalid' % rel_name
                    )
                QgsProject.instance().relationManager().addRelation(rel)
    def testRelations(self):
        """ check that layers are shown in widget model"""
        p = QgsProject.instance()

        # not valid, but doesn't matter for test....
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        rel2 = QgsRelation()
        rel2.setId('rel2')
        rel2.setName('Relation Number Two')
        rel2.setReferencingLayer(self.referencingLayer.id())
        rel2.setReferencedLayer(self.referencedLayer.id())
        rel2.addFieldPair('foreignkey', 'y')

        p.relationManager().addRelation(rel)
        p.relationManager().addRelation(rel2)

        w = QgsExpressionBuilderWidget()
        m = w.model()

        # check that relations are shown
        items = m.findItems('Relation Number One', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
        items = m.findItems('Relation Number Two', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
Exemplo n.º 18
0
    def test_getReferencedFeature(self):
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = next(self.referencingLayer.getFeatures())

        f = rel.getReferencedFeature(feat)

        self.assertTrue(f.isValid())
        self.assertEqual(f[0], 'foo')

        # try mixing up the field pair field name cases -- we should be tolerant to this
        rel2 = QgsRelation()
        rel2.setId('rel1')
        rel2.setName('Relation Number One')
        rel2.setReferencingLayer(self.referencingLayer.id())
        rel2.setReferencedLayer(self.referencedLayer.id())
        rel2.addFieldPair('ForeignKey', 'Y')

        feat = next(self.referencingLayer.getFeatures())

        f = rel2.getReferencedFeature(feat)

        self.assertTrue(f.isValid())
        self.assertEqual(f[0], 'foo')
Exemplo n.º 19
0
    def test_getRelatedFeatures(self):
        rel = QgsRelation()

        rel.setRelationId("rel1")
        rel.setRelationName("Relation Number One")
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair("foreignkey", "y")

        feat = next(self.referencedLayer.getFeatures())

        self.assertEqual(rel.getRelatedFeaturesFilter(feat), '"foreignkey" = 123')

        it = rel.getRelatedFeatures(feat)
        assert [a.attributes() for a in it] == [["test1", 123], ["test2", 123]]
Exemplo n.º 20
0
 def test_generateId_empty_relation(self):
     rel = QgsRelation()
     # Check that it does not crash
     rel.generateId()
Exemplo n.º 21
0
    def test_RelationReference_representValue(self):

        first_layer = QgsVectorLayer("none?field=foreign_key:integer",
                                     "first_layer", "memory")
        assert first_layer.isValid()
        second_layer = QgsVectorLayer("none?field=pkid:integer&field=decoded:string",
                                      "second_layer", "memory")
        assert second_layer.isValid()
        QgsMapLayerRegistry.instance().addMapLayers([first_layer, second_layer])
        f = QgsFeature()
        f.setAttributes([123])
        assert first_layer.dataProvider().addFeatures([f])
        f = QgsFeature()
        f.setAttributes([123, 'decoded_val'])
        assert second_layer.dataProvider().addFeatures([f])

        relMgr = QgsProject.instance().relationManager()

        reg = QgsEditorWidgetRegistry.instance()
        factory = reg.factory("RelationReference")
        self.assertIsNotNone(factory)

        rel = QgsRelation()
        rel.setRelationId('rel1')
        rel.setRelationName('Relation Number One')
        rel.setReferencingLayer(first_layer.id())
        rel.setReferencedLayer(second_layer.id())
        rel.addFieldPair('foreign_key', 'pkid')
        assert(rel.isValid())

        relMgr.addRelation(rel)

        # Everything valid
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), 'decoded_val')

        # Code not find match in foreign layer
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '456'), '456')

        # Invalid relation id
        config = {'Relation': 'invalid'}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

        # No display expression
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression(None)
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

        # Invalid display expression
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('invalid +')
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

        # Missing relation
        config = {}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

        # Inconsistent layer provided to representValue()
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(second_layer, 0, config, None, '123'), '123')

        # Inconsistent idx provided to representValue()
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(first_layer, 1, config, None, '123'), '123')

        # Invalid relation
        rel = QgsRelation()
        rel.setRelationId('rel2')
        rel.setRelationName('Relation Number Two')
        rel.setReferencingLayer(first_layer.id())
        rel.addFieldPair('foreign_key', 'pkid')
        self.assertFalse(rel.isValid())

        relMgr.addRelation(rel)

        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(factory.representValue(first_layer, 0, config, None, '123'), '123')

        QgsMapLayerRegistry.instance().removeAllMapLayers()
Exemplo n.º 22
0
    def open_db(self):
        edb_filename = self.open_db_lineedit.text()
        edb_name, ext = os.path.splitext(os.path.basename(str(edb_filename)))
        QgsMessageLog.logMessage("Loading edb %s" % edb_filename,
                                 'AirviroOfflineEdb', QgsMessageLog.INFO)

        self.db_uri.setDatabase(edb_filename)
        self.con, self.cur = connect(str(self.db_uri.database()))
        self.epsg = get_epsg(self.con)

        root = QgsProject.instance().layerTreeRoot()

        edb_increment = 1
        while root.findGroup(edb_name) is not None:
            edb_name = edb_name + unicode(edb_increment)
            edb_increment += 1

        QgsMessageLog.logMessage("Adding edb layers in %s" % edb_name,
                                 'AirviroOfflineEdb', QgsMessageLog.INFO)
        edb_group = root.addGroup(edb_name)

        point_group = edb_group.addGroup('Point sources')
        area_group = edb_group.addGroup('Area sources')
        grid_group = edb_group.addGroup('Grid sources')
        road_group = edb_group.addGroup('Road sources')
        subtable_group = edb_group.addGroup('Subtables')
        company_group = edb_group.addGroup('Companies')
        facility_group = edb_group.addGroup('Facilities')
        emis_group = edb_group.addGroup('Emissions')

        point_support_group = point_group.addGroup('Support tables')
        area_support_group = area_group.addGroup('Support tables')
        grid_support_group = grid_group.addGroup('Support tables')
        road_support_group = road_group.addGroup('Support tables')
        facility_support_group = facility_group.addGroup('Support tables')
        company_support_group = company_group.addGroup('Support tables')

        unit_group = subtable_group.addGroup('Units')
        road_vehicle_group = subtable_group.addGroup('Road vehicles')
        road_vehicle_support_group = road_vehicle_group.addGroup(
            'Support tables')
        roadtype_group = subtable_group.addGroup('Roadtypes')
        emis_func_group = subtable_group.addGroup('Emission functions')
        searchkey_group = subtable_group.addGroup('Searchkeys')
        timevar_group = subtable_group.addGroup('Time variations')
        subgrp_group = subtable_group.addGroup('Substance groups')

        self.layers = {}
        schema = ''
        geom_table_column_dict = dict(GEOMETRY_TABLES_COLUMNS)
        for table in TABLES:
            if not table_in_db(self.cur, table):
                iface.messageBar().pushMessage("Warning",
                                               "Table %s not found in edb" %
                                               table,
                                               level=QgsMessageBar.WARNING,
                                               duration=3)
                continue
            geom_col = geom_table_column_dict.get(table, None)
            self.db_uri.setDataSource(schema, table, geom_col or '')
            layer_uri = self.db_uri.uri()  # + "&crs=EPSG:4326"
            layer = QgsVectorLayer(layer_uri, table, 'spatialite')
            layer.setCrs(
                QgsCoordinateReferenceSystem(
                    self.epsg, QgsCoordinateReferenceSystem.EpsgCrsId))
            if not layer.isValid():
                raise ValueError(edb_filename)
            map_layer = QgsMapLayerRegistry.instance().addMapLayer(
                layer, False)

            if 'timevar' in table:
                group = timevar_group
            elif 'emission_function' in table:
                group = emis_func_group
            elif 'searchkey' in table:
                group = searchkey_group
            elif 'unit' in table:
                group = unit_group
            elif 'subgrp' in table:
                group = subgrp_group
            elif table == 'substances':
                group = subtable_group
            elif table.endswith('_emis'):
                group = emis_group
            elif table == 'points':
                group = point_group
            elif 'point_' in table:
                group = point_support_group
            elif table == 'areas':
                group = area_group
            elif 'area_' in table:
                group = area_support_group
            elif table == 'roads':
                group = road_group
            elif table in ('road_vehicle_link', 'road_alobs'):
                group = road_support_group
            elif 'road_' in table:
                group = road_vehicle_group
            elif 'roadtype' in table:
                group = roadtype_group
            elif table == 'facilties':
                group = facility_group
            elif 'facility' in table:
                group = facility_support_group
            elif 'companies' == table:
                group = company_group
            elif 'company' in table:
                group = company_support_group
            elif 'traffic_situation' in table:
                group = road_vehicle_support_group

            group.setVisible(False)
            group.setExpanded(False)
            group.addLayer(map_layer)
            self.layers[table] = map_layer.id()

        for table in TABLES:
            foreign_keys = get_foreign_keys(self.con, table)
            referencing_layer = self.layers[table]
            for row in foreign_keys:
                referenced_layer = self.layers[row['table']]
                from_column = row['from']
                to_column = row['to']

                rel = QgsRelation()
                rel.setReferencingLayer(referencing_layer)
                rel.setReferencedLayer(referenced_layer)
                rel.addFieldPair(from_column, to_column)
                rel_name = 'fk_%s_%s-%s_%s' % (table, from_column,
                                               row['table'], to_column)
                rel.setRelationId(rel_name)
                rel.setRelationName(
                    'fk_%s_%s-%s_%s' %
                    (table, from_column, row['table'], to_column))

                if not rel.isValid():
                    raise ValueError('Reference %s is invalid' % rel_name)
                QgsProject.instance().relationManager().addRelation(rel)
Exemplo n.º 23
0
    def test_fieldPairs(self):
        rel = QgsRelation()

        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        assert (rel.fieldPairs() == {'foreignkey': 'y'})
Exemplo n.º 24
0
    def test_getRelatedFeatures(self):
        rel = QgsRelation()

        rel.setRelationId('rel1')
        rel.setRelationName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = self.referencedLayer.getFeatures().next()

        it = rel.getRelatedFeatures(feat)

        assert [a.attributes() for a in it] == [[u'test1', 123],
                                                [u'test2', 123]]
def import_in_qgis(gmlas_uri, provider, schema = None):
    """Imports layers from a GMLAS file in QGIS with relations and editor widgets

    @param gmlas_uri connection parameters
    @param provider name of the QGIS provider that handles gmlas_uri parameters (postgresql or spatialite)
    @param schema name of the PostgreSQL schema where tables and metadata tables are
    """
    if schema is not None:
        schema_s = schema + "."
    else:
        schema_s = ""

    ogr.UseExceptions()
    drv = ogr.GetDriverByName(provider)
    ds = drv.Open(gmlas_uri)
    if ds is None:
        raise RuntimeError("Problem opening {}".format(gmlas_uri))

    # get list of layers
    sql = "select o.*, g.f_geometry_column, g.srid from {}_ogr_layers_metadata o left join geometry_columns g on g.f_table_name = o.layer_name".format(schema_s)
    
    l = ds.ExecuteSQL(sql)
    layers = {}
    for f in l:
        ln = f.GetField("layer_name")
        if ln not in layers:
            layers[ln] = {
                'uid': f.GetField("layer_pkid_name"),
                'category': f.GetField("layer_category"),
                'xpath': f.GetField("layer_xpath"),
                'parent_pkid': f.GetField("layer_parent_pkid_name"),
                'srid': f.GetField("srid"),
                'geometry_column': f.GetField("f_geometry_column"),
                '1_n' : [], # 1:N relations
                'layer_id': None,
                'layer_name' : ln,
                'layer': None,
                'fields' : []}
        else:
            # additional geometry columns
            g = f.GetField("f_geometry_column")
            k = "{} ({})".format(ln, g)
            layers[k] = dict(layers[ln])
            layers[k]["geometry_column"] = g

    # collect fields with xlink:href
    href_fields = {}
    for ln, layer in layers.items():
        layer_name = layer["layer_name"]
        for f in ds.ExecuteSQL("select field_name, field_xpath from {}_ogr_fields_metadata where layer_name='{}'".format(schema_s, layer_name)):
            field_name, field_xpath = f.GetField("field_name"), f.GetField("field_xpath")
            if field_xpath and field_xpath.endswith("@xlink:href"):
                if ln not in href_fields:
                    href_fields[ln] = []
                href_fields[ln].append(field_name)

    # with unknown srid, don't ask for each layer, set to a default
    settings = QgsSettings()
    projection_behavior = settings.value("Projections/defaultBehavior")
    projection_default = settings.value("Projections/layerDefaultCrs")
    settings.setValue("Projections/defaultBehavior", "useGlobal")
    settings.setValue("Projections/layerDefaultCrs", "EPSG:4326")

    # add layers
    crs = QgsCoordinateReferenceSystem("EPSG:4326")
    for ln in sorted(layers.keys()):
        lyr = layers[ln]
        g_column = lyr["geometry_column"] or None
        l = _qgis_layer(gmlas_uri, schema, lyr["layer_name"], g_column, provider, ln, lyr["xpath"], lyr["uid"])
        if not l.isValid():
            raise RuntimeError("Problem loading layer {} with {}".format(ln, l.source()))
        if g_column is not None:
            if lyr["srid"]:
                crs = QgsCoordinateReferenceSystem("EPSG:{}".format(lyr["srid"]))
            l.setCrs(crs)
        QgsProject.instance().addMapLayer(l)
        layers[ln]['layer_id'] = l.id()
        layers[ln]['layer'] = l
        # save fields which represent a xlink:href
        if ln in href_fields:
            l.setCustomProperty("href_fields", href_fields[ln])
        # save gmlas_uri
        l.setCustomProperty("ogr_uri", gmlas_uri)
        l.setCustomProperty("ogr_schema", schema)

        # change icon the layer has a custom viewer
        xpath = no_ns(l.customProperty("xpath", ""))
        for viewer_cls, _ in get_custom_viewers().values():
            tag = no_prefix(viewer_cls.xml_tag())
            if tag == xpath:
                lg = CustomViewerLegend(viewer_cls.name(), viewer_cls.icon())
                l.setLegend(lg)

    # restore settings
    settings.setValue("Projections/defaultBehavior", projection_behavior)
    settings.setValue("Projections/layerDefaultCrs", projection_default)

    # add 1:1 relations
    relations_1_1 = []
    sql = """
select
  layer_name, field_name, field_related_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.parent_element_name = f.field_name
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs=1
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            rel = QgsRelation()
            rel.setId('1_1_' + f.GetField('layer_name') + '_' + f.GetField('field_name'))
            rel.setName('1_1_' + f.GetField('layer_name') + '_' + f.GetField('field_name'))
            # parent layer
            rel.setReferencingLayer(layers[f.GetField('layer_name')]['layer_id'])
            # child layer
            rel.setReferencedLayer(layers[f.GetField('field_related_layer')]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('field_name'), f.GetField('child_pkid'))
            #rel.generateId()
            if rel.isValid():
                relations_1_1.append(rel)

    # add 1:N relations
    relations_1_n = []
    sql = """
select
  layer_name, r.parent_pkid, field_related_layer as child_layer, r.child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category in ('PATH_TO_CHILD_ELEMENT_WITH_LINK', 'PATH_TO_CHILD_ELEMENT_NO_LINK')
  and field_max_occurs>1
-- junctions - 1st way
union all
select
  layer_name, r.parent_pkid, field_junction_layer as child_layer, 'parent_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
-- junctions - 2nd way
union all
select
  field_related_layer as layer_name, r.child_pkid, field_junction_layer as child_layer, 'child_pkid' as child_pkid
from
  {0}_ogr_fields_metadata f
  join {0}_ogr_layer_relationships r
    on r.parent_layer = f.layer_name
   and r.child_layer = f.field_related_layer
where
  field_category = 'PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE'
""".format(schema_s)
    l = ds.ExecuteSQL(sql)
    if l is not None:
        for f in l:
            parent_layer = f.GetField("layer_name")
            child_layer = f.GetField("child_layer")
            if parent_layer not in layers or child_layer not in layers:
                continue
            rel = QgsRelation()
            rel.setId('1_n_' + f.GetField('layer_name') + '_' + f.GetField('child_layer') + '_' + f.GetField('parent_pkid') + '_' + f.GetField('child_pkid'))
            rel.setName(f.GetField('child_layer'))
            # parent layer
            rel.setReferencedLayer(layers[parent_layer]['layer_id'])
            # child layer
            rel.setReferencingLayer(layers[child_layer]['layer_id'])
            # parent, child
            rel.addFieldPair(f.GetField('child_pkid'), f.GetField('parent_pkid'))
            #rel.addFieldPair(f.GetField('child_pkid'), 'ogc_fid')
            if rel.isValid():
                relations_1_n.append(rel)
                # add relation to layer
                layers[f.GetField('layer_name')]['1_n'].append(rel)

    for rel in relations_1_1 + relations_1_n:
        QgsProject.instance().relationManager().addRelation(rel)

    # add "show form" option to 1:1 relations
    for rel in relations_1_1:
        l = rel.referencingLayer()
        idx = rel.referencingFields()[0]
        s = QgsEditorWidgetSetup("RelationReference", {'AllowNULL': False,
                                      'ReadOnly': True,
                                      'Relation': rel.id(),
                                      'OrderByValue': False,
                                      'MapIdentification': False,
                                      'AllowAddFeatures': False,
                                      'ShowForm': True})
        l.setEditorWidgetSetup(idx, s)

    # setup form for layers
    for layer, lyr in layers.items():
        l = lyr['layer']
        fc = l.editFormConfig()
        fc.clearTabs()
        fc.setLayout(QgsEditFormConfig.TabLayout)
        # Add fields
        c = QgsAttributeEditorContainer("Main", fc.invisibleRootContainer())
        c.setIsGroupBox(False) # a tab
        for idx, f in enumerate(l.fields()):
            c.addChildElement(QgsAttributeEditorField(f.name(), idx, c))
        fc.addTab(c)

        # Add 1:N relations
        c_1_n = QgsAttributeEditorContainer("1:N links", fc.invisibleRootContainer())
        c_1_n.setIsGroupBox(False) # a tab
        fc.addTab(c_1_n)
        
        for rel in lyr['1_n']:
            c_1_n.addChildElement(QgsAttributeEditorRelation(rel.name(), rel, c_1_n))

        l.setEditFormConfig(fc)

        install_viewer_on_feature_form(l)
Exemplo n.º 26
0
class DocumentModel(QAbstractTableModel):

    DocumentIdRole = Qt.UserRole + 1
    DocumentPathRole = Qt.UserRole + 2
    DocumentNameRole = Qt.UserRole + 3
    DocumentExistsRole = Qt.UserRole + 4
    DocumentToolTipRole = Qt.UserRole + 5
    DocumentIsImageRole = Qt.UserRole + 6

    def __init__(self, parent: QObject = None):
        super(DocumentModel, self).__init__(parent)
        self._relation = QgsRelation()
        self._nmRelation = QgsRelation()
        self._documents_path = str()
        self._document_filename = str()
        self._feature = QgsFeature()
        self._document_list = []

    def init(self, relation: QgsRelation, nmRelation: QgsRelation,
             feature: QgsFeature, documents_path: str, document_filename: str):
        self._relation = relation
        self._nmRelation = nmRelation
        self._documents_path = documents_path
        self._document_filename = document_filename
        self._feature = feature
        self.reloadData()

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return len(self._document_list)

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return 1

    def headerData(self,
                   section: int,
                   orientation: Qt.Orientation,
                   role: int = ...):
        return None

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
        return flags

    def data(self, index: QModelIndex, role: int = ...):
        if index.row() < 0 or index.row() >= self.rowCount(QModelIndex()):
            return None

        return self._document_list[index.row()][role]

    def setData(self,
                index: QModelIndex,
                value,
                role: int = Qt.EditRole) -> bool:
        if index.row() < 0 or index.row() >= self.rowCount(QModelIndex()):
            return False

        return False

    def roleNames(self):
        return {
            self.DocumentIdRole: b'DocumentId',
            self.DocumentPathRole: b'DocumentPath',
            self.DocumentNameRole: b'DocumentName',
            self.DocumentExistsRole: b'DocumentExists',
            self.DocumentToolTipRole: b'DocumentToolTip',
            self.DocumentIsImageRole: b'DocumentIsImage'
        }

    def reloadData(self):
        self.beginResetModel()
        self._document_list = []

        if self._relation.isValid() is False or self._feature.isValid(
        ) is False:
            self.endResetModel()
            return

        feature_list = []
        layer = self._relation.referencingLayer()
        request = self._relation.getRelatedFeaturesRequest(self._feature)
        for feature in layer.getFeatures(request):
            feature_list.append(feature)

        if self._nmRelation.isValid():
            filters = []
            for joinTableFeature in feature_list:
                referencedFeatureRequest = self._nmRelation.getReferencedFeatureRequest(
                    joinTableFeature)
                filterExpression = referencedFeatureRequest.filterExpression()
                filters.append("(" + filterExpression.expression() + ")")

            nmRequest = QgsFeatureRequest()
            nmRequest.setFilterExpression(" OR ".join(filters))

            feature_list = []
            layer = self._nmRelation.referencedLayer()
            for documentFeature in layer.getFeatures(nmRequest):
                feature_list.append(documentFeature)

        for documentFeature in feature_list:
            documents_path = str()
            if self._documents_path:
                exp = QgsExpression(self._documents_path)
                context = QgsExpressionContext()
                context.appendScopes(
                    QgsExpressionContextUtils.globalProjectLayerScopes(layer))
                context.setFeature(documentFeature)
                documents_path = exp.evaluate(context)

            document_filename = str()
            if self._document_filename:
                exp = QgsExpression(self._document_filename)
                context = QgsExpressionContext()
                context.appendScopes(
                    QgsExpressionContextUtils.globalProjectLayerScopes(layer))
                context.setFeature(documentFeature)
                document_filename = exp.evaluate(context)
            file_info = QFileInfo(QDir(str(documents_path)),
                                  str(document_filename))

            # ToolTip
            toolTipList = []
            toolTipList.append("<ul>")
            for field in documentFeature.fields():
                index = documentFeature.fields().indexFromName(field.name())
                toolTipList.append("<li><strong>{0}</strong>: {1}</li>".format(
                    field.displayName(), documentFeature[index]))
            toolTipList.append("</ul>")

            self._document_list.append({
                self.DocumentIdRole:
                documentFeature.id(),
                self.DocumentPathRole:
                file_info.filePath(),
                self.DocumentNameRole:
                file_info.fileName(),
                self.DocumentExistsRole:
                file_info.exists(),
                self.DocumentToolTipRole:
                "".join(toolTipList),
                self.DocumentIsImageRole:
                PreviewImageProvider.isMimeTypeSupported(file_info.filePath())
            })

        self.endResetModel()
Exemplo n.º 27
0
 def createRelation(self):
     rel = QgsRelation()
     rel.setReferencingLayer(self.referencingLayer.id())
     rel.setReferencedLayer(self.referencedLayer.id())
     rel.addFieldPair('foreignkey', 'y')
     return rel
Exemplo n.º 28
0
    def test_isValid(self):
        rel = QgsRelation()
        assert not rel.isValid()

        rel.setId('rel1')
        assert not rel.isValid()

        rel.setName('Relation Number One')
        assert not rel.isValid()

        rel.setReferencingLayer(self.referencingLayer.id())
        assert not rel.isValid()

        rel.setReferencedLayer(self.referencedLayer.id())
        assert not rel.isValid()

        rel.addFieldPair('foreignkey', 'y')
        assert rel.isValid()
Exemplo n.º 29
0
 def applyJoin(self):
     self.dlg.show()
     selectedFields = self.getSelFieldList() or []
     if self.dlg.targetLayerCombo.currentText()[:6] != 'Select' and self.dlg.joinLayerCombo.currentText()[:6] != 'Select' and self.dlg.spatialTypeCombo.currentText()[:6] != 'Select':
         targetLayer = self.layerSet[self.dlg.targetLayerCombo.currentText()]
         joinLayer = self.layerSet[self.dlg.joinLayerCombo.currentText()]
         joinLayerFields = [field.name() for field in joinLayer.pendingFields()]
         #targetLayerFields = [field.name() for field in targetLayer.pendingFields()]
         targetLayerFields = []
         for field in targetLayer.pendingFields():
             if field.name()[0:7] == 'spjoin_':
                 idx = targetLayer.pendingFields().fieldNameIndex(field.name())
                 self.tra.ce("removing:"+field.name()+str(idx))
                 targetLayer.dataProvider().deleteAttributes([idx])
                 targetLayer.removeExpressionField(idx)
             else:
                 targetLayerFields.append(field.name())
         self.tra.ce(targetLayerFields)
         targetLayer.updateFields()
         joinField = 'spjoin_rif'
         exp = "geom"+self.dlg.spatialTypeCombo.currentText()+"('"+joinLayer.name()+"','$id')"
         self.tra.ce( exp)
         #add a rif field if build relation requested
         if self.dlg.checkBuildRelation.checkState() == Qt.Checked:
             #joinLayer.addExpressionField('$id', QgsField('spjoin_rif', QVariant.Int))
             joinLayer.dataProvider().addAttributes([QgsField(joinField, QVariant.Int)])
             #joinLayer.updateFields()
             idx = joinLayer.dataProvider().fields().fieldNameIndex(joinField)
             expObj = QgsExpression('$id')
             changes = {}
             for feature in joinLayer.getFeatures():
                 value = expObj.evaluate(feature)
                 #joinLayer.dataProvider().changeAttributeValues({feature.id():{idx:value}})
                 changes[feature.id()] = {idx:value}
             joinLayer.dataProvider().changeAttributeValues(changes)
         if self.dlg.checkDynamicJoin.checkState() == Qt.Checked:
             targetLayer.addExpressionField(exp, QgsField(joinField, QVariant.Int))
         else:
             #Create static rif field
             targetLayer.dataProvider().addAttributes([QgsField(joinField, QVariant.Int)])
             #targetLayer.updateFields()
             F = [field.name() for field in targetLayer.dataProvider().fields()]
             self.tra.ce(F)
             #Compile spatial expression to get feature rifs
             expObj = QgsExpression(exp)
             expObj.prepare(targetLayer.pendingFields())
             idx = targetLayer.dataProvider().fields().fieldNameIndex(joinField)
             self.tra.ce( "new " + joinField + str(idx))
             changes = {}
             #init progress bar
             self.dlg.progressBar.setMinimum(0)
             self.dlg.progressBar.setMaximum(targetLayer.featureCount())
             #cicle into feature to build mod vector
             for count, feature in enumerate(targetLayer.getFeatures()):
                 self.dlg.progressBar.setValue(count)
                 value = expObj.evaluate(feature)
                 changes[feature.id()] = {idx:value}
             self.tra.ce(changes)
             #apply mod vector
             targetLayer.dataProvider().changeAttributeValues(changes)
         targetLayer.updateFields()
         #build expression fields to connect to join field rif
         for f in selectedFields:
             fieldType = joinLayer.pendingFields().field(f).type()
             exp = """dbvaluebyid('%s','%s',"%s")""" %(joinLayer.name(),f,joinField)
             self.tra.ce(exp)
             targetLayer.addExpressionField(exp, QgsField('spjoin_'+f, fieldType))
         targetLayer.updateFields()
         if self.dlg.checkBuildRelation.checkState() == Qt.Checked:
             jRel = 	QgsRelation()
             jRel.setRelationId(targetLayer.name()+"_"+str(uuid.uuid1()))
             jRel.setRelationName("%s_%s-rif_%s" % (targetLayer.name(),self.dlg.spatialTypeCombo.currentText(),joinLayer.name()))
             jRel.setReferencedLayer(joinLayer.id())
             jRel.setReferencingLayer(targetLayer.id())
             jRel.addFieldPair('spjoin_rif','spjoin_rif')
             QgsProject.instance().relationManager().addRelation(jRel)
         self.dlg.hide()
Exemplo n.º 30
0
    def __init__(self, config, parent):
        super().__init__(config, parent)
        self._updateUiTimer = QTimer()
        self._updateUiTimer.setSingleShot(True)
        self._updateUiTimer.timeout.connect(self.updateUiTimeout)
        self.setupUi(self)

        self.polymorphicRelationEnabled = False
        self.polymorphicRelationId = str()

        self.generatedRelationList = []

        self._nmRelation = QgsRelation()
        self._polymorphicRelation = QgsPolymorphicRelation()
        self._layerInSameTransactionGroup = False

        self.cardinality = Cardinality.ManyToOne

        # Actions
        self.actionToggleEditing = QAction(
            QIcon(":/images/themes/default/mActionToggleEditing.svg"),
            self.tr("Toggle editing mode for child layers"))
        self.actionToggleEditing.setCheckable(True)
        self.actionSaveEdits = QAction(
            QIcon(":/images/themes/default/mActionSaveEdits.svg"),
            self.tr("Save child layer edits"))
        self.actionShowForm = QAction(
            QIcon(":/images/themes/default/mActionMultiEdit.svg"),
            self.tr("Show form"))
        self.actionLinkFeature = QAction(
            QIcon(":/images/themes/default/mActionLink.svg"),
            self.tr("Link feature"))
        self.actionUnlinkFeature = QAction(
            QIcon(":/images/themes/default/mActionUnlink.svg"),
            self.tr("Unlink feature"))

        # Tool buttons
        self.mToggleEditingToolButton.setDefaultAction(
            self.actionToggleEditing)
        self.mSaveEditsToolButton.setDefaultAction(self.actionSaveEdits)
        self.mShowFormToolButton.setDefaultAction(self.actionShowForm)
        self.mShowFormToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.mLinkFeaturesToolButton.setDefaultAction(self.actionLinkFeature)
        self.mLinkFeaturesToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.mUnlinkFeaturesToolButton.setDefaultAction(
            self.actionUnlinkFeature)
        self.mUnlinkFeaturesToolButton.setToolButtonStyle(
            Qt.ToolButtonIconOnly)

        # TreeWidgetItem menu
        self.mFeaturesTreeWidget.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.mFeaturesTreeWidget.addAction(self.actionShowForm)
        self.mFeaturesTreeWidget.addAction(self.actionUnlinkFeature)

        # Set initial state for add / remove etc.buttons
        self.updateButtons()

        # Signal slots
        self.actionToggleEditing.triggered.connect(self.toggleEditing)
        self.actionSaveEdits.triggered.connect(self.saveChildLayerEdits)
        self.actionShowForm.triggered.connect(self.actionShowFormTriggered)
        self.actionLinkFeature.triggered.connect(
            self.actionLinkFeatureTriggered)
        self.actionUnlinkFeature.triggered.connect(
            self.actionUnlinkFeatureTriggered)
        self.mFeaturesTreeWidget.currentItemChanged.connect(self.updateButtons)
Exemplo n.º 31
0
    def test_isValid(self):
        referencedLayer = createReferencedLayer()
        referencingLayer = createReferencingLayer()
        QgsMapLayerRegistry.instance().addMapLayers([referencedLayer,referencingLayer])

        rel = QgsRelation()
        assert not rel.isValid()

        rel.setRelationId( 'rel1' )
        assert not rel.isValid()

        rel.setRelationName( 'Relation Number One' )
        assert not rel.isValid()

        rel.setReferencingLayer( referencingLayer.id() )
        assert not rel.isValid()

        rel.setReferencedLayer( referencedLayer.id() )
        assert not rel.isValid()

        rel.addFieldPair( 'foreignkey', 'y' )
        assert rel.isValid()

        QgsMapLayerRegistry.instance().removeAllMapLayers()
Exemplo n.º 32
0
    def testRelations(self):
        """ check that layers are shown in widget model"""
        p = QgsProject.instance()

        # not valid, but doesn't matter for test....
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        rel2 = QgsRelation()
        rel2.setId('rel2')
        rel2.setName('Relation Number Two')
        rel2.setReferencingLayer(self.referencingLayer.id())
        rel2.setReferencedLayer(self.referencedLayer.id())
        rel2.addFieldPair('foreignkey', 'y')

        p.relationManager().addRelation(rel)
        p.relationManager().addRelation(rel2)

        w = QgsExpressionBuilderWidget()
        m = w.model()

        # check that relations are shown
        items = m.findItems('Relation Number One', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
        items = m.findItems('Relation Number Two', Qt.MatchRecursive)
        self.assertEqual(len(items), 1)
Exemplo n.º 33
0
    def test_getRelatedFeatures(self):
        rel = QgsRelation()

        rel.setRelationId('rel1')
        rel.setRelationName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = self.referencedLayer.getFeatures().next()

        it = rel.getRelatedFeatures(feat)

        assert [a.attributes() for a in it] == [[u'test1', 123], [u'test2', 123]]
Exemplo n.º 34
0
    def testDuplicateFeature(self):
        """ test duplicating a feature """

        project = QgsProject().instance()

        # LAYERS
        # - add first layer (parent)
        layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer",
                                "parentlayer", "memory")
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # -  set the value for the copy
        layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)"))
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # - add second layer (child)
        layer2 = QgsVectorLayer(
            "Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer",
            "childlayer", "memory")
        # > check second layer (child)
        self.assertTrue(layer2.isValid())
        # - add layers
        project.addMapLayers([layer1, layer2])

        # FEATURES
        # - add 2 features on layer1 (parent)
        l1f1orig = QgsFeature()
        l1f1orig.setFields(layer1.fields())
        l1f1orig.setAttributes(["F_l1f1", 100])
        l1f2orig = QgsFeature()
        l1f2orig.setFields(layer1.fields())
        l1f2orig.setAttributes(["F_l1f2", 101])
        # > check by adding features
        self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig,
                                                           l1f2orig]))
        # add 4 features on layer2 (child)
        l2f1orig = QgsFeature()
        l2f1orig.setFields(layer2.fields())
        l2f1orig.setAttributes(["F_l2f1", 201, 100])
        l2f2orig = QgsFeature()
        l2f2orig.setFields(layer2.fields())
        l2f2orig.setAttributes(["F_l2f2", 202, 100])
        l2f3orig = QgsFeature()
        l2f3orig.setFields(layer2.fields())
        l2f3orig.setAttributes(["F_l2f3", 203, 100])
        l2f4orig = QgsFeature()
        l2f4orig.setFields(layer2.fields())
        l2f4orig.setAttributes(["F_l2f4", 204, 101])
        # > check by adding features
        self.assertTrue(layer2.dataProvider().addFeatures(
            [l2f1orig, l2f2orig, l2f3orig, l2f4orig]))

        # RELATION
        # - create the relationmanager
        relMgr = project.relationManager()
        # - create the relation
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('childrel')
        rel.setReferencingLayer(layer2.id())
        rel.setReferencedLayer(layer1.id())
        rel.addFieldPair('foreign_key', 'pkid')
        rel.setStrength(QgsRelation.Composition)
        # > check relation
        self.assertTrue(rel.isValid())
        # - add relation
        relMgr.addRelation(rel)
        # > check if referencedLayer is layer1
        self.assertEqual(rel.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(rel.referencingLayer(), layer2)
        # > check if the layers are correct in relation when loading from relationManager
        relations = project.relationManager().relations()
        relation = relations[list(relations.keys())[0]]
        # > check if referencedLayer is layer1
        self.assertEqual(relation.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(relation.referencingLayer(), layer2)
        # > check the relatedfeatures
        '''
        # testoutput 1
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        print( "\n--------------------------")

        print( "\nFeatures on layer1")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2")
        for f in layer2.getFeatures():
            print( f.attributes() )
        '''

        # DUPLICATION
        # - duplicate feature l1f1orig with children
        layer1.startEditing()
        results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig,
                                                       project, 0)

        # > check if name is name of duplicated (pk is different)
        result_feature = results[0]
        self.assertEqual(result_feature.attribute('fldtxt'),
                         l1f1orig.attribute('fldtxt'))
        # > check duplicated child layer
        result_layer = results[1].layers()[0]
        self.assertEqual(result_layer, layer2)
        #  > check duplicated child features
        self.assertTrue(results[1].duplicatedFeatures(result_layer))
        '''
        # testoutput 2
        print( "\nFeatures on layer1 (after duplication)")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2 (after duplication)")
        for f in layer2.getFeatures():
            print( f.attributes() )
            
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        '''

        # > compare text of parent feature
        self.assertEqual(result_feature.attribute('fldtxt'),
                         l1f1orig.attribute('fldtxt'))

        # - create copyValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(result_feature)
        copyValueList = []
        while relfeatit.nextFeature(childFeature):
            copyValueList.append(childFeature.attribute('fldtxt'))
        # - create origValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(l1f1orig)
        origValueList = []
        while relfeatit.nextFeature(childFeature):
            origValueList.append(childFeature.attribute('fldtxt'))

        # - check if the ids are still the same
        self.assertEqual(copyValueList, origValueList)
Exemplo n.º 35
0
    def test_getReferencedFeature(self):
        rel = QgsRelation()
        rel.setRelationId("rel1")
        rel.setRelationName("Relation Number One")
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair("foreignkey", "y")

        feat = next(self.referencingLayer.getFeatures())

        f = rel.getReferencedFeature(feat)

        assert f.isValid()
        assert f[0] == "foo"
Exemplo n.º 36
0
    def test_representValue(self):

        first_layer = QgsVectorLayer("none?field=foreign_key:integer",
                                     "first_layer", "memory")
        self.assertTrue(first_layer.isValid())
        second_layer = QgsVectorLayer("none?field=pkid:integer&field=decoded:string",
                                      "second_layer", "memory")
        self.assertTrue(second_layer.isValid())
        QgsProject.instance().addMapLayers([first_layer, second_layer])
        f = QgsFeature()
        f.setAttributes([123])
        first_layer.dataProvider().addFeatures([f])
        f = QgsFeature()
        f.setAttributes([123, 'decoded_val'])
        second_layer.dataProvider().addFeatures([f])

        relMgr = QgsProject.instance().relationManager()

        fieldFormatter = QgsRelationReferenceFieldFormatter()

        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(first_layer.id())
        rel.setReferencedLayer(second_layer.id())
        rel.addFieldPair('foreign_key', 'pkid')
        self.assertTrue(rel.isValid())

        relMgr.addRelation(rel)

        # Everything valid
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '123'), 'decoded_val')

        # Code not find match in foreign layer
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '456'), '456')

        # Invalid relation id
        config = {'Relation': 'invalid'}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123')

        # No display expression
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression(None)
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123')

        # Invalid display expression
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('invalid +')
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123')

        # Missing relation
        config = {}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123')

        # Inconsistent layer provided to representValue()
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(second_layer, 0, config, None, '123'), '123')

        # Inconsistent idx provided to representValue()
        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(first_layer, 1, config, None, '123'), '123')

        # Invalid relation
        rel = QgsRelation()
        rel.setId('rel2')
        rel.setName('Relation Number Two')
        rel.setReferencingLayer(first_layer.id())
        rel.addFieldPair('foreign_key', 'pkid')
        self.assertFalse(rel.isValid())

        relMgr.addRelation(rel)

        config = {'Relation': rel.id()}
        second_layer.setDisplayExpression('decoded')
        self.assertEqual(fieldFormatter.representValue(first_layer, 0, config, None, '123'), '123')

        QgsProject.instance().removeAllMapLayers()
Exemplo n.º 37
0
    def test_isValid(self):
        rel = QgsRelation()
        assert not rel.isValid()

        rel.setRelationId("rel1")
        assert not rel.isValid()

        rel.setRelationName("Relation Number One")
        assert not rel.isValid()

        rel.setReferencingLayer(self.referencingLayer.id())
        assert not rel.isValid()

        rel.setReferencedLayer(self.referencedLayer.id())
        assert not rel.isValid()

        rel.addFieldPair("foreignkey", "y")
        assert rel.isValid()
Exemplo n.º 38
0
    def testExportFeatureRelations(self):
        """ Test exporting a feature with relations """

        #parent layer
        parent = QgsVectorLayer(
            "Point?field=fldtxt:string&field=fldint:integer&field=foreignkey:integer",
            "parent", "memory")
        pr = parent.dataProvider()
        pf1 = QgsFeature()
        pf1.setFields(parent.fields())
        pf1.setAttributes(["test1", 67, 123])
        pf2 = QgsFeature()
        pf2.setFields(parent.fields())
        pf2.setAttributes(["test2", 68, 124])
        assert pr.addFeatures([pf1, pf2])

        #child layer
        child = QgsVectorLayer(
            "Point?field=x:string&field=y:integer&field=z:integer",
            "referencedlayer", "memory")
        pr = child.dataProvider()
        f1 = QgsFeature()
        f1.setFields(child.fields())
        f1.setAttributes(["foo", 123, 321])
        f2 = QgsFeature()
        f2.setFields(child.fields())
        f2.setAttributes(["bar", 123, 654])
        f3 = QgsFeature()
        f3.setFields(child.fields())
        f3.setAttributes(["foobar", 124, 554])
        assert pr.addFeatures([f1, f2, f3])

        QgsProject.instance().addMapLayers([child, parent])

        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('relation one')
        rel.setReferencingLayer(child.id())
        rel.setReferencedLayer(parent.id())
        rel.addFieldPair('y', 'foreignkey')

        QgsProject.instance().relationManager().addRelation(rel)

        exporter = QgsJsonExporter()

        exporter.setVectorLayer(parent)
        self.assertEqual(exporter.vectorLayer(), parent)
        exporter.setIncludeRelated(True)
        self.assertEqual(exporter.includeRelated(), True)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test1",
      "fldint":67,
      "foreignkey":123,
      "relation one":[{"x":"foo",
"y":123,
"z":321},
{"x":"bar",
"y":123,
"z":654}]
   }
}"""
        self.assertEqual(exporter.exportFeature(pf1), expected)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test2",
      "fldint":68,
      "foreignkey":124,
      "relation one":[{"x":"foobar",
"y":124,
"z":554}]
   }
}"""
        self.assertEqual(exporter.exportFeature(pf2), expected)

        # with field formatter
        setup = QgsEditorWidgetSetup('ValueMap',
                                     {"map": {
                                         "apples": 123,
                                         "bananas": 124
                                     }})
        child.setEditorWidgetSetup(1, setup)
        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test1",
      "fldint":67,
      "foreignkey":123,
      "relation one":[{"x":"foo",
"y":"apples",
"z":321},
{"x":"bar",
"y":"apples",
"z":654}]
   }
}"""
        self.assertEqual(exporter.exportFeature(pf1), expected)

        # test excluding related attributes
        exporter.setIncludeRelated(False)
        self.assertEqual(exporter.includeRelated(), False)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test2",
      "fldint":68,
      "foreignkey":124
   }
}"""
        self.assertEqual(exporter.exportFeature(pf2), expected)

        # test without vector layer set
        exporter.setIncludeRelated(True)
        exporter.setVectorLayer(None)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test2",
      "fldint":68,
      "foreignkey":124
   }
}"""
        self.assertEqual(exporter.exportFeature(pf2), expected)
Exemplo n.º 39
0
 def createRelation(self):
     rel = QgsRelation()
     rel.setReferencingLayer(self.referencingLayer.id())
     rel.setReferencedLayer(self.referencedLayer.id())
     rel.addFieldPair('foreignkey', 'y')
     return rel
Exemplo n.º 40
0
    def test_getRelatedFeatures(self):
        rel = QgsRelation()

        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = next(self.referencedLayer.getFeatures())

        self.assertEqual(rel.getRelatedFeaturesFilter(feat),
                         '"foreignkey" = 123')

        it = rel.getRelatedFeatures(feat)
        assert [a.attributes() for a in it] == [['test1', 123], ['test2', 123]]
Exemplo n.º 41
0
    def testExportFeatureRelations(self):
        """ Test exporting a feature with relations """

        #parent layer
        parent = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=foreignkey:integer",
                                "parent", "memory")
        pr = parent.dataProvider()
        pf1 = QgsFeature()
        pf1.setFields(parent.fields())
        pf1.setAttributes(["test1", 67, 123])
        pf2 = QgsFeature()
        pf2.setFields(parent.fields())
        pf2.setAttributes(["test2", 68, 124])
        assert pr.addFeatures([pf1, pf2])

        #child layer
        child = QgsVectorLayer(
            "Point?field=x:string&field=y:integer&field=z:integer",
            "referencedlayer", "memory")
        pr = child.dataProvider()
        f1 = QgsFeature()
        f1.setFields(child.fields())
        f1.setAttributes(["foo", 123, 321])
        f2 = QgsFeature()
        f2.setFields(child.fields())
        f2.setAttributes(["bar", 123, 654])
        f3 = QgsFeature()
        f3.setFields(child.fields())
        f3.setAttributes(["foobar", 124, 554])
        assert pr.addFeatures([f1, f2, f3])

        QgsProject.instance().addMapLayers([child, parent])

        rel = QgsRelation()
        rel.setRelationId('rel1')
        rel.setRelationName('relation one')
        rel.setReferencingLayer(child.id())
        rel.setReferencedLayer(parent.id())
        rel.addFieldPair('y', 'foreignkey')

        QgsProject.instance().relationManager().addRelation(rel)

        exporter = QgsJSONExporter()

        exporter.setVectorLayer(parent)
        self.assertEqual(exporter.vectorLayer(), parent)
        exporter.setIncludeRelated(True)
        self.assertEqual(exporter.includeRelated(), True)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test1",
      "fldint":67,
      "foreignkey":123,
      "relation one":[{"x":"foo",
"y":123,
"z":321},
{"x":"bar",
"y":123,
"z":654}]
   }
}"""
        self.assertEqual(exporter.exportFeature(pf1), expected)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test2",
      "fldint":68,
      "foreignkey":124,
      "relation one":[{"x":"foobar",
"y":124,
"z":554}]
   }
}"""
        self.assertEqual(exporter.exportFeature(pf2), expected)

        # test excluding related attributes
        exporter.setIncludeRelated(False)
        self.assertEqual(exporter.includeRelated(), False)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test2",
      "fldint":68,
      "foreignkey":124
   }
}"""
        self.assertEqual(exporter.exportFeature(pf2), expected)

        # test without vector layer set
        exporter.setIncludeRelated(True)
        exporter.setVectorLayer(None)

        expected = """{
   "type":"Feature",
   "id":0,
   "geometry":null,
   "properties":{
      "fldtxt":"test2",
      "fldint":68,
      "foreignkey":124
   }
}"""
        self.assertEqual(exporter.exportFeature(pf2), expected)
Exemplo n.º 42
0
    def test_getRelatedFeaturesWithQuote(self):
        rel = QgsRelation()

        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('fldtxt', 'x')

        feat = self.referencedLayer.getFeature(3)

        it = rel.getRelatedFeatures(feat)
        assert next(it).attributes() == ["foobar'bar", 124]
Exemplo n.º 43
0
    def test_getRelatedFeatures(self):
        rel = QgsRelation()

        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = next(self.referencedLayer.getFeatures())

        self.assertEqual(rel.getRelatedFeaturesFilter(feat), '"foreignkey" = 123')

        it = rel.getRelatedFeatures(feat)
        assert [a.attributes() for a in it] == [['test1', 123], ['test2', 123]]
Exemplo n.º 44
0
    def test_getReferencedFeature(self):
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = next(self.referencingLayer.getFeatures())

        f = rel.getReferencedFeature(feat)

        assert f.isValid()
        assert f[0] == 'foo'
Exemplo n.º 45
0
    def test_getReferencedFeature(self):
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        feat = next(self.referencingLayer.getFeatures())

        f = rel.getReferencedFeature(feat)

        assert f.isValid()
        assert f[0] == 'foo'
Exemplo n.º 46
0
    def test_fieldPairs(self):
        rel = QgsRelation()

        rel.setId('rel1')
        rel.setName('Relation Number One')
        rel.setReferencingLayer(self.referencingLayer.id())
        rel.setReferencedLayer(self.referencedLayer.id())
        rel.addFieldPair('foreignkey', 'y')

        assert (rel.fieldPairs() == {'foreignkey': 'y'})
Exemplo n.º 47
0
    def test_isValid(self):
        rel = QgsRelation()
        assert not rel.isValid()

        rel.setId('rel1')
        assert not rel.isValid()

        rel.setName('Relation Number One')
        assert not rel.isValid()

        rel.setReferencingLayer(self.referencingLayer.id())
        assert not rel.isValid()

        rel.setReferencedLayer(self.referencedLayer.id())
        assert not rel.isValid()

        rel.addFieldPair('foreignkey', 'y')
        assert rel.isValid()
    def createRelation(manager, referenced, referencing, field_a, field_b, name, id_relation, feedback):
        rel = QgsRelation()
        rel.setReferencedLayer(referenced.id())
        rel.setReferencingLayer(referencing.id())
        rel.addFieldPair(field_a, field_b)
        rel.setName(name)
        rel.setId(id_relation)

        manager.addRelation(rel)
        feedback.pushInfo("// Relation entre {} et {}".format(referenced.name(), referencing.name()))