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.

Suportul hărții este implementat ca și clasa QgsMapCanvas, în modulul qgis.gui. Implementarea se bazează pe cadrul de lucru Qt Graphics View. Acest cadru, în general, pune la dispoziție o suprafață și o fereastră de vizualizare a acesteia, unde sunt plasate elementele grafice personalizate, utilizatorul putând interacționa cu ele. Vom presupune că v-ați familiarizat suficient cu Qt, pentru a înțelege conceptele de scenă grafică, vizualizare și elemente. Dacă nu, vă rugăm să citiți o prezentare generală a cadrului de lucru.

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ă

Canevasul hărții este un widget ca orice alt widget Qt, așa că utilizarea este la fel de simplă ca și crearea și afișarea lui

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ă.

În mod implicit, canevasul hărții are un fundal negru și nu utilizează anti-zimțare. Pentru a seta fundalul alb și pentru a permite anti-zimțare pentru o redare mai bună

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.)

Acum este timpul adăugării mai multor straturi de hartă. Vom deschide mai întâi un strat și-l vom adăuga la registrul straturilor. Apoi vom stabili extinderea canevasului și vom stabili lista straturilor

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 canevas ș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 folosindu-se 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)

Puteți pune codul de mai sus într-un fișier, de exemplu, mywnd.py și să-l încercați apoi în consola Python din QGIS. Acest cod va pune stratul curent selectat în noul canevas creat

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 canevasului. Cu toate că este posibil să se creeze clase de elemente de canevas 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 canevasul este rotit sau mărit.

Pentru a afișa o polilinie

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

Pentru a afișa un poligon

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.

Benzile elastice acceptă unele personalizări, și anume schimbarea culorii și a lățimii liniei

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

Elementele suportului sunt legate de suportul hărții. Pentru a le ascunde temporar (și a le arăta din nou, folosiți combinația hide() și show(). Pentru a elimina complet elementul, trebuie să-l eliminăm de pe scena canevasului

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)

Banda elastică poate fi de asemenea utilizată pentru desenarea de puncte, însă, clasa QgsVertexMarker este mai potrivită pentru aceasta (QgsRubberBand ar trasa doar un dreptunghi în jurul punctului dorit). Cum să utilizați simbolul nodului

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

În acest mod se va desena o cruciuliță roșie pe poziția [0,0]. Este posibilă personalizarea tipului pictogramei, dimensiunea, culoarea și lățimea instrumentului de desenare

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ă

Puteți crea propriile instrumente, pentru a implementa un comportament personalizat pentru acțiunile executate de către utilizatori pe canevas.

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.

Iată un exemplu de instrument pentru hartă, care permite definirea unei limite dreptunghiulare, făcând clic și trăgând cursorul mouse-ului pe canevas. După ce este definit dreptunghiul, coordonatele sale sunt afișate în consolă. Se utilizează elementele benzii elastice descrise mai înainte, pentru a arăta dreptunghiul selectat, așa cum a fost definit.

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.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum()

  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ă

DE EFECTUAT:
how to create a map canvas item
import sys
from qgis.core import QgsApplication
from qgis.gui import QgsMapCanvas

def init():
  a = QgsApplication(sys.argv, True)
  QgsApplication.setPrefixPath('/home/martin/qgis/inst', True)
  QgsApplication.initQgis()
  return a

def show_canvas(app):
  canvas = QgsMapCanvas()
  canvas.show()
  app.exec_()
app = init()
show_canvas(app)