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)
Ejemplo n.º 2
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"}
Ejemplo n.º 3
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'})
Ejemplo n.º 4
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]
Ejemplo n.º 5
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]]
Ejemplo n.º 6
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"
Ejemplo n.º 7
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'
Ejemplo n.º 8
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]]
Ejemplo n.º 9
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]]
Ejemplo n.º 10
0
    def test_fieldPairs(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' )

        assert( rel.fieldPairs() == { 'foreignkey': 'y'} )

        QgsMapLayerRegistry.instance().removeAllMapLayers()
Ejemplo n.º 11
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()
Ejemplo n.º 12
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()
Ejemplo n.º 13
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()
Ejemplo n.º 14
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)
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
 def createRelation(self):
     rel = QgsRelation()
     rel.setReferencingLayer(self.referencingLayer.id())
     rel.setReferencedLayer(self.referencedLayer.id())
     rel.addFieldPair('foreignkey', 'y')
     return rel
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)
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)
Ejemplo n.º 19
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
Ejemplo n.º 20
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()
Ejemplo n.º 21
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)
Ejemplo n.º 22
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)
Ejemplo n.º 23
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)
Ejemplo n.º 24
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()
Ejemplo n.º 25
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()
Ejemplo n.º 26
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()
def on_resolve_href_(dialog, layer, feature, field):
    """
    @param dialog the dialog where the feature form is opened
    @param layer the layer on which the href link stands
    @param feature the current feature
    @param field the field name storing the href URL
    @param linked_layer_id the QGIS layer id of the already resolved layer, for update
    """
    from .import_gmlas_panel import ImportGmlasPanel

    path = feature[field]
    if not path:
        return

    # if parent is a Dialog, we are in a feature form
    # else in a attribute table
    is_feature_form = isinstance(dialog.parent, QDialog)

    # The href is resolved thanks to the OGR GMLAS driver.
    # We need to determine what is the "root" layer of the imported
    # href, so that we can connect the xlink:href link to the
    # newly loaded set of layers.
    # There seems to be no way to determine what is the "root" layer
    # of a GMLAS database.
    # So, we rely on XML parsing to determine the root element
    # and on layer xpath found in metadata

    # Download the file so that it is used for XML parsing
    # and for GMLAS loading
    import tempfile

    from ..core.gml_utils import extract_features_from_file
    from ..core.qgis_urlopener import remote_open_from_qgis
    from ..core.xml_utils import no_ns, no_prefix

    with remote_open_from_qgis(path) as fi:
        with tempfile.NamedTemporaryFile(delete=False) as fo:
            fo.write(fi.read())
            tmp_file = fo.name

    _, _, nodes = extract_features_from_file(tmp_file)
    if not nodes:
        raise RuntimeError("No feature found in linked document")
    root_tag = nodes[0].tag

    # reuse the GMLAS import panel widget
    dlg = QDialog()
    import_widget = ImportGmlasPanel(dlg, gml_path=tmp_file)
    path_edit = QLineEdit(path, dlg)
    path_edit.setEnabled(False)
    btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dlg)
    layout = QVBoxLayout()
    layout.addWidget(path_edit)
    layout.addWidget(import_widget)
    layout.addItem(
        QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding))
    layout.addWidget(btn)
    dlg.setLayout(layout)
    btn.accepted.connect(dlg.accept)
    btn.rejected.connect(dlg.reject)
    dlg.resize(400, 300)
    dlg.setWindowTitle("Options for xlink:href loading")
    if not dlg.exec_():
        return

    # close the current form
    w = dialog
    while not isinstance(w, QDialog):
        w = w.parent()
    w.close()

    import_widget.do_load()
    # Add a link between the current layer
    # and the root layer of the newly loaded (complex) features

    # 1. determine the root layer and pkid of all its features
    root_layer = None
    for l in QgsProject.instance().mapLayers().values():
        if no_ns(l.customProperty("xpath", "")) == no_prefix(root_tag):
            root_layer = l
            break
    if root_layer is None:
        raise RuntimeError("Cannot determine the root layer")

    pkid = layer.customProperty("pkid")
    pkid_value = feature[pkid]
    root_layer.startEditing()
    # 2. add a href_origin_pkid field in the root layer
    if "parent_href_pkid" not in [f.name() for f in root_layer.fields()]:
        new_field = QgsField(layer.fields().field(pkid))
        new_field.setName("parent_href_pkid")
        root_layer.addAttribute(new_field)

    # 3. set its value to the id of current feature
    ids_to_change = []
    for f in root_layer.getFeatures():
        if f["parent_href_pkid"] is None:
            ids_to_change.append(f.id())
    idx = root_layer.fields().indexFromName("parent_href_pkid")
    for fid in ids_to_change:
        # sets the pkid_value
        root_layer.changeAttributeValue(fid, idx, pkid_value)

    root_layer.commitChanges()

    # 4. declare a new QgsRelation
    rel_name = "1_n_" + layer.name() + "_" + field
    rel = QgsProject.instance().relationManager().relations().get(rel_name)
    if rel is None:
        rel = QgsRelation()
        rel.setId(rel_name)
        rel.setName(field)
        rel.setReferencedLayer(layer.id())
        rel.setReferencingLayer(root_layer.id())
        rel.addFieldPair("parent_href_pkid", pkid)
        QgsProject.instance().relationManager().addRelation(rel)

    # 5. declare the new relation in the form widgets
    # new 1:N in the current layer
    fc = layer.editFormConfig()
    rel_tab = fc.tabs()[1]
    rel_tab.addChildElement(
        QgsAttributeEditorRelation(rel.name(), rel, rel_tab))
    # new field in the root layer
    fc = root_layer.editFormConfig()
    main_tab = fc.tabs()[0]
    main_tab.addChildElement(
        QgsAttributeEditorField("parent_href_pkid", idx, main_tab))
    # declare as reference relation widget
    s = QgsEditorWidgetSetup(
        "RelationReference",
        {
            "AllowNULL": False,
            "ReadOnly": True,
            "Relation": rel.id(),
            "OrderByValue": False,
            "MapIdentification": False,
            "AllowAddFeatures": False,
            "ShowForm": True,
        },
    )
    root_layer.setEditorWidgetSetup(idx, s)

    # write metadata in layers
    href_resolved = layer.customProperty("href_resolved", [])
    if path not in href_resolved:
        layer.setCustomProperty("href_resolved", href_resolved + [path])
    href_linked_layers = layer.customProperty("href_linked_layers", {})
    href_linked_layers[field] = root_layer.id()
    layer.setCustomProperty("href_linked_layers", href_linked_layers)

    # 6. reload the current form
    from ..main import get_iface

    if is_feature_form:
        get_iface().openFeatureForm(layer, feature)
    else:
        get_iface().showAttributeTable(layer)
Ejemplo n.º 28
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)
Ejemplo n.º 29
0
 def createRelation(self):
     rel = QgsRelation()
     rel.setReferencingLayer(self.referencingLayer.id())
     rel.setReferencedLayer(self.referencedLayer.id())
     rel.addFieldPair('foreignkey', 'y')
     return rel
    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)
Ejemplo n.º 31
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()