Folosirea suportului de hartă

Widget-ul suportului de hartă este, probabil, cel mai important în QGIS, deoarece prezintă o hartă compusă din straturi suprapuse și permite atât interacțiunea cu harta cât și cu straturile. Suportul arată întotdeauna o parte a hărții definită de caseta de încadrare curentă. Interacțiunea se realizează prin utilizarea unor instrumente pentru hartă: există instrumente de panoramare, de mărire, de identificare a straturilor, de măsurare, de editare vectorială și altele. Similar altor programe de grafică, există întotdeauna un instrument activ, iar utilizatorul poate comuta între instrumentele disponibile.

Map canvas is implemented as QgsMapCanvas class in qgis.gui module. The implementation is based on the Qt Graphics View framework. This framework generally provides a surface and a view where custom graphics items are placed and user can interact with them. We will assume that you are familiar enough with Qt to understand the concepts of the graphics scene, view and items. If not, please make sure to read the overview of the framework.

Ori de câte ori harta a fost deplasată, mărită/micșorată (sau alte acțiuni care declanșează o recitire), harta este randată iarăși în interiorul granițelor curente. Straturile sunt transformate într-o imagine (folosind clasa QgsMapRenderer) iar acea imagine este afișată pe suport. Elementul grafic (în termeni ai cadrului de lucru Qt Graphics View) responsabil pentru a afișarea hărții este QgsMapCanvasMap. Această clasă controlează, de asemenea, recitirea hărții randate. În afară de acest element, care acționează ca fundal, pot exista mai multe elemente ale suportului hărții. Elementele tipice suportului de hartă sunt benzile elastice (utilizate pentru măsurare, editare vectorială etc) sau marcajele nodurilor. Elementele suportului sunt de obicei utilizate pentru a oferi un răspuns vizual pentru instrumentele hărții, de exemplu, atunci când se creează un nou poligon, instrumentul corespunzător creează o bandă elastică de forma actuală a poligonului. Toate elementele suportului de hartă reprezintă subclase ale QgsMapCanvasItem care adaugă mai multe funcționalități obiectelor de bază QGraphicsItem.

Pentru a rezuma, arhitectura suportului pentru hartă constă în trei concepte:

  • suportul de hartă — pentru vizualizarea hărții

  • elementele — elemente suplimentare care pot fi afișate în suportul hărții

  • instrumentele hărții — pentru interacțiunea cu suportul hărții

Încapsularea suportului de hartă

Map canvas is a widget like any other Qt widget, so using it is as simple as creating and showing it:

canvas = QgsMapCanvas()
canvas.show()

Acest cod va produce o fereastră de sine stătătoare cu suport pentru hartă. Ea poate fi, de asemenea, încorporată într-un widget sau într-o fereastră deja existente. Atunci când se utilizează fișiere .ui și Qt Designer, puneți un QWidget pe formă pe care, ulterior, o veți promova la o nouă clasă: setați QgsMapCanvas ca nume de clasă și stabiliți qgis.gui ca fișier antet. Utilitarul ``pyuic4 `` va avea grijă de ea. Acesta este un mod foarte convenabil de încapsulare a suportului. Cealaltă posibilitate este de a scrie manual codul pentru a construi suportul hărții și alte widget-uri (în calitate de copii ai ferestrei principale sau de dialog), apoi creați o așezare în pagină.

By default, map canvas has black background and does not use anti-aliasing. To set white background and enable anti-aliasing for smooth rendering:

canvas.setCanvasColor(Qt.white)
canvas.enableAntiAliasing(True)

(În cazul în care vă întrebați, Qt vine de la modulul PyQt4.QtCore iar Qt.white este una dintre instanțele QColor predefinite.)

Now it is time to add some map layers. We will first open a layer and add it to the map layer registry. Then we will set the canvas extent and set the list of layers for canvas:

layer = QgsVectorLayer(path, name, provider)
if not layer.isValid():
  raise IOError, "Failed to open the layer"

# add layer to the registry
QgsMapLayerRegistry.instance().addMapLayer(layer)

# set extent to the extent of our layer
canvas.setExtent(layer.extent())

# set the map canvas layer set
canvas.setLayerSet( [ QgsMapCanvasLayer(layer) ] )

După executarea acestor comenzi, suportul ar trebui să arate stratul pe care le-ați încărcat.

Folosirea instrumentelor în suportul de hartă

Următorul exemplu construiește o fereastră care conține un suport de hartă și instrumente de bază pentru panoramare și mărire hartă. Acțiunile sunt create pentru activarea fiecărui instrument: panoramarea se face cu QgsMapToolPan, mărirea/micșorarea cu o pereche de instanțe a QgsMapToolZoom. Acțiunile sunt setate ca selectabile și asignate ulterior instrumentelor pentru a permite gestionarea automată a stării selectabile a acțiunilor - atunci când un instrument al hărții este activat, acțiunea sa este marcată ca fiind selectată iar acțiunea instrumentului anterior este deselectată. Instrumentele sunt activate folosind metoda setMapTool().

from qgis.gui import *
from PyQt4.QtGui import QAction, QMainWindow
from PyQt4.QtCore import SIGNAL, Qt, QString

class MyWnd(QMainWindow):
  def __init__(self, layer):
    QMainWindow.__init__(self)

    self.canvas = QgsMapCanvas()
    self.canvas.setCanvasColor(Qt.white)

    self.canvas.setExtent(layer.extent())
    self.canvas.setLayerSet( [ QgsMapCanvasLayer(layer) ] )

    self.setCentralWidget(self.canvas)

    actionZoomIn = QAction(QString("Zoom in"), self)
    actionZoomOut = QAction(QString("Zoom out"), self)
    actionPan = QAction(QString("Pan"), self)

    actionZoomIn.setCheckable(True)
    actionZoomOut.setCheckable(True)
    actionPan.setCheckable(True)

    self.connect(actionZoomIn, SIGNAL("triggered()"), self.zoomIn)
    self.connect(actionZoomOut, SIGNAL("triggered()"), self.zoomOut)
    self.connect(actionPan, SIGNAL("triggered()"), self.pan)

    self.toolbar = self.addToolBar("Canvas actions")
    self.toolbar.addAction(actionZoomIn)
    self.toolbar.addAction(actionZoomOut)
    self.toolbar.addAction(actionPan)

    # create the map tools
    self.toolPan = QgsMapToolPan(self.canvas)
    self.toolPan.setAction(actionPan)
    self.toolZoomIn = QgsMapToolZoom(self.canvas, False) # false = in
    self.toolZoomIn.setAction(actionZoomIn)
    self.toolZoomOut = QgsMapToolZoom(self.canvas, True) # true = out
    self.toolZoomOut.setAction(actionZoomOut)

    self.pan()

  def zoomIn(self):
    self.canvas.setMapTool(self.toolZoomIn)

  def zoomOut(self):
    self.canvas.setMapTool(self.toolZoomOut)

  def pan(self):
    self.canvas.setMapTool(self.toolPan)

You can put the above code to a file, e.g. mywnd.py and try it out in Python console within QGIS. This code will put the currently selected layer into newly created canvas:

import mywnd
w = mywnd.MyWnd(qgis.utils.iface.activeLayer())
w.show()

Doar asigurați-vă că fișierul mywnd.py se află în calea de căutare pentru Python (sys.path). În cazul în care nu este, puteți pur și simplu să o adăugați: sys.path.insert(0, '/calea/mea') — altfel declarația de import nu va reuși, negăsind modulul.

Benzile elastice și marcajele nodurilor

Pentru a arăta unele date suplimentare în partea de sus a hărții, folosiți elemente ale suportului de hartă. Cu toate că este posibil să se creeze clase de elemente de suport personalizate (detaliate mai jos), există două clase de elemente confortabile QgsRubberBand pentru desenarea de polilinii sau poligoane, și QgsVertexMarker pentru puncte. Amândouă lucrează cu coordonatele hărții, astfel încât o formă este mutată/scalată în mod automat atunci când suportul este rotit sau mărit.

To show a polyline:

r = QgsRubberBand(canvas, False)  # False = not a polygon
points = [ QgsPoint(-1,-1), QgsPoint(0,1), QgsPoint(1,-1) ]
r.setToGeometry(QgsGeometry.fromPolyline(points), None)

To show a polygon:

r = QgsRubberBand(canvas, True)  # True = a polygon
points = [ [ QgsPoint(-1,-1), QgsPoint(0,1), QgsPoint(1,-1) ] ]
r.setToGeometry(QgsGeometry.fromPolygon(points), None)

Rețineți că punctele pentru poligon nu reprezintă o simplă listă: în fapt, aceasta este o listă de inele conținând inele liniare ale poligonului: primul inel reprezintă granița exterioară, în plus (opțional) inelele corespund găurilor din poligon.

Rubber bands allow some customization, namely to change their color and line width:

r.setColor(QColor(0,0,255))
r.setWidth(3)

The canvas items are bound to the canvas scene. To temporarily hide them (and show again, use the hide() and show() combo. To completely remove the item, you have to remove it from the scene of the canvas:

canvas.scene().removeItem(r)

(În C + + este posibilă ștergerea doar a elementului, însă în Python del r ar șterge doar referința iar obiectul va exista în continuare, acesta fiind deținut de suport)

Rubber band can be also used for drawing points, however QgsVertexMarker class is better suited for this (QgsRubberBand would only draw a rectangle around the desired point). How to use the vertex marker:

m = QgsVertexMarker(canvas)
m.setCenter(QgsPoint(0,0))

This will draw a red cross on position [0,0]. It is possible to customize the icon type, size, color and pen width:

m.setColor(QColor(0,255,0))
m.setIconSize(5)
m.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X
m.setPenWidth(3)

Pentru ascunderea temporară a markerilor vertex și pentru eliminarea lor de pe suport, același lucru este valabil și pentru benzile elastice.

Dezvoltarea instrumentelor personalizate pentru suportul de hartă

You can write your custom tools, to implement a custom behaviour to actions perfored by users on the canvas.

Instrumentele de hartă ar trebui să moștenească clasa QgsMapTool sau orice altă clasă derivată, și să fie selectate ca instrumente active pe suport, folosindu-se metoda setMapTool(), așa cum am văzut deja.

Here is an example of a map tool that allows to define a rectangular extent by clicking and draggin on the canvas. When the rectangle is defined, it prints its boundary coordinates in the console. It uses the rubber band elements described before to show the selected rectangle as it is being defined.

class RectangleMapTool(QgsMapToolEmitPoint):
  def __init__(self, canvas):
      self.canvas = canvas
      QgsMapToolEmitPoint.__init__(self, self.canvas)
      self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
      self.rubberBand.setColor(Qt.red)
      self.rubberBand.setWidth(1)
      self.reset()

  def reset(self):
      self.startPoint = self.endPoint = None
      self.isEmittingPoint = False
      self.rubberBand.reset(QGis.Polygon)

  def canvasPressEvent(self, e):
      self.startPoint = self.toMapCoordinates(e.pos())
      self.endPoint = self.startPoint
      self.isEmittingPoint = True
      self.showRect(self.startPoint, self.endPoint)

  def canvasReleaseEvent(self, e):
      self.isEmittingPoint = False
      r = self.rectangle()
      if r is not None:
        print "Rectangle:", r.xMin(), r.yMin(), r.xMax(), r.yMax()

  def canvasMoveEvent(self, e):
      if not self.isEmittingPoint:
        return

      self.endPoint = self.toMapCoordinates( e.pos() )
      self.showRect(self.startPoint, self.endPoint)

  def showRect(self, startPoint, endPoint):
      self.rubberBand.reset(QGis.Polygon)
      if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
        return

      point1 = QgsPoint(startPoint.x(), startPoint.y())
      point2 = QgsPoint(startPoint.x(), endPoint.y())
      point3 = QgsPoint(endPoint.x(), endPoint.y())
      point4 = QgsPoint(endPoint.x(), startPoint.y())

      self.rubberBand.addPoint( point1, False )
      self.rubberBand.addPoint( point2, False )
      self.rubberBand.addPoint( point3, False )
      self.rubberBand.addPoint( point4, True )    # true to update canvas
      self.rubberBand.show()

  def rectangle(self):
      if self.startPoint is None or self.endPoint is None:
        return None
      elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == \
        self.endPoint.y():
        return None

      return QgsRectangle(self.startPoint, self.endPoint)

  def deactivate(self):
      QgsMapTool.deactivate(self)
      self.emit(SIGNAL("deactivated()"))

Dezvoltarea elementelor personalizate pentru suportul de hartă

TODO: how to create a map canvas item