Jag såg nyligen en film av Lene Fischer på YouTube om etiketter i QGIS för att göra anpassade ”call outs” (länk).
I detta inlägg tittar jag närmare på hur man kan jobba med anpassade etiketter på olika sätt.
När man skapar etiketter på en karta så är det inte alltid dessa skrivs ut på den absolut bästa platsen. Ibland kan man även vilja rotera etiketter så att de bättre passar ett objekt. Placering och rotation är två saker som går att styra via datadefinierade åsidosättningar och dessutom så finns det ett verktyg som kan styra detta på ett väldigt integrerat sätt.
Här har jag skapat ett enkelt punkt, linje och polygonlager med en simpel etikett. Jag har även plockat fram verktygsfältet för etiketter (label toolbar) där man har direktåtkomst till etikettinställningar och möjlighet att direkt redigera texten i en etikett. Däremot så finns det ett antal ”gråa” knappar, som nu skall aktiveras.
För att göra det så behövs tre extra attribut i lagerna. Text_X, Text_Y och Text_Ri samt Text_Vis. Du kan döpa attributen som du vill men de skall representera positioner och vinklar så de skall vara av typen långa decimaltal (double). Text_Vis skall lagra en etta eller en nolla så ett vanligt heltal räcker.
I de datadefinerade inställningarna för etikettplacering finns inställningar för X, Y och rotation. Här anger man de nyss skapade attributfälten. På renderingsfliken finns även datadefinierad inställning för etikettvisning (show label).
När sedan lagren är i editeringsläge så är knapparna aktiverade. Det kan hända att alla etiketter är försvunna men det beror antagligen på att värdet i fältet Text_Vis är något annat än ”1”. Du kan antingen ändra innehållet i fälten till 1 eller använda verktyget för etikettsynlighet och klicka eller klick-dra på objektet. Detta skall ”tända” etiketten. Samma sak fast med shift nedtryckt kommer att släcka etiketterna.
Över till placering och rotation.
För att flytta etiketter så väljer man det verktyget, och sedan är det bara att ta tag i och dra iväg den dit man vill ha den. Detta fungerar för alla lager som har X och Y fält definierade i placeringsinställningarna.
Om man flyttar etiketter som redan har en rotation kopplad till formen på en linje eller polygon så bryter man denna koppling och rotationen nollställs när den flyttas. Därför så får man sedan använda verktyget för att rotera etiketterna.
Avslutningsvis, till det som Lene visade i sin film. Hur du lägger till ”call-outs” eller ”pekare” från den flyttade etiketten till källpunkten. Det jag visar här fungerar framför allt på punktgeometrier, men det går att anpassa till såväl linjer som ytor om man vill.
I stilinställningarna så skall man lägga till ett stillager av typen geometrigenerator. Typen skall vara ”linje” och sedan skriver man in ett uttryck som skapar en linje mellan de skapade attributen för textplacering och punktens geometri.
make_line( make_point( "Text_X", "Text_Y" ), $geometry )
Sedan kan man ju byta utseendet från en enkel linje till den nya linjestilen ”pil”…
Python
Ett Pythonskript för att göra ett markerat lager förberett för etiketter? Tja varför inte.
##Add Advanced Labeling Fields To Selected Layer=name
from PyQt4.QtCore import QVariant
from qgis.utils import iface
from qgis.core import QgsField, QgsPalLayerSettings
# Function source: https://gis.stackexchange.com/a/216110/55741
def setColumnVisibility( layer, columnName, visible ):
config = layer.attributeTableConfig()
form = layer.editFormConfig()
form.setWidgetType(layer.fieldNameIndex(columnName), "Hidden")
columns = config.columns()
for column in columns:
if column.name == columnName:
column.hidden = not visible
break
config.setColumns( columns )
layer.setAttributeTableConfig( config )
lager = iface.activeLayer()
if not lager.isEditable():
iface.actionToggleEditing().trigger()
lager.dataProvider().addAttributes([QgsField("Text_X", QVariant.Double, 'double', 10, 5),
QgsField("Text_Y", QVariant.Double, 'double', 10, 5), QgsField("Text_Ri", QVariant.Double, 'double', 3, 2), QgsField("Text_Vis", QVariant.Int, "", 3)])
lager.setDefaultValueExpression(lager.fieldNameIndex("Text_Vis"), '1')
lager.updateFields()
palager = QgsPalLayerSettings()
palager.readFromLayer(lager)
palager.enabled = True
palager.setDataDefinedProperty(QgsPalLayerSettings.PositionX, True, False, '', "Text_X")
palager.setDataDefinedProperty(QgsPalLayerSettings.PositionY, True, False, '', "Text_Y")
palager.setDataDefinedProperty(QgsPalLayerSettings.Rotation, True, False, '', "Text_Ri")
#palager.setDataDefinedProperty(QgsPalLayerSettings.Show, True, False, '', "Text_Vis")
setColumnVisibility( lager, 'Text_X', False)
setColumnVisibility( lager, 'Text_Y', False)
setColumnVisibility( lager, 'Text_Ri', False)
setColumnVisibility( lager, 'Text_Vis', False)
palager.writeToLayer(lager)
rader = lager.getFeatures()
for rad in rader:
rad['Text_Vis'] = 1
lager.updateFeature(rad)
lager.commitChanges()
lager.startEditing()
iface.mapCanvas().refresh()
Om man läser in ovanstående pythonskript som en fil i .qgis2/processing/scripts/ så kan man när man kör det genom ”Processing” så lägger det till fyra attribut i tabellen för markerat lager, döljer dessa med mera. Nu kan man flytta etiketter, rotera dessa med mera. Skriptet gör det även möjligt att visa och dölja enskilda etiketter med samma verktygsfält. Skriptet döljer de skapade fälten i såväl formulär som attributtabell, men de finns där. (Det strular lite med synligheten som jag inte vet vad det beror på. Därför har jag satt ett ”#” tecken framför den palager-rad som gör att det inte fungerar, men det är inte där felet finns. Felet ligger snarare i ”for rad in rader:” som inte verkar sätta värdet ”1” i kolumnen ”Text_Vis” som den skall.)
Vill du ”bara” lägga till kolumnerna och göra resten av jobbet själv så räcker följande:
##Add Advanced Labeling Fields=name
from PyQt4.QtCore import QVariant
from qgis.utils import iface
from qgis.core import QgsField
lager = iface.activeLayer()
if not lager.isEditable():
iface.actionToggleEditing().trigger()
lager.dataProvider().addAttributes([QgsField("Text_X", QVariant.Double, 'double', 10, 5),
QgsField("Text_Y", QVariant.Double, 'double', 10, 5), QgsField("Text_Ri", QVariant.Double, 'double', 3, 2), QgsField("Text_Vis", QVariant.Int, "", 3)])
lager.setDefaultValueExpression(lager.fieldNameIndex("Text_Vis"), '1')
lager.updateFields()