1""" 2*************************************************************************** 3 test_qgssymbollayer_createsld.py 4 --------------------- 5 Date : July 2016 6 Copyright : (C) 2016 by Andrea Aime 7 Email : andrea dot aime at geosolutions dot it 8*************************************************************************** 9* * 10* This program is free software; you can redistribute it and/or modify *less 11* it under the terms of the GNU General Public License as published by * 12* the Free Software Foundation; either version 2 of the License, or * 13* (at your option) any later version. * 14* * 15*************************************************************************** 16""" 17 18__author__ = 'Andrea Aime' 19__date__ = 'July 2016' 20__copyright__ = '(C) 2012, Andrea Aime' 21 22import qgis # NOQA 23 24from qgis.PyQt.QtCore import Qt, QDir, QFile, QIODevice, QPointF, QSizeF 25from qgis.PyQt.QtXml import QDomDocument 26from qgis.PyQt.QtGui import QColor, QFont 27 28from qgis.core import ( 29 QgsSimpleMarkerSymbolLayer, QgsSimpleMarkerSymbolLayerBase, QgsUnitTypes, QgsSvgMarkerSymbolLayer, 30 QgsFontMarkerSymbolLayer, QgsEllipseSymbolLayer, QgsSimpleLineSymbolLayer, 31 QgsMarkerLineSymbolLayer, QgsMarkerSymbol, QgsSimpleFillSymbolLayer, QgsSVGFillSymbolLayer, 32 QgsLinePatternFillSymbolLayer, QgsPointPatternFillSymbolLayer, QgsVectorLayer, QgsVectorLayerSimpleLabeling, 33 QgsTextBufferSettings, QgsPalLayerSettings, QgsTextBackgroundSettings, QgsRuleBasedLabeling) 34from qgis.testing import start_app, unittest 35from utilities import unitTestDataPath 36 37# Convenience instances in case you may need them 38# not used in this test 39start_app() 40 41 42class TestQgsSymbolLayerCreateSld(unittest.TestCase): 43 """ 44 This class tests the creation of SLD from QGis layers 45 """ 46 47 def testSimpleMarkerRotation(self): 48 symbol = QgsSimpleMarkerSymbolLayer( 49 QgsSimpleMarkerSymbolLayerBase.Star, color=QColor(255, 0, 0), strokeColor=QColor(0, 255, 0), size=10) 50 symbol.setAngle(50) 51 dom, root = self.symbolToSld(symbol) 52 # print( "Simple marker rotation: " + root.ownerDocument().toString()) 53 54 self.assertStaticRotation(root, '50') 55 56 def testSimpleMarkerUnitDefault(self): 57 symbol = QgsSimpleMarkerSymbolLayer( 58 QgsSimpleMarkerSymbolLayerBase.Star, color=QColor(255, 0, 0), strokeColor=QColor(0, 255, 0), size=10) 59 symbol.setStrokeWidth(3) 60 symbol.setOffset(QPointF(5, 10)) 61 dom, root = self.symbolToSld(symbol) 62 # print("Simple marker unit mm: " + root.ownerDocument().toString()) 63 64 # Check the size has been rescaled to pixels 65 self.assertStaticSize(root, '36') 66 67 # Check the same happened to the stroke width 68 self.assertStrokeWidth(root, 2, 11) 69 self.assertStaticDisplacement(root, 18, 36) 70 71 def testSimpleMarkerUnitPixels(self): 72 symbol = QgsSimpleMarkerSymbolLayer( 73 QgsSimpleMarkerSymbolLayerBase.Star, color=QColor(255, 0, 0), strokeColor=QColor(0, 255, 0), size=10) 74 symbol.setStrokeWidth(3) 75 symbol.setOffset(QPointF(5, 10)) 76 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 77 dom, root = self.symbolToSld(symbol) 78 # print("Marker unit mm: " + root.ownerDocument().toString()) 79 80 # Check the size has not been rescaled 81 self.assertStaticSize(root, '10') 82 83 # Check the same happened to the stroke width 84 self.assertStrokeWidth(root, 2, 3) 85 self.assertStaticDisplacement(root, 5, 10) 86 87 def testSvgMarkerUnitDefault(self): 88 symbol = QgsSvgMarkerSymbolLayer('symbols/star.svg', 10, 90) 89 symbol.setFillColor(QColor("blue")) 90 symbol.setStrokeWidth(1) 91 symbol.setStrokeColor(QColor('red')) 92 symbol.setPath('symbols/star.svg') 93 symbol.setOffset(QPointF(5, 10)) 94 95 dom, root = self.symbolToSld(symbol) 96 # print("Svg marker mm: " + dom.toString()) 97 98 self.assertExternalGraphic(root, 0, 99 'symbols/star.svg?fill=%230000ff&fill-opacity=1&outline=%23ff0000&outline-opacity=1&outline-width=4', 100 'image/svg+xml') 101 self.assertExternalGraphic(root, 1, 102 'symbols/star.svg', 'image/svg+xml') 103 self.assertWellKnownMark(root, 0, 'square', '#0000ff', '#ff0000', 4) 104 105 # Check the size has been rescaled 106 self.assertStaticSize(root, '36') 107 108 # Check rotation for good measure 109 self.assertStaticRotation(root, '90') 110 self.assertStaticDisplacement(root, 18, 36) 111 112 def testSvgMarkerUnitPixels(self): 113 symbol = QgsSvgMarkerSymbolLayer('symbols/star.svg', 10, 0) 114 symbol.setFillColor(QColor("blue")) 115 symbol.setStrokeWidth(1) 116 symbol.setStrokeColor(QColor('red')) 117 symbol.setPath('symbols/star.svg') 118 symbol.setOffset(QPointF(5, 10)) 119 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 120 dom, root = self.symbolToSld(symbol) 121 # print("Svg marker unit px: " + dom.toString()) 122 123 self.assertExternalGraphic(root, 0, 124 'symbols/star.svg?fill=%230000ff&fill-opacity=1&outline=%23ff0000&outline-opacity=1&outline-width=1', 125 'image/svg+xml') 126 self.assertExternalGraphic(root, 1, 127 'symbols/star.svg', 'image/svg+xml') 128 self.assertWellKnownMark(root, 0, 'square', '#0000ff', '#ff0000', 1) 129 130 # Check the size has not been rescaled 131 self.assertStaticSize(root, '10') 132 self.assertStaticDisplacement(root, 5, 10) 133 134 def testFontMarkerUnitDefault(self): 135 symbol = QgsFontMarkerSymbolLayer('sans', ',', 10, QColor('black'), 45) 136 symbol.setOffset(QPointF(5, 10)) 137 dom, root = self.symbolToSld(symbol) 138 # print("Font marker unit mm: " + dom.toString()) 139 140 # Check the size has been rescaled 141 self.assertStaticSize(root, '36') 142 self.assertStaticRotation(root, '45') 143 self.assertStaticDisplacement(root, 18, 36) 144 145 def testFontMarkerUnitPixel(self): 146 symbol = QgsFontMarkerSymbolLayer('sans', ',', 10, QColor('black'), 45) 147 symbol.setOffset(QPointF(5, 10)) 148 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 149 dom, root = self.symbolToSld(symbol) 150 # print ("Font marker unit mm: " + dom.toString()) 151 152 # Check the size has been rescaled 153 self.assertStaticSize(root, '10') 154 self.assertStaticRotation(root, '45') 155 self.assertStaticDisplacement(root, 5, 10) 156 157 def createEllipseSymbolLayer(self): 158 # No way to build it programmatically... 159 mTestName = 'QgsEllipseSymbolLayer' 160 mFilePath = QDir.toNativeSeparators( 161 '%s/symbol_layer/%s.sld' % (unitTestDataPath(), mTestName)) 162 163 mDoc = QDomDocument(mTestName) 164 mFile = QFile(mFilePath) 165 mFile.open(QIODevice.ReadOnly) 166 mDoc.setContent(mFile, True) 167 mFile.close() 168 mSymbolLayer = QgsEllipseSymbolLayer.createFromSld( 169 mDoc.elementsByTagName('PointSymbolizer').item(0).toElement()) 170 return mSymbolLayer 171 172 def testEllipseMarkerUnitDefault(self): 173 symbol = self.createEllipseSymbolLayer() 174 symbol.setOffset(QPointF(5, 10)) 175 symbol.setOutputUnit(QgsUnitTypes.RenderMillimeters) 176 dom, root = self.symbolToSld(symbol) 177 # print ("Ellipse marker unit mm: " + dom.toString()) 178 179 # Check the size has been rescaled 180 self.assertStaticSize(root, '25') 181 # Check also the stroke width 182 self.assertStrokeWidth(root, 2, 4) 183 self.assertStaticDisplacement(root, 18, 36) 184 185 def testEllipseMarkerUnitPixel(self): 186 symbol = self.createEllipseSymbolLayer() 187 symbol.setOffset(QPointF(5, 10)) 188 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 189 dom, root = self.symbolToSld(symbol) 190 # print ("Ellipse marker unit mm: " + dom.toString()) 191 192 # Check the size has been rescaled 193 self.assertStaticSize(root, '7') 194 # Check also the stroke width 195 self.assertStrokeWidth(root, 2, 1) 196 self.assertStaticDisplacement(root, 5, 10) 197 198 def testSimpleLineHairline(self): 199 symbol = QgsSimpleLineSymbolLayer(QColor("black"), 0) 200 dom, root = self.symbolToSld(symbol) 201 202 # print ("Simple line px: \n" + dom.toString()) 203 204 # Hairline is turned into 0.5px 205 self.assertStrokeWidth(root, 1, 0.5) 206 207 def testSimpleLineUnitDefault(self): 208 symbol = QgsSimpleLineSymbolLayer(QColor("black"), 1) 209 symbol.setCustomDashVector([10, 10]) 210 symbol.setUseCustomDashPattern(True) 211 symbol.setOffset(5) 212 dom, root = self.symbolToSld(symbol) 213 214 # print ("Simple line px: \n" + dom.toString()) 215 216 self.assertStrokeWidth(root, 1, 4) 217 self.assertDashPattern(root, 4, '36 36') 218 self.assertStaticPerpendicularOffset(root, '18') 219 220 def testSimpleLineUnitPixel(self): 221 symbol = QgsSimpleLineSymbolLayer(QColor("black"), 1) 222 symbol.setCustomDashVector([10, 10]) 223 symbol.setUseCustomDashPattern(True) 224 symbol.setOffset(5) 225 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 226 dom, root = self.symbolToSld(symbol) 227 228 # print ("Simple line px: \n" + dom.toString()) 229 230 self.assertStrokeWidth(root, 1, 1) 231 self.assertDashPattern(root, 4, '10 10') 232 self.assertStaticPerpendicularOffset(root, '5') 233 234 def testMarkLineUnitDefault(self): 235 symbol = QgsMarkerLineSymbolLayer() 236 symbol.setSubSymbol( 237 QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '3'})) 238 symbol.setInterval(5) 239 symbol.setOffset(5) 240 dom, root = self.symbolToSld(symbol) 241 242 # print ("Mark line mm: \n" + dom.toString()) 243 244 # size of the mark 245 self.assertStaticSize(root, '11') 246 # gap and offset 247 self.assertStaticGap(root, '18') 248 self.assertStaticPerpendicularOffset(root, '18') 249 250 def testMarkLineUnitPixels(self): 251 symbol = QgsMarkerLineSymbolLayer() 252 symbol.setSubSymbol( 253 QgsMarkerSymbol.createSimple({'color': '#ffffff', 'size': '3'})) 254 symbol.setInterval(5) 255 symbol.setOffset(5) 256 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 257 dom, root = self.symbolToSld(symbol) 258 259 # print ("Mark line px: \n" + dom.toString()) 260 261 # size of the mark 262 self.assertStaticSize(root, '3') 263 # gap and offset 264 self.assertStaticGap(root, '5') 265 self.assertStaticPerpendicularOffset(root, '5') 266 267 def testSimpleFillDefault(self): 268 symbol = QgsSimpleFillSymbolLayer( 269 QColor('red'), Qt.SolidPattern, QColor('green'), Qt.SolidLine, 5) 270 symbol.setOffset(QPointF(5, 10)) 271 272 dom, root = self.symbolToSld(symbol) 273 274 # print ("Simple fill mm: \n" + dom.toString()) 275 276 self.assertStrokeWidth(root, 2, 18) 277 self.assertStaticDisplacement(root, 18, 36) 278 279 def testSimpleFillPixels(self): 280 symbol = QgsSimpleFillSymbolLayer( 281 QColor('red'), Qt.SolidPattern, QColor('green'), Qt.SolidLine, 5) 282 symbol.setOffset(QPointF(5, 10)) 283 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 284 285 dom, root = self.symbolToSld(symbol) 286 # print ( "Simple fill px: \n" + dom.toString()) 287 288 self.assertStrokeWidth(root, 2, 5) 289 self.assertStaticDisplacement(root, 5, 10) 290 291 def testSvgFillDefault(self): 292 symbol = QgsSVGFillSymbolLayer('test/star.svg', 10, 45) 293 symbol.setSvgFillColor(QColor('blue')) 294 symbol.setSvgStrokeWidth(3) 295 symbol.setSvgStrokeColor(QColor('yellow')) 296 symbol.subSymbol().setWidth(10) 297 298 dom, root = self.symbolToSld(symbol) 299 # print ("Svg fill mm: \n" + dom.toString()) 300 301 self.assertExternalGraphic(root, 0, 302 'test/star.svg?fill=%230000ff&fill-opacity=1&outline=%23ffff00&outline-opacity=1&outline-width=11', 303 'image/svg+xml') 304 self.assertExternalGraphic(root, 1, 305 'test/star.svg', 'image/svg+xml') 306 self.assertWellKnownMark(root, 0, 'square', '#0000ff', '#ffff00', 11) 307 308 self.assertStaticRotation(root, '45') 309 self.assertStaticSize(root, '36') 310 # width of the polygon stroke 311 lineSymbolizer = root.elementsByTagName('se:LineSymbolizer').item(0).toElement() 312 self.assertStrokeWidth(lineSymbolizer, 1, 36) 313 314 def testSvgFillPixel(self): 315 symbol = QgsSVGFillSymbolLayer('test/star.svg', 10, 45) 316 symbol.setSvgFillColor(QColor('blue')) 317 symbol.setSvgStrokeWidth(3) 318 symbol.setSvgStrokeColor(QColor('black')) 319 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 320 symbol.subSymbol().setWidth(10) 321 322 dom, root = self.symbolToSld(symbol) 323 # print ("Svg fill px: \n" + dom.toString()) 324 325 self.assertExternalGraphic(root, 0, 326 'test/star.svg?fill=%230000ff&fill-opacity=1&outline=%23000000&outline-opacity=1&outline-width=3', 327 'image/svg+xml') 328 self.assertExternalGraphic(root, 1, 329 'test/star.svg', 'image/svg+xml') 330 self.assertWellKnownMark(root, 0, 'square', '#0000ff', '#000000', 3) 331 332 self.assertStaticRotation(root, '45') 333 self.assertStaticSize(root, '10') 334 # width of the polygon stroke 335 lineSymbolizer = root.elementsByTagName('se:LineSymbolizer').item(0).toElement() 336 self.assertStrokeWidth(lineSymbolizer, 1, 10) 337 338 def testLineFillDefault(self): 339 symbol = QgsLinePatternFillSymbolLayer() 340 symbol.setLineAngle(45) 341 symbol.setLineWidth(1) 342 symbol.setOffset(5) 343 344 dom, root = self.symbolToSld(symbol) 345 # print ("Line fill mm: \n" + dom.toString()) 346 347 self.assertStaticRotation(root, '45') 348 self.assertStrokeWidth(root, 1, 4) 349 self.assertStaticSize(root, '18') 350 self.assertStaticDisplacement(root, 15, 9) 351 352 def testLineFillPixels(self): 353 symbol = QgsLinePatternFillSymbolLayer() 354 symbol.setLineAngle(45) 355 symbol.setLineWidth(1) 356 symbol.setOffset(5) 357 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 358 359 dom, root = self.symbolToSld(symbol) 360 # print ("Line fill px: \n" + dom.toString()) 361 362 self.assertStaticRotation(root, '45') 363 self.assertStrokeWidth(root, 1, 1) 364 self.assertStaticSize(root, '5') 365 self.assertStaticDisplacement(root, 4.25, 2.63) 366 367 def testPointFillDefault(self): 368 symbol = QgsPointPatternFillSymbolLayer() 369 dom, root = self.symbolToSld(symbol) 370 # print ("Point fill mm: \n" + dom.toString()) 371 372 self.assertStaticSize(root, '7') 373 374 def testPointFillpixels(self): 375 symbol = QgsPointPatternFillSymbolLayer() 376 symbol.setOutputUnit(QgsUnitTypes.RenderPixels) 377 dom, root = self.symbolToSld(symbol) 378 # print ("Point fill px: \n" + dom.toString()) 379 380 self.assertStaticSize(root, '2') 381 382 def testSingleSymbolNoScaleDependencies(self): 383 layer = QgsVectorLayer("Point", "addfeat", "memory") 384 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "singleSymbol")) 385 layer.loadNamedStyle(mFilePath) 386 387 dom, root = self.layerToSld(layer) 388 # print("No dep on single symbol:" + dom.toString()) 389 390 self.assertScaleDenominator(root, None, None) 391 392 def testSingleSymbolScaleDependencies(self): 393 layer = QgsVectorLayer("Point", "addfeat", "memory") 394 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "singleSymbol")) 395 layer.loadNamedStyle(mFilePath) 396 layer.setMaximumScale(1000) 397 layer.setMinimumScale(500000) 398 layer.setScaleBasedVisibility(True) 399 400 dom, root = self.layerToSld(layer) 401 # print("Scale dep on single symbol:" + dom.toString()) 402 403 self.assertScaleDenominator(root, '1000', '500000') 404 405 def testCategorizedNoScaleDependencies(self): 406 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 407 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "categorized")) 408 layer.loadNamedStyle(mFilePath) 409 410 dom, root = self.layerToSld(layer) 411 # print("Categorized no scale deps:" + dom.toString()) 412 413 ruleCount = root.elementsByTagName('se:Rule').size() 414 for i in range(0, ruleCount): 415 self.assertScaleDenominator(root, None, None, i) 416 417 def testCategorizedWithScaleDependencies(self): 418 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 419 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "categorized")) 420 layer.loadNamedStyle(mFilePath) 421 layer.setMaximumScale(1000) 422 layer.setMinimumScale(500000) 423 layer.setScaleBasedVisibility(True) 424 425 dom, root = self.layerToSld(layer) 426 # print("Categorized with scale deps:" + dom.toString()) 427 428 ruleCount = root.elementsByTagName('se:Rule').size() 429 for i in range(0, ruleCount): 430 self.assertScaleDenominator(root, '1000', '500000', i) 431 432 def testGraduatedNoScaleDependencies(self): 433 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 434 435 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "graduated")) 436 status = layer.loadNamedStyle(mFilePath) # NOQA 437 438 dom, root = self.layerToSld(layer) 439 # print("Graduated no scale deps:" + dom.toString()) 440 441 ruleCount = root.elementsByTagName('se:Rule').size() 442 for i in range(0, ruleCount): 443 self.assertScaleDenominator(root, None, None, i) 444 445 # def testRuleBasedNoRootScaleDependencies(self): 446 # layer = QgsVectorLayer("Polygon", "addfeat", "memory") 447 # 448 # mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "ruleBased")) 449 # status = layer.loadNamedStyle(mFilePath) # NOQA 450 # 451 # dom, root = self.layerToSld(layer) 452 # print(("Rule based, no root scale deps:" + dom.toString())) 453 # 454 # ruleCount = root.elementsByTagName('se:Rule').size() # NOQA 455 # self.assertScaleDenominator(root, '1000', '40000000', 0) 456 # self.assertScaleDenominator(root, None, None, 1) 457 458 def testRuleBasedNoRootScaleDependencies(self): 459 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 460 461 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "ruleBased")) 462 status = layer.loadNamedStyle(mFilePath) # NOQA 463 layer.setMaximumScale(5000) 464 layer.setMinimumScale(50000000) 465 layer.setScaleBasedVisibility(True) 466 467 dom, root = self.layerToSld(layer) 468 # print("Rule based, with root scale deps:" + dom.toString()) 469 470 ruleCount = root.elementsByTagName('se:Rule').size() # NOQA 471 self.assertScaleDenominator(root, '5000', '40000000', 0) 472 self.assertScaleDenominator(root, '5000', '50000000', 1) 473 474 def testCategorizedFunctionConflict(self): 475 layer = QgsVectorLayer("Point", "addfeat", "memory") 476 477 mFilePath = QDir.toNativeSeparators( 478 '%s/symbol_layer/%s.qml' % (unitTestDataPath(), "categorizedFunctionConflict")) 479 status = layer.loadNamedStyle(mFilePath) # NOQA 480 481 dom, root = self.layerToSld(layer) 482 # print("Rule based, with root scale deps:" + dom.toString()) 483 484 ruleCount = root.elementsByTagName('se:Rule').size() # NOQA 485 self.assertEqual(7, ruleCount) 486 self.assertRuleRangeFilter(root, 0, 'Area', '0', True, '500', True) 487 self.assertRuleRangeFilter(root, 1, 'Area', '500', False, '1000', True) 488 self.assertRuleRangeFilter(root, 2, 'Area', '1000', False, '5000', True) 489 self.assertRuleRangeFilter(root, 3, 'Area', '5000', False, '10000', True) 490 self.assertRuleRangeFilter(root, 4, 'Area', '10000', False, '50000', True) 491 self.assertRuleRangeFilter(root, 5, 'Area', '50000', False, '100000', True) 492 self.assertRuleRangeFilter(root, 6, 'Area', '100000', False, '200000', True) 493 494 def assertRuleRangeFilter(self, root, index, attributeName, min, includeMin, max, includeMax): 495 rule = root.elementsByTagName('se:Rule').item(index).toElement() 496 filter = rule.elementsByTagName("Filter").item(0).firstChild() 497 self.assertEqual("ogc:And", filter.nodeName()) 498 499 gt = filter.firstChild() 500 expectedGtName = "ogc:PropertyIsGreaterThanOrEqualTo" if includeMin else "ogc:PropertyIsGreaterThan" 501 self.assertEqual(expectedGtName, gt.nodeName()) 502 gtProperty = gt.firstChild() 503 self.assertEqual("ogc:PropertyName", gtProperty.nodeName()) 504 self.assertEqual(attributeName, gtProperty.toElement().text()) 505 gtValue = gt.childNodes().item(1) 506 self.assertEqual(min, gtValue.toElement().text()) 507 508 lt = filter.childNodes().item(1) 509 expectedLtName = "ogc:PropertyIsLessThanOrEqualTo" if includeMax else "ogc:PropertyIsLessThan" 510 self.assertEqual(expectedLtName, lt.nodeName()) 511 ltProperty = lt.firstChild() 512 self.assertEqual("ogc:PropertyName", ltProperty.nodeName()) 513 self.assertEqual(attributeName, ltProperty.toElement().text()) 514 ltValue = lt.childNodes().item(1) 515 self.assertEqual(max, ltValue.toElement().text()) 516 517 def testSimpleLabeling(self): 518 layer = QgsVectorLayer("Point", "addfeat", "memory") 519 self.loadStyleWithCustomProperties(layer, "simpleLabel") 520 # Pick a local default font 521 fontFamily = QFont().family() 522 settings = layer.labeling().settings() 523 format = settings.format() 524 font = format.font() 525 font.setFamily(fontFamily) 526 font.setBold(False) 527 font.setItalic(False) 528 format.setFont(font) 529 settings.setFormat(format) 530 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 531 532 dom, root = self.layerToSld(layer) 533 # print("Simple label text symbolizer" + dom.toString()) 534 535 ts = self.getTextSymbolizer(root, 1, 0) 536 self.assertPropertyName(ts, 'se:Label', 'NAME') 537 font = self.assertElement(ts, 'se:Font', 0) 538 self.assertEqual(fontFamily, self.assertSvgParameter(font, 'font-family').text()) 539 self.assertEqual('11', self.assertSvgParameter(font, 'font-size').text()) 540 541 fill = self.assertElement(ts, 'se:Fill', 0) 542 self.assertEqual('#000000', self.assertSvgParameter(fill, "fill").text()) 543 self.assertIsNone(self.assertSvgParameter(fill, "fill-opacity", True)) 544 545 def testLabelingUomMillimeter(self): 546 layer = QgsVectorLayer("Point", "addfeat", "memory") 547 self.loadStyleWithCustomProperties(layer, "simpleLabel") 548 self.updateLayerLabelingUnit(layer, QgsUnitTypes.RenderMillimeters) 549 550 dom, root = self.layerToSld(layer) 551 # print("Label sized in mm " + dom.toString()) 552 553 ts = self.getTextSymbolizer(root, 1, 0) 554 font = self.assertElement(ts, 'se:Font', 0) 555 self.assertEqual('32', self.assertSvgParameter(font, 'font-size').text()) 556 557 def testLabelingUomPixels(self): 558 layer = QgsVectorLayer("Point", "addfeat", "memory") 559 self.loadStyleWithCustomProperties(layer, "simpleLabel") 560 self.updateLayerLabelingUnit(layer, QgsUnitTypes.RenderPixels) 561 562 dom, root = self.layerToSld(layer) 563 # print("Label sized in pixels " + dom.toString()) 564 565 ts = self.getTextSymbolizer(root, 1, 0) 566 font = self.assertElement(ts, 'se:Font', 0) 567 self.assertEqual('9', self.assertSvgParameter(font, 'font-size').text()) 568 569 def testLabelingUomInches(self): 570 layer = QgsVectorLayer("Point", "addfeat", "memory") 571 self.loadStyleWithCustomProperties(layer, "simpleLabel") 572 self.updateLayerLabelingUnit(layer, QgsUnitTypes.RenderInches) 573 574 dom, root = self.layerToSld(layer) 575 # print("Label sized in inches " + dom.toString()) 576 577 ts = self.getTextSymbolizer(root, 1, 0) 578 font = self.assertElement(ts, 'se:Font', 0) 579 self.assertEqual('816', self.assertSvgParameter(font, 'font-size').text()) 580 581 def testTextStyle(self): 582 layer = QgsVectorLayer("Point", "addfeat", "memory") 583 self.loadStyleWithCustomProperties(layer, "simpleLabel") 584 585 # testing regular 586 self.updateLayerLabelingFontStyle(layer, False, False) 587 dom, root = self.layerToSld(layer) 588 # print("Simple label italic text" + dom.toString()) 589 ts = self.getTextSymbolizer(root, 1, 0) 590 font = self.assertElement(ts, 'se:Font', 0) 591 self.assertIsNone(self.assertSvgParameter(font, 'font-weight', True)) 592 self.assertIsNone(self.assertSvgParameter(font, 'font-style', True)) 593 594 # testing bold 595 self.updateLayerLabelingFontStyle(layer, True, False) 596 dom, root = self.layerToSld(layer) 597 # print("Simple label bold text" + dom.toString()) 598 ts = self.getTextSymbolizer(root, 1, 0) 599 font = self.assertElement(ts, 'se:Font', 0) 600 self.assertEqual('bold', self.assertSvgParameter(font, 'font-weight').text()) 601 self.assertIsNone(self.assertSvgParameter(font, 'font-style', True)) 602 603 # testing italic 604 self.updateLayerLabelingFontStyle(layer, False, True) 605 dom, root = self.layerToSld(layer) 606 # print("Simple label italic text" + dom.toString()) 607 ts = self.getTextSymbolizer(root, 1, 0) 608 font = self.assertElement(ts, 'se:Font', 0) 609 self.assertEqual('italic', self.assertSvgParameter(font, 'font-style').text()) 610 self.assertIsNone(self.assertSvgParameter(font, 'font-weight', True)) 611 612 # testing bold italic 613 self.updateLayerLabelingFontStyle(layer, True, True) 614 dom, root = self.layerToSld(layer) 615 # print("Simple label bold and italic text" + dom.toString()) 616 ts = self.getTextSymbolizer(root, 1, 0) 617 font = self.assertElement(ts, 'se:Font', 0) 618 self.assertEqual('italic', self.assertSvgParameter(font, 'font-style').text()) 619 self.assertEqual('bold', self.assertSvgParameter(font, 'font-weight').text()) 620 621 # testing underline and strikethrough vendor options 622 self.updateLayerLabelingFontStyle(layer, False, False, True, True) 623 dom, root = self.layerToSld(layer) 624 # print("Simple label underline and strikethrough text" + dom.toString()) 625 ts = self.getTextSymbolizer(root, 1, 0) 626 font = self.assertElement(ts, 'se:Font', 0) 627 self.assertEqual('true', self.assertVendorOption(ts, 'underlineText').text()) 628 self.assertEqual('true', self.assertVendorOption(ts, 'strikethroughText').text()) 629 630 def testTextMixedCase(self): 631 self.assertCapitalizationFunction(QFont.MixedCase, None) 632 633 def testTextUppercase(self): 634 self.assertCapitalizationFunction(QFont.AllUppercase, "strToUpperCase") 635 636 def testTextLowercase(self): 637 self.assertCapitalizationFunction(QFont.AllLowercase, "strToLowerCase") 638 639 def testTextCapitalcase(self): 640 self.assertCapitalizationFunction(QFont.Capitalize, "strCapitalize") 641 642 def assertCapitalizationFunction(self, capitalization, expectedFunction): 643 layer = QgsVectorLayer("Point", "addfeat", "memory") 644 self.loadStyleWithCustomProperties(layer, "simpleLabel") 645 646 settings = layer.labeling().settings() 647 format = settings.format() 648 font = format.font() 649 font.setCapitalization(capitalization) 650 format.setFont(font) 651 settings.setFormat(format) 652 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 653 654 dom, root = self.layerToSld(layer) 655 # print("Simple text with capitalization " + str(QFont.AllUppercase) + ": " + dom.toString()) 656 ts = self.getTextSymbolizer(root, 1, 0) 657 label = self.assertElement(ts, "se:Label", 0) 658 if expectedFunction is None: 659 property = self.assertElement(label, "ogc:PropertyName", 0) 660 self.assertEqual("NAME", property.text()) 661 else: 662 function = self.assertElement(label, "ogc:Function", 0) 663 self.assertEqual(expectedFunction, function.attribute("name")) 664 property = self.assertElement(function, "ogc:PropertyName", 0) 665 self.assertEqual("NAME", property.text()) 666 667 def testLabelingTransparency(self): 668 layer = QgsVectorLayer("Point", "addfeat", "memory") 669 self.loadStyleWithCustomProperties(layer, "simpleLabel") 670 settings = layer.labeling().settings() 671 format = settings.format() 672 format.setOpacity(0.5) 673 settings.setFormat(format) 674 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 675 676 dom, root = self.layerToSld(layer) 677 # print("Label with transparency " + dom.toString()) 678 679 ts = self.getTextSymbolizer(root, 1, 0) 680 fill = self.assertElement(ts, 'se:Fill', 0) 681 self.assertEqual('#000000', self.assertSvgParameter(fill, "fill").text()) 682 self.assertEqual('0.5', self.assertSvgParameter(fill, "fill-opacity").text()) 683 684 def testLabelingBuffer(self): 685 layer = QgsVectorLayer("Point", "addfeat", "memory") 686 self.loadStyleWithCustomProperties(layer, "simpleLabel") 687 buffer = QgsTextBufferSettings() 688 buffer.setEnabled(True) 689 buffer.setSize(10) 690 buffer.setSizeUnit(QgsUnitTypes.RenderPixels) 691 buffer.setColor(QColor("Black")) 692 self.setLabelBufferSettings(layer, buffer) 693 694 dom, root = self.layerToSld(layer) 695 # print("Label with buffer 10 px " + dom.toString()) 696 697 ts = self.getTextSymbolizer(root, 1, 0) 698 halo = self.assertElement(ts, 'se:Halo', 0) 699 # not full width, just radius here 700 self.assertEqual('5', self.assertElement(ts, 'se:Radius', 0).text()) 701 haloFill = self.assertElement(halo, 'se:Fill', 0) 702 self.assertEqual('#000000', self.assertSvgParameter(haloFill, "fill").text()) 703 704 def testLabelingBufferPointTranslucent(self): 705 layer = QgsVectorLayer("Point", "addfeat", "memory") 706 self.loadStyleWithCustomProperties(layer, "simpleLabel") 707 buffer = QgsTextBufferSettings() 708 buffer.setEnabled(True) 709 buffer.setSize(10) 710 buffer.setSizeUnit(QgsUnitTypes.RenderPoints) 711 buffer.setColor(QColor("Red")) 712 buffer.setOpacity(0.5) 713 self.setLabelBufferSettings(layer, buffer) 714 715 dom, root = self.layerToSld(layer) 716 # print("Label with buffer 10 points, red 50% transparent " + dom.toString()) 717 718 ts = self.getTextSymbolizer(root, 1, 0) 719 halo = self.assertElement(ts, 'se:Halo', 0) 720 # not full width, just radius here 721 self.assertEqual('6.5', self.assertElement(ts, 'se:Radius', 0).text()) 722 haloFill = self.assertElement(halo, 'se:Fill', 0) 723 self.assertEqual('#ff0000', self.assertSvgParameter(haloFill, "fill").text()) 724 self.assertEqual('0.5', self.assertSvgParameter(haloFill, "fill-opacity").text()) 725 726 def testLabelingLowPriority(self): 727 self.assertLabelingPriority(0, 0, '0') 728 729 def testLabelingDefaultPriority(self): 730 self.assertLabelingPriority(0, 5, None) 731 732 def testLabelingHighPriority(self): 733 self.assertLabelingPriority(0, 10, '1000') 734 735 def testLabelingZIndexLowPriority(self): 736 self.assertLabelingPriority(1, 0, '1001') 737 738 def testLabelingZIndexDefaultPriority(self): 739 self.assertLabelingPriority(1, 5, "1500") 740 741 def testLabelingZIndexHighPriority(self): 742 self.assertLabelingPriority(1, 10, '2000') 743 744 def assertLabelingPriority(self, zIndex, priority, expectedSldPriority): 745 layer = QgsVectorLayer("Point", "addfeat", "memory") 746 self.loadStyleWithCustomProperties(layer, "simpleLabel") 747 settings = layer.labeling().settings() 748 settings.zIndex = zIndex 749 settings.priority = priority 750 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 751 752 dom, root = self.layerToSld(layer) 753 # print("Label with zIndex at " + str(zIndex) + " and priority at " + str(priority) + ": " + dom.toString()) 754 755 ts = self.getTextSymbolizer(root, 1, 0) 756 priorityElement = self.assertElement(ts, "se:Priority", 0, True) 757 if expectedSldPriority is None: 758 self.assertIsNone(priorityElement) 759 else: 760 self.assertEqual(expectedSldPriority, priorityElement.text()) 761 762 def testLabelingPlacementOverPointOffsetRotation(self): 763 layer = QgsVectorLayer("Point", "addfeat", "memory") 764 self.loadStyleWithCustomProperties(layer, "simpleLabel") 765 settings = layer.labeling().settings() 766 settings.placement = QgsPalLayerSettings.OverPoint 767 settings.xOffset = 5 768 settings.yOffset = 10 769 settings.offsetUnits = QgsUnitTypes.RenderMillimeters 770 settings.quadOffset = QgsPalLayerSettings.QuadrantOver 771 settings.angleOffset = 30 772 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 773 774 dom, root = self.layerToSld(layer) 775 # print("Label with 'over point' placement " + dom.toString()) 776 777 ts = self.getTextSymbolizer(root, 1, 0) 778 pointPlacement = self.assertPointPlacement(ts) 779 self.assertStaticDisplacement(pointPlacement, 18, 36) 780 self.assertStaticAnchorPoint(pointPlacement, 0.5, 0.5) 781 782 def testPointPlacementAboveLeft(self): 783 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantAboveLeft, "AboveLeft", 1, 0) 784 785 def testPointPlacementAbove(self): 786 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantAbove, "Above", 0.5, 0) 787 788 def testPointPlacementAboveRight(self): 789 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantAboveRight, "AboveRight", 0, 0) 790 791 def testPointPlacementLeft(self): 792 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantLeft, "Left", 1, 0.5) 793 794 def testPointPlacementRight(self): 795 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantRight, "Right", 0, 0.5) 796 797 def testPointPlacementBelowLeft(self): 798 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantBelowLeft, "BelowLeft", 1, 1) 799 800 def testPointPlacementBelow(self): 801 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantBelow, "Below", 0.5, 1) 802 803 def testPointPlacementAboveRight(self): 804 self.assertLabelQuadrant(QgsPalLayerSettings.QuadrantBelowRight, "BelowRight", 0, 1) 805 806 def testPointPlacementCartoraphic(self): 807 self.assertPointPlacementDistance(QgsPalLayerSettings.OrderedPositionsAroundPoint) 808 809 def testPointPlacementCartoraphic(self): 810 self.assertPointPlacementDistance(QgsPalLayerSettings.AroundPoint) 811 812 def testLineParallelPlacement(self): 813 layer = QgsVectorLayer("LineString", "addfeat", "memory") 814 self.loadStyleWithCustomProperties(layer, "lineLabel") 815 816 dom, root = self.layerToSld(layer) 817 # print("Label with parallel line placement " + dom.toString()) 818 linePlacement = self.assertLinePlacement(root) 819 generalize = self.assertElement(linePlacement, 'se:GeneralizeLine', 0) 820 self.assertEqual("true", generalize.text()) 821 822 def testLineParallelPlacementOffsetRepeat(self): 823 layer = QgsVectorLayer("LineString", "addfeat", "memory") 824 self.loadStyleWithCustomProperties(layer, "lineLabel") 825 self.updateLinePlacementProperties(layer, QgsPalLayerSettings.Line, 2, 50) 826 827 dom, root = self.layerToSld(layer) 828 # print("Label with parallel line placement, perp. offset and repeat " + dom.toString()) 829 ts = self.getTextSymbolizer(root, 1, 0) 830 linePlacement = self.assertLinePlacement(ts) 831 generalize = self.assertElement(linePlacement, 'se:GeneralizeLine', 0) 832 self.assertEqual("true", generalize.text()) 833 offset = self.assertElement(linePlacement, 'se:PerpendicularOffset', 0) 834 self.assertEqual("7", offset.text()) 835 repeat = self.assertElement(linePlacement, 'se:Repeat', 0) 836 self.assertEqual("true", repeat.text()) 837 gap = self.assertElement(linePlacement, 'se:Gap', 0) 838 self.assertEqual("179", gap.text()) 839 self.assertEqual("179", self.assertVendorOption(ts, "repeat").text()) 840 841 def testLineCurvePlacementOffsetRepeat(self): 842 layer = QgsVectorLayer("LineString", "addfeat", "memory") 843 self.loadStyleWithCustomProperties(layer, "lineLabel") 844 self.updateLinePlacementProperties(layer, QgsPalLayerSettings.Curved, 2, 50, 30, 40) 845 846 dom, root = self.layerToSld(layer) 847 # print("Label with curved line placement " + dom.toString()) 848 849 ts = self.getTextSymbolizer(root, 1, 0) 850 linePlacement = self.assertLinePlacement(ts) 851 generalize = self.assertElement(linePlacement, 'se:GeneralizeLine', 0) 852 self.assertEqual("true", generalize.text()) 853 offset = self.assertElement(linePlacement, 'se:PerpendicularOffset', 0) 854 self.assertEqual("7", offset.text()) 855 repeat = self.assertElement(linePlacement, 'se:Repeat', 0) 856 self.assertEqual("true", repeat.text()) 857 gap = self.assertElement(linePlacement, 'se:Gap', 0) 858 self.assertEqual("179", gap.text()) 859 self.assertEqual("179", self.assertVendorOption(ts, "repeat").text()) 860 self.assertEqual("true", self.assertVendorOption(ts, "followLine").text()) 861 self.assertEqual("30", self.assertVendorOption(ts, "maxAngleDelta").text()) 862 863 def testLineCurveMergeLines(self): 864 layer = QgsVectorLayer("LineString", "addfeat", "memory") 865 self.loadStyleWithCustomProperties(layer, "lineLabel") 866 settings = layer.labeling().settings() 867 settings.placement = QgsPalLayerSettings.Curved 868 settings.mergeLines = True 869 settings.labelPerPart = True 870 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 871 872 dom, root = self.layerToSld(layer) 873 # print("Label with curved line and line grouping " + dom.toString()) 874 875 ts = self.getTextSymbolizer(root, 1, 0) 876 self.assertEqual("yes", self.assertVendorOption(ts, "group").text()) 877 self.assertEqual("true", self.assertVendorOption(ts, "labelAllGroup").text()) 878 879 def testLabelingPolygonFree(self): 880 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 881 self.loadStyleWithCustomProperties(layer, "polygonLabel") 882 settings = layer.labeling().settings() 883 settings.placement = QgsPalLayerSettings.Free 884 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 885 886 dom, root = self.layerToSld(layer) 887 # print("Polygon label with 'Free' placement " + dom.toString()) 888 889 ts = self.getTextSymbolizer(root, 1, 0) 890 pointPlacement = self.assertPointPlacement(ts) 891 self.assertIsNone(self.assertElement(ts, "se:Displacement", 0, True)) 892 self.assertStaticAnchorPoint(pointPlacement, 0.5, 0.5) 893 894 def testLabelingPolygonPerimeterCurved(self): 895 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 896 self.loadStyleWithCustomProperties(layer, "polygonLabel") 897 self.updateLinePlacementProperties(layer, QgsPalLayerSettings.PerimeterCurved, 2, 50, 30, -40) 898 899 dom, root = self.layerToSld(layer) 900 # print("Polygon Label with curved perimeter line placement " + dom.toString()) 901 902 ts = self.getTextSymbolizer(root, 1, 0) 903 linePlacement = self.assertLinePlacement(ts) 904 generalize = self.assertElement(linePlacement, 'se:GeneralizeLine', 0) 905 self.assertEqual("true", generalize.text()) 906 offset = self.assertElement(linePlacement, 'se:PerpendicularOffset', 0) 907 self.assertEqual("7", offset.text()) 908 repeat = self.assertElement(linePlacement, 'se:Repeat', 0) 909 self.assertEqual("true", repeat.text()) 910 gap = self.assertElement(linePlacement, 'se:Gap', 0) 911 self.assertEqual("179", gap.text()) 912 self.assertEqual("179", self.assertVendorOption(ts, "repeat").text()) 913 self.assertEqual("true", self.assertVendorOption(ts, "followLine").text()) 914 self.assertEqual("30", self.assertVendorOption(ts, "maxAngleDelta").text()) 915 916 def testLabelScaleDependencies(self): 917 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 918 self.loadStyleWithCustomProperties(layer, "polygonLabel") 919 settings = layer.labeling().settings() 920 settings.scaleVisibility = True 921 # Careful: min scale -> large scale denomin 922 settings.minimumScale = 10000000 923 settings.maximumScale = 1000000 924 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 925 926 dom, root = self.layerToSld(layer) 927 # print("Labeling with scale dependencies " + dom.toString()) 928 self.assertScaleDenominator(root, "1000000", "10000000", 1) 929 930 def testLabelShowAll(self): 931 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 932 self.loadStyleWithCustomProperties(layer, "polygonLabel") 933 settings = layer.labeling().settings() 934 settings.displayAll = True 935 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 936 937 dom, root = self.layerToSld(layer) 938 # print("Labeling, showing all labels " + dom.toString()) 939 940 ts = self.getTextSymbolizer(root, 1, 0) 941 self.assertVendorOption(ts, "conflictResolution", "false") 942 943 def testLabelUpsideDown(self): 944 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 945 self.loadStyleWithCustomProperties(layer, "polygonLabel") 946 settings = layer.labeling().settings() 947 settings.upsidedownLabels = QgsPalLayerSettings.ShowAll 948 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 949 950 dom, root = self.layerToSld(layer) 951 # print("Labeling, showing upside down labels on lines " + dom.toString()) 952 953 ts = self.getTextSymbolizer(root, 1, 0) 954 self.assertVendorOption(ts, "forceLeftToRight", "false") 955 956 def testLabelBackgroundSquareResize(self): 957 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeSquare, 'square', 958 QgsTextBackgroundSettings.SizeBuffer, 'proportional') 959 960 def testLabelBackgroundRectangleResize(self): 961 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeRectangle, 'square', 962 QgsTextBackgroundSettings.SizeBuffer, 'stretch') 963 964 def testLabelBackgroundCircleResize(self): 965 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeCircle, 'circle', 966 QgsTextBackgroundSettings.SizeBuffer, 'proportional') 967 968 def testLabelBackgroundEllipseResize(self): 969 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeEllipse, 'circle', 970 QgsTextBackgroundSettings.SizeBuffer, 'stretch') 971 972 def testLabelBackgroundSquareAbsolute(self): 973 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeSquare, 'square', 974 QgsTextBackgroundSettings.SizeFixed, None) 975 976 def testLabelBackgroundRectangleAbsolute(self): 977 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeRectangle, 'square', 978 QgsTextBackgroundSettings.SizeFixed, None) 979 980 def testLabelBackgroundCircleAbsolute(self): 981 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeCircle, 'circle', 982 QgsTextBackgroundSettings.SizeFixed, None) 983 984 def testLabelBackgroundEllipseAbsolute(self): 985 self.assertLabelBackground(QgsTextBackgroundSettings.ShapeEllipse, 'circle', 986 QgsTextBackgroundSettings.SizeFixed, None) 987 988 def assertLabelBackground(self, backgroundType, expectedMarkName, sizeType, expectedResize): 989 layer = QgsVectorLayer("Polygon", "addfeat", "memory") 990 self.loadStyleWithCustomProperties(layer, "polygonLabel") 991 settings = layer.labeling().settings() 992 background = QgsTextBackgroundSettings() 993 background.setEnabled(True) 994 background.setType(backgroundType) 995 background.setFillColor(QColor('yellow')) 996 background.setStrokeColor(QColor('black')) 997 background.setStrokeWidth(2) 998 background.setSize(QSizeF(10, 10)) 999 background.setSizeType(sizeType) 1000 format = settings.format() 1001 format.setBackground(background) 1002 settings.setFormat(format) 1003 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1004 1005 dom, root = self.layerToSld(layer) 1006 # print("Labeling, with background type " + str(backgroundType) + " and size type " + str(sizeType) + ": " + dom.toString()) 1007 1008 ts = self.getTextSymbolizer(root, 1, 0) 1009 graphic = self.assertElement(ts, "se:Graphic", 0) 1010 self.assertEqual("36", self.assertElement(graphic, 'se:Size', 0).text()) 1011 self.assertWellKnownMark(graphic, 0, expectedMarkName, '#ffff00', '#000000', 7) 1012 if expectedResize is None: 1013 self.assertIsNone(expectedResize, self.assertVendorOption(ts, 'graphic-resize', True)) 1014 else: 1015 self.assertEqual(expectedResize, self.assertVendorOption(ts, 'graphic-resize').text()) 1016 if sizeType == 0: 1017 # check extra padding for proportional ellipse 1018 if backgroundType == QgsTextBackgroundSettings.ShapeEllipse: 1019 self.assertEqual("42.5 49", self.assertVendorOption(ts, 'graphic-margin').text()) 1020 else: 1021 self.assertEqual("36 36", self.assertVendorOption(ts, 'graphic-margin').text()) 1022 else: 1023 self.assertIsNone(self.assertVendorOption(ts, 'graphic-margin', True)) 1024 1025 def testRuleBasedLabels(self): 1026 layer = QgsVectorLayer("Point", "addfeat", "memory") 1027 self.loadStyleWithCustomProperties(layer, "ruleLabel") 1028 1029 dom, root = self.layerToSld(layer) 1030 # print("Rule based labeling: " + dom.toString()) 1031 1032 # three rules, one with the point symbol, one with the first rule based label, 1033 # one with the second rule based label 1034 rule1 = self.getRule(root, 0) 1035 self.assertElement(rule1, 'se:PointSymbolizer', 0) 1036 1037 rule2 = self.getRule(root, 1) 1038 self.assertScaleDenominator(root, '100000', '10000000', 1) 1039 tsRule2 = self.assertElement(rule2, 'se:TextSymbolizer', 0) 1040 gt = rule2.elementsByTagName("Filter").item(0).firstChild() 1041 self.assertEqual("ogc:PropertyIsGreaterThan", gt.nodeName()) 1042 gtProperty = gt.toElement().firstChild() 1043 self.assertEqual("ogc:PropertyName", gtProperty.nodeName()) 1044 self.assertEqual("POP_MAX", gtProperty.toElement().text()) 1045 gtValue = gt.childNodes().item(1) 1046 self.assertEqual("1000000", gtValue.toElement().text()) 1047 1048 rule3 = self.getRule(root, 2) 1049 tsRule3 = self.assertElement(rule3, 'se:TextSymbolizer', 0) 1050 lt = rule3.elementsByTagName("Filter").item(0).firstChild() 1051 self.assertEqual("ogc:PropertyIsLessThan", lt.nodeName()) 1052 ltProperty = lt.toElement().firstChild() 1053 self.assertEqual("ogc:PropertyName", ltProperty.nodeName()) 1054 self.assertEqual("POP_MAX", ltProperty.toElement().text()) 1055 ltValue = gt.childNodes().item(1) 1056 self.assertEqual("1000000", gtValue.toElement().text()) 1057 1058 # check that adding a rule without settings does not segfault 1059 xml1 = dom.toString() 1060 layer.labeling().rootRule().appendChild(QgsRuleBasedLabeling.Rule(None)) 1061 dom, root = self.layerToSld(layer) 1062 xml2 = dom.toString() 1063 self.assertEqual(xml1, xml2) 1064 1065 def updateLinePlacementProperties(self, layer, linePlacement, distance, repeat, maxAngleInternal=25, 1066 maxAngleExternal=-25): 1067 settings = layer.labeling().settings() 1068 settings.placement = linePlacement 1069 settings.dist = distance 1070 settings.repeatDistance = repeat 1071 settings.maxCurvedCharAngleIn = maxAngleInternal 1072 settings.maxCurvedCharAngleOut = maxAngleExternal 1073 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1074 1075 def assertPointPlacementDistance(self, placement): 1076 layer = QgsVectorLayer("Point", "addfeat", "memory") 1077 self.loadStyleWithCustomProperties(layer, "simpleLabel") 1078 1079 settings = layer.labeling().settings() 1080 settings.placement = placement 1081 settings.xOffset = 0 1082 settings.yOffset = 0 1083 settings.dist = 2 1084 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1085 1086 dom, root = self.layerToSld(layer) 1087 # print("Label with around point placement " + dom.toString()) 1088 ts = self.getTextSymbolizer(root, 1, 0) 1089 pointPlacement = self.assertPointPlacement(ts) 1090 self.assertStaticAnchorPoint(pointPlacement, 0, 0.5) 1091 self.assertStaticDisplacement(pointPlacement, 4.95, 4.95) 1092 1093 def assertLabelQuadrant(self, quadrant, label, ax, ay): 1094 layer = QgsVectorLayer("Point", "addfeat", "memory") 1095 self.loadStyleWithCustomProperties(layer, "simpleLabel") 1096 1097 settings = layer.labeling().settings() 1098 settings.placement = QgsPalLayerSettings.OverPoint 1099 settings.xOffset = 0 1100 settings.yOffset = 0 1101 settings.quadOffset = quadrant 1102 settings.angleOffset = 0 1103 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1104 1105 dom, root = self.layerToSld(layer) 1106 # print("Label with " + label + " placement " + dom.toString()) 1107 self.assertStaticAnchorPoint(root, ax, ay) 1108 1109 def setLabelBufferSettings(self, layer, buffer): 1110 settings = layer.labeling().settings() 1111 format = settings.format() 1112 format.setBuffer(buffer) 1113 settings.setFormat(format) 1114 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1115 1116 def updateLayerLabelingFontStyle(self, layer, bold, italic, underline=False, strikeout=False): 1117 settings = layer.labeling().settings() 1118 format = settings.format() 1119 font = format.font() 1120 font.setBold(bold) 1121 font.setItalic(italic) 1122 font.setUnderline(underline) 1123 font.setStrikeOut(strikeout) 1124 format.setFont(font) 1125 settings.setFormat(format) 1126 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1127 1128 def updateLayerLabelingUnit(self, layer, unit): 1129 settings = layer.labeling().settings() 1130 format = settings.format() 1131 format.setSizeUnit(unit) 1132 settings.setFormat(format) 1133 layer.setLabeling(QgsVectorLayerSimpleLabeling(settings)) 1134 1135 def loadStyleWithCustomProperties(self, layer, qmlFileName): 1136 # load the style, only vector symbology 1137 path = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), qmlFileName)) 1138 1139 # labeling is in custom properties, they need to be loaded separately 1140 status = layer.loadNamedStyle(path) 1141 doc = QDomDocument() 1142 file = QFile(path) 1143 file.open(QIODevice.ReadOnly) 1144 doc.setContent(file, True) 1145 file.close() 1146 flag = layer.readCustomProperties(doc.documentElement()) 1147 1148 def assertPointPlacement(self, textSymbolizer): 1149 labelPlacement = self.assertElement(textSymbolizer, 'se:LabelPlacement', 0) 1150 self.assertIsNone(self.assertElement(labelPlacement, 'se:LinePlacement', 0, True)) 1151 pointPlacement = self.assertElement(labelPlacement, 'se:PointPlacement', 0) 1152 return pointPlacement 1153 1154 def assertLinePlacement(self, textSymbolizer): 1155 labelPlacement = self.assertElement(textSymbolizer, 'se:LabelPlacement', 0) 1156 self.assertIsNone(self.assertElement(labelPlacement, 'se:PointPlacement', 0, True)) 1157 linePlacement = self.assertElement(labelPlacement, 'se:LinePlacement', 0) 1158 return linePlacement 1159 1160 def assertElement(self, container, elementName, index, allowMissing=False): 1161 list = container.elementsByTagName(elementName) 1162 if list.size() <= index: 1163 if allowMissing: 1164 return None 1165 else: 1166 self.fail('Expected to find at least ' + str( 1167 index + 1) + ' ' + elementName + ' in ' + container.nodeName() + ' but found ' + str(list.size())) 1168 1169 node = list.item(index) 1170 self.assertTrue(node.isElement(), 'Found node but it''s not an element') 1171 return node.toElement() 1172 1173 def getRule(self, root, ruleIndex): 1174 rule = self.assertElement(root, 'se:Rule', ruleIndex) 1175 return rule 1176 1177 def getTextSymbolizer(self, root, ruleIndex, textSymbolizerIndex): 1178 rule = self.assertElement(root, 'se:Rule', ruleIndex) 1179 textSymbolizer = self.assertElement(rule, 'se:TextSymbolizer', textSymbolizerIndex) 1180 return textSymbolizer 1181 1182 def assertPropertyName(self, root, containerProperty, expectedAttributeName): 1183 container = root.elementsByTagName(containerProperty).item(0).toElement() 1184 property = container.elementsByTagName("ogc:PropertyName").item(0).toElement() 1185 self.assertEqual(expectedAttributeName, property.text()) 1186 1187 def assertSvgParameter(self, container, expectedName, allowMissing=False): 1188 list = container.elementsByTagName("se:SvgParameter") 1189 for i in range(0, list.size()): 1190 item = list.item(i) 1191 if item.isElement and item.isElement() and item.toElement().attribute('name') == expectedName: 1192 return item.toElement() 1193 if allowMissing: 1194 return None 1195 else: 1196 self.fail('Could not find a se:SvgParameter named ' + expectedName + ' in ' + container.nodeName()) 1197 1198 def assertVendorOption(self, container, expectedName, allowMissing=False): 1199 list = container.elementsByTagName("se:VendorOption") 1200 for i in range(0, list.size()): 1201 item = list.item(i) 1202 if item.isElement and item.isElement() and item.toElement().attribute('name') == expectedName: 1203 return item.toElement() 1204 if allowMissing: 1205 return None 1206 else: 1207 self.fail('Could not find a se:VendorOption named ' + expectedName + ' in ' + container.nodeName()) 1208 1209 def testRuleBaseEmptyFilter(self): 1210 layer = QgsVectorLayer("Point", "addfeat", "memory") 1211 1212 mFilePath = QDir.toNativeSeparators('%s/symbol_layer/%s.qml' % (unitTestDataPath(), "categorizedEmptyValue")) 1213 status = layer.loadNamedStyle(mFilePath) # NOQA 1214 1215 dom, root = self.layerToSld(layer) 1216 # print("Rule based, with last rule checking against empty value:" + dom.toString()) 1217 1218 # get the third rule 1219 rule = root.elementsByTagName('se:Rule').item(2).toElement() 1220 filter = rule.elementsByTagName('Filter').item(0).toElement() 1221 filter = filter.firstChild().toElement() 1222 self.assertEqual("ogc:Or", filter.nodeName()) 1223 self.assertEqual(1, filter.elementsByTagName('ogc:PropertyIsEqualTo').size()) 1224 self.assertEqual(1, filter.elementsByTagName('ogc:PropertyIsNull').size()) 1225 1226 def assertScaleDenominator(self, root, expectedMinScale, expectedMaxScale, index=0): 1227 rule = root.elementsByTagName('se:Rule').item(index).toElement() 1228 1229 if expectedMinScale: 1230 minScale = rule.elementsByTagName('se:MinScaleDenominator').item(0) 1231 self.assertEqual(expectedMinScale, minScale.firstChild().nodeValue()) 1232 else: 1233 self.assertEqual(0, root.elementsByTagName('se:MinScaleDenominator').size()) 1234 1235 if expectedMaxScale: 1236 maxScale = rule.elementsByTagName('se:MaxScaleDenominator').item(0) 1237 self.assertEqual(expectedMaxScale, maxScale.firstChild().nodeValue()) 1238 else: 1239 self.assertEqual(0, root.elementsByTagName('se:MaxScaleDenominator').size()) 1240 1241 def assertDashPattern(self, root, svgParameterIdx, expectedPattern): 1242 strokeWidth = root.elementsByTagName( 1243 'se:SvgParameter').item(svgParameterIdx) 1244 svgParameterName = strokeWidth.attributes().namedItem('name') 1245 self.assertEqual("stroke-dasharray", svgParameterName.nodeValue()) 1246 self.assertEqual( 1247 expectedPattern, strokeWidth.firstChild().nodeValue()) 1248 1249 def assertStaticGap(self, root, expectedValue): 1250 # Check the rotation element is a literal, not a 1251 rotation = root.elementsByTagName('se:Gap').item(0) 1252 literal = rotation.firstChild() 1253 self.assertEqual("ogc:Literal", literal.nodeName()) 1254 self.assertEqual(expectedValue, literal.firstChild().nodeValue()) 1255 1256 def assertStaticSize(self, root, expectedValue): 1257 size = root.elementsByTagName('se:Size').item(0) 1258 self.assertEqual(expectedValue, size.firstChild().nodeValue()) 1259 1260 def assertExternalGraphic(self, root, index, expectedLink, expectedFormat): 1261 graphic = root.elementsByTagName('se:ExternalGraphic').item(index) 1262 onlineResource = graphic.firstChildElement('se:OnlineResource') 1263 self.assertEqual(expectedLink, onlineResource.attribute('xlink:href')) 1264 format = graphic.firstChildElement('se:Format') 1265 self.assertEqual(expectedFormat, format.firstChild().nodeValue()) 1266 1267 def assertStaticPerpendicularOffset(self, root, expectedValue): 1268 offset = root.elementsByTagName('se:PerpendicularOffset').item(0) 1269 self.assertEqual(expectedValue, offset.firstChild().nodeValue()) 1270 1271 def assertWellKnownMark(self, root, index, expectedName, expectedFill, expectedStroke, expectedStrokeWidth): 1272 mark = root.elementsByTagName('se:Mark').item(index) 1273 wkn = mark.firstChildElement('se:WellKnownName') 1274 self.assertEqual(expectedName, wkn.text()) 1275 1276 fill = mark.firstChildElement('se:Fill') 1277 if expectedFill is None: 1278 self.assertTrue(fill.isNull()) 1279 else: 1280 parameter = fill.firstChildElement('se:SvgParameter') 1281 self.assertEqual('fill', parameter.attribute('name')) 1282 self.assertEqual(expectedFill, parameter.text()) 1283 1284 stroke = mark.firstChildElement('se:Stroke') 1285 if expectedStroke is None: 1286 self.assertTrue(stroke.isNull()) 1287 else: 1288 parameter = stroke.firstChildElement('se:SvgParameter') 1289 self.assertEqual('stroke', parameter.attribute('name')) 1290 self.assertEqual(expectedStroke, parameter.text()) 1291 parameter = parameter.nextSiblingElement('se:SvgParameter') 1292 self.assertEqual('stroke-width', parameter.attribute('name')) 1293 self.assertEqual(str(expectedStrokeWidth), parameter.text()) 1294 1295 def assertStaticRotation(self, root, expectedValue, index=0): 1296 # Check the rotation element is a literal, not a 1297 rotation = root.elementsByTagName('se:Rotation').item(index) 1298 literal = rotation.firstChild() 1299 self.assertEqual("ogc:Literal", literal.nodeName()) 1300 self.assertEqual(expectedValue, literal.firstChild().nodeValue()) 1301 1302 def assertStaticDisplacement(self, root, expectedAnchorX, expectedAnchorY): 1303 displacement = root.elementsByTagName('se:Displacement').item(0) 1304 self.assertIsNotNone(displacement) 1305 dx = displacement.firstChild() 1306 self.assertIsNotNone(dx) 1307 self.assertEqual("se:DisplacementX", dx.nodeName()) 1308 self.assertSldNumber(expectedAnchorX, dx.firstChild().nodeValue()) 1309 dy = displacement.lastChild() 1310 self.assertIsNotNone(dy) 1311 self.assertEqual("se:DisplacementY", dy.nodeName()) 1312 self.assertSldNumber(expectedAnchorY, dy.firstChild().nodeValue()) 1313 1314 def assertStaticAnchorPoint(self, root, expectedDispX, expectedDispY): 1315 anchor = root.elementsByTagName('se:AnchorPoint').item(0) 1316 self.assertIsNotNone(anchor) 1317 ax = anchor.firstChild() 1318 self.assertIsNotNone(ax) 1319 self.assertEqual("se:AnchorPointX", ax.nodeName()) 1320 self.assertSldNumber(expectedDispX, ax.firstChild().nodeValue()) 1321 ay = anchor.lastChild() 1322 self.assertIsNotNone(ay) 1323 self.assertEqual("se:AnchorPointY", ay.nodeName()) 1324 self.assertSldNumber(expectedDispY, ay.firstChild().nodeValue()) 1325 1326 def assertSldNumber(self, expected, stringValue): 1327 value = float(stringValue) 1328 self.assertFloatEquals(expected, value, 0.01) 1329 1330 def assertFloatEquals(self, expected, actual, tol): 1331 self.assertLess(abs(expected - actual), tol, 'Expected %d but was %d' % (expected, actual)) 1332 1333 def assertStrokeWidth(self, root, svgParameterIdx, expectedWidth): 1334 strokeWidth = root.elementsByTagName( 1335 'se:SvgParameter').item(svgParameterIdx) 1336 svgParameterName = strokeWidth.attributes().namedItem('name') 1337 self.assertEqual("stroke-width", svgParameterName.nodeValue()) 1338 self.assertSldNumber( 1339 expectedWidth, strokeWidth.firstChild().nodeValue()) 1340 1341 def symbolToSld(self, symbolLayer): 1342 dom = QDomDocument() 1343 root = dom.createElement("FakeRoot") 1344 dom.appendChild(root) 1345 symbolLayer.toSld(dom, root, {}) 1346 return dom, root 1347 1348 def layerToSld(self, mapLayer): 1349 dom = QDomDocument() 1350 root = dom.createElement("FakeRoot") 1351 dom.appendChild(root) 1352 error = None 1353 mapLayer.writeSld(root, dom, error, {}) 1354 return dom, root 1355 1356 1357if __name__ == '__main__': 1358 unittest.main() 1359