mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-29 00:03:59 -04:00
268 lines
11 KiB
Python
268 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""QGIS Unit tests for QgsSnappingUtils (complement to C++-based tests)
|
|
|
|
.. note:: This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
"""
|
|
__author__ = 'Hugo Mercier'
|
|
__date__ = '12/07/2016'
|
|
__copyright__ = 'Copyright 2016, The QGIS Project'
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
__revision__ = '$Format:%H$'
|
|
|
|
import qgis # NOQA
|
|
import os
|
|
|
|
from qgis.core import (QgsProject,
|
|
QgsVectorLayer,
|
|
QgsMapSettings,
|
|
QgsSnappingUtils,
|
|
QgsSnappingConfig,
|
|
QgsPointLocator,
|
|
QgsTolerance,
|
|
QgsRectangle,
|
|
QgsPoint,
|
|
QgsFeature,
|
|
QgsGeometry,
|
|
QgsProject,
|
|
QgsLayerDefinition,
|
|
QgsMapLayerDependency
|
|
)
|
|
|
|
from qgis.testing import start_app, unittest
|
|
from utilities import unitTestDataPath
|
|
|
|
from qgis.PyQt.QtCore import QSize, QPoint
|
|
|
|
import tempfile
|
|
|
|
from qgis.utils import spatialite_connect
|
|
|
|
# Convenience instances in case you may need them
|
|
start_app()
|
|
|
|
|
|
class TestLayerDependencies(unittest.TestCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""Run before all tests"""
|
|
|
|
# create a temp spatialite db with a trigger
|
|
fo = tempfile.NamedTemporaryFile()
|
|
fn = fo.name
|
|
fo.close()
|
|
cls.fn = fn
|
|
con = spatialite_connect(fn)
|
|
cur = con.cursor()
|
|
cur.execute("SELECT InitSpatialMetadata(1)")
|
|
cur.execute("create table node(id integer primary key autoincrement);")
|
|
cur.execute("select AddGeometryColumn('node', 'geom', 4326, 'POINT');")
|
|
cur.execute("create table section(id integer primary key autoincrement, node1 integer, node2 integer);")
|
|
cur.execute("select AddGeometryColumn('section', 'geom', 4326, 'LINESTRING');")
|
|
cur.execute("create trigger add_nodes after insert on section begin insert into node (geom) values (st_startpoint(NEW.geom)); insert into node (geom) values (st_endpoint(NEW.geom)); end;")
|
|
cur.execute("insert into node (geom) values (geomfromtext('point(0 0)', 4326));")
|
|
cur.execute("insert into node (geom) values (geomfromtext('point(1 0)', 4326));")
|
|
cur.execute("create table node2(id integer primary key autoincrement);")
|
|
cur.execute("select AddGeometryColumn('node2', 'geom', 4326, 'POINT');")
|
|
cur.execute("create trigger add_nodes2 after insert on node begin insert into node2 (geom) values (st_translate(NEW.geom, 0.2, 0, 0)); end;")
|
|
con.commit()
|
|
con.close()
|
|
|
|
cls.pointsLayer = QgsVectorLayer("dbname='%s' table=\"node\" (geom) sql=" % fn, "points", "spatialite")
|
|
assert (cls.pointsLayer.isValid())
|
|
cls.linesLayer = QgsVectorLayer("dbname='%s' table=\"section\" (geom) sql=" % fn, "lines", "spatialite")
|
|
assert (cls.linesLayer.isValid())
|
|
cls.pointsLayer2 = QgsVectorLayer("dbname='%s' table=\"node2\" (geom) sql=" % fn, "_points2", "spatialite")
|
|
assert (cls.pointsLayer2.isValid())
|
|
QgsProject.instance().addMapLayers([cls.pointsLayer, cls.linesLayer, cls.pointsLayer2])
|
|
|
|
# save the project file
|
|
fo = tempfile.NamedTemporaryFile()
|
|
fn = fo.name
|
|
fo.close()
|
|
cls.projectFile = fn
|
|
QgsProject.instance().setFileName(cls.projectFile)
|
|
QgsProject.instance().write()
|
|
|
|
@classmethod
|
|
def tearDownClass(cls):
|
|
"""Run after all tests"""
|
|
pass
|
|
|
|
def setUp(self):
|
|
"""Run before each test."""
|
|
pass
|
|
|
|
def tearDown(self):
|
|
"""Run after each test."""
|
|
pass
|
|
|
|
def test_resetSnappingIndex(self):
|
|
self.pointsLayer.setDependencies([])
|
|
self.linesLayer.setDependencies([])
|
|
self.pointsLayer2.setDependencies([])
|
|
|
|
ms = QgsMapSettings()
|
|
ms.setOutputSize(QSize(100, 100))
|
|
ms.setExtent(QgsRectangle(0, 0, 1, 1))
|
|
self.assertTrue(ms.hasValidSettings())
|
|
|
|
u = QgsSnappingUtils()
|
|
u.setMapSettings(ms)
|
|
cfg = u.config()
|
|
cfg.setEnabled(True)
|
|
cfg.setMode(QgsSnappingConfig.AdvancedConfiguration)
|
|
cfg.setIndividualLayerSettings(self.pointsLayer,
|
|
QgsSnappingConfig.IndividualLayerSettings(True,
|
|
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
|
u.setConfig(cfg)
|
|
|
|
m = u.snapToMap(QPoint(95, 100))
|
|
self.assertTrue(m.isValid())
|
|
self.assertTrue(m.hasVertex())
|
|
self.assertEqual(m.point(), QgsPoint(1, 0))
|
|
|
|
f = QgsFeature(self.linesLayer.fields())
|
|
f.setId(1)
|
|
geom = QgsGeometry.fromWkt("LINESTRING(0 0,1 1)")
|
|
f.setGeometry(geom)
|
|
self.linesLayer.startEditing()
|
|
self.linesLayer.addFeatures([f])
|
|
self.linesLayer.commitChanges()
|
|
|
|
l1 = len([f for f in self.pointsLayer.getFeatures()])
|
|
self.assertEqual(l1, 4)
|
|
m = u.snapToMap(QPoint(95, 0))
|
|
# snapping not updated
|
|
self.pointsLayer.setDependencies([])
|
|
self.assertEqual(m.isValid(), False)
|
|
|
|
# set layer dependencies
|
|
self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())])
|
|
# add another line
|
|
f = QgsFeature(self.linesLayer.fields())
|
|
f.setId(2)
|
|
geom = QgsGeometry.fromWkt("LINESTRING(0 0,0.5 0.5)")
|
|
f.setGeometry(geom)
|
|
self.linesLayer.startEditing()
|
|
self.linesLayer.addFeatures([f])
|
|
self.linesLayer.commitChanges()
|
|
# check the snapped point is ok
|
|
m = u.snapToMap(QPoint(45, 50))
|
|
self.assertTrue(m.isValid())
|
|
self.assertTrue(m.hasVertex())
|
|
self.assertEqual(m.point(), QgsPoint(0.5, 0.5))
|
|
self.pointsLayer.setDependencies([])
|
|
|
|
# test chained layer dependencies A -> B -> C
|
|
cfg.setIndividualLayerSettings(self.pointsLayer2,
|
|
QgsSnappingConfig.IndividualLayerSettings(True,
|
|
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
|
u.setConfig(cfg)
|
|
self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())])
|
|
self.pointsLayer2.setDependencies([QgsMapLayerDependency(self.pointsLayer.id())])
|
|
# add another line
|
|
f = QgsFeature(self.linesLayer.fields())
|
|
f.setId(3)
|
|
geom = QgsGeometry.fromWkt("LINESTRING(0 0.2,0.5 0.8)")
|
|
f.setGeometry(geom)
|
|
self.linesLayer.startEditing()
|
|
self.linesLayer.addFeatures([f])
|
|
self.linesLayer.commitChanges()
|
|
# check the second snapped point is ok
|
|
m = u.snapToMap(QPoint(75, 100 - 80))
|
|
self.assertTrue(m.isValid())
|
|
self.assertTrue(m.hasVertex())
|
|
self.assertEqual(m.point(), QgsPoint(0.7, 0.8))
|
|
self.pointsLayer.setDependencies([])
|
|
self.pointsLayer2.setDependencies([])
|
|
|
|
def test_cycleDetection(self):
|
|
self.assertTrue(self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())]))
|
|
self.assertFalse(self.linesLayer.setDependencies([QgsMapLayerDependency(self.pointsLayer.id())]))
|
|
self.pointsLayer.setDependencies([])
|
|
self.linesLayer.setDependencies([])
|
|
|
|
def test_layerDefinitionRewriteId(self):
|
|
tmpfile = os.path.join(tempfile.tempdir, "test.qlr")
|
|
|
|
ltr = QgsProject.instance().layerTreeRoot()
|
|
|
|
self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())])
|
|
|
|
QgsLayerDefinition.exportLayerDefinition(tmpfile, [ltr])
|
|
|
|
grp = ltr.addGroup("imported")
|
|
QgsLayerDefinition.loadLayerDefinition(tmpfile, grp)
|
|
|
|
newPointsLayer = None
|
|
newLinesLayer = None
|
|
for l in grp.findLayers():
|
|
if l.layerId().startswith('points'):
|
|
newPointsLayer = l.layer()
|
|
elif l.layerId().startswith('lines'):
|
|
newLinesLayer = l.layer()
|
|
self.assertFalse(newPointsLayer is None)
|
|
self.assertFalse(newLinesLayer is None)
|
|
self.assertTrue(newLinesLayer.id() in [dep.layerId() for dep in newPointsLayer.dependencies()])
|
|
|
|
self.pointsLayer.setDependencies([])
|
|
|
|
def test_signalConnection(self):
|
|
# remove all layers
|
|
QgsProject.instance().removeAllMapLayers()
|
|
# set dependencies and add back layers
|
|
self.pointsLayer = QgsVectorLayer("dbname='%s' table=\"node\" (geom) sql=" % self.fn, "points", "spatialite")
|
|
assert (self.pointsLayer.isValid())
|
|
self.linesLayer = QgsVectorLayer("dbname='%s' table=\"section\" (geom) sql=" % self.fn, "lines", "spatialite")
|
|
assert (self.linesLayer.isValid())
|
|
self.pointsLayer2 = QgsVectorLayer("dbname='%s' table=\"node2\" (geom) sql=" % self.fn, "_points2", "spatialite")
|
|
assert (self.pointsLayer2.isValid())
|
|
self.pointsLayer.setDependencies([QgsMapLayerDependency(self.linesLayer.id())])
|
|
self.pointsLayer2.setDependencies([QgsMapLayerDependency(self.pointsLayer.id())])
|
|
# this should update connections between layers
|
|
QgsProject.instance().addMapLayers([self.pointsLayer])
|
|
QgsProject.instance().addMapLayers([self.linesLayer])
|
|
QgsProject.instance().addMapLayers([self.pointsLayer2])
|
|
|
|
ms = QgsMapSettings()
|
|
ms.setOutputSize(QSize(100, 100))
|
|
ms.setExtent(QgsRectangle(0, 0, 1, 1))
|
|
self.assertTrue(ms.hasValidSettings())
|
|
|
|
u = QgsSnappingUtils()
|
|
u.setMapSettings(ms)
|
|
cfg = u.config()
|
|
cfg.setEnabled(True)
|
|
cfg.setMode(QgsSnappingConfig.AdvancedConfiguration)
|
|
cfg.setIndividualLayerSettings(self.pointsLayer,
|
|
QgsSnappingConfig.IndividualLayerSettings(True,
|
|
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
|
cfg.setIndividualLayerSettings(self.pointsLayer2,
|
|
QgsSnappingConfig.IndividualLayerSettings(True,
|
|
QgsSnappingConfig.Vertex, 20, QgsTolerance.Pixels))
|
|
u.setConfig(cfg)
|
|
# add another line
|
|
f = QgsFeature(self.linesLayer.fields())
|
|
f.setId(4)
|
|
geom = QgsGeometry.fromWkt("LINESTRING(0.5 0.2,0.6 0)")
|
|
f.setGeometry(geom)
|
|
self.linesLayer.startEditing()
|
|
self.linesLayer.addFeatures([f])
|
|
self.linesLayer.commitChanges()
|
|
# check the second snapped point is ok
|
|
m = u.snapToMap(QPoint(75, 100 - 0))
|
|
self.assertTrue(m.isValid())
|
|
self.assertTrue(m.hasVertex())
|
|
self.assertEqual(m.point(), QgsPoint(0.8, 0.0))
|
|
|
|
self.pointsLayer.setDependencies([])
|
|
self.pointsLayer2.setDependencies([])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|