"""@package CreateLineMapTool This module holds all structures and methods required to perform "create line" operation on current OSM data. Snapping to existing points is supported when creating new line. Process generates some rubberBands and vertexMarkers so that user can watch the whole operation on the map in a nice way. There is also an interaction with plugin's "OSM Feature" dialog. Points to which snapping is performed are loaded to it dynamically. """ from PyQt4.QtCore import * from PyQt4.QtGui import * from qgis.core import * from qgis.gui import * class CreateLineMapTool(QgsMapTool): """This class holds all structures and methods required to perform "create line" operation on current OSM data. Snapping to existing points is supported when creating new line. Process generates some rubberBands and vertexMarkers so that user can watch the whole operation on the map in a nice way. There is also an interaction with plugin's "OSM Feature" dialog. Points to which snapping is performed are loaded to it dynamically. """ def __init__(self, plugin): """The constructor. Initializes the map tool, creates necessary snappers. @param plugin pointer to OSM Plugin instance """ QgsMapTool.__init__(self,plugin.canvas) self.canvas=plugin.canvas self.dockWidget=plugin.dockWidget self.dbm=plugin.dbm self.ur=plugin.undoredo # initialization self.snappingEnabled=True self.lastPointIsStable=True self.linePoints=[] self.snappedPoint=None self.snapFeat=None self.snapFeatType=None # creating rubberband which will be on new line self.lineRubBand=self.createLineRubberband() # creating rubberband for snapped objects self.snapVerMarker=self.createSnapVertexMarker() # creating snapper to this map tool self.snapper=self.createSnapper(self.canvas.mapRenderer()) def databaseChanged(self,dbKey): """This function is called automatically when current OSM database has changed. Function does re-initialization of maptool and create new snappers again (if necessary). @param dbKey key of database with new current OSM data """ # re-initialization self.snappingEnabled=True self.snapFeat=None self.snapFeatType=None self.snapVerMarker.setCenter(QgsPoint(-1000,-1000)) del self.snapVerMarker self.snapVerMarker=self.createSnapVertexMarker() self.lineRubBand.reset(False) self.lastPointIsStable=True self.linePoints=[] self.snappedPoint=None if dbKey: del self.snapper self.snapper=self.createSnapper(self.canvas.mapRenderer()) self.dockWidget.plugin.iface.mainWindow().statusBar().showMessage("") def createLineRubberband(self): """Function creates rubberband that is used for marking new line on the map. @return rubberband that marks new line """ # get qgis settings of line width and color for rubberband settings=QSettings() qgsLineWidth=settings.value("/qgis/digitizing/line_width",QVariant(10)).toInt() qgsLineRed=settings.value("/qgis/digitizing/line_color_red",QVariant(255)).toInt() qgsLineGreen=settings.value("/qgis/digitizing/line_color_green",QVariant(0)).toInt() qgsLineBlue=settings.value("/qgis/digitizing/line_color_blue",QVariant(0)).toInt() rband=QgsRubberBand(self.canvas,False) rband.setColor(QColor(qgsLineRed[0],qgsLineGreen[0],qgsLineBlue[0])) rband.setWidth(qgsLineWidth[0]) return rband def createSnapVertexMarker(self): """Function creates vertexMarker that is used for marking feature to which snapping was done. @return vertex marker - QgsVertexMarker object """ # get qgis settings settings=QSettings() qgsLineWidth=settings.value("/qgis/digitizing/line_width",QVariant(10)).toInt() qgsLineRed=settings.value("/qgis/digitizing/line_color_red",QVariant(255)).toInt() qgsLineGreen=settings.value("/qgis/digitizing/line_color_green",QVariant(0)).toInt() qgsLineBlue=settings.value("/qgis/digitizing/line_color_blue",QVariant(0)).toInt() verMarker=QgsVertexMarker(self.canvas) verMarker.setIconType(2) verMarker.setIconSize(13) verMarker.setColor(QColor(qgsLineRed[0],qgsLineGreen[0],qgsLineBlue[0])) verMarker.setPenWidth(qgsLineWidth[0]) verMarker.setCenter(QgsPoint(-1000,-1000)) return verMarker def createSnapper(self,canvasRenderer): """Function creates snapper that snaps within standard qgis tolerance. Snapping of this snapper is done to all segments and vertexes of all three layers of current OSM database. @param canvasRenderer renderer of current map canvas @return instance of vertex+segment QgsSnapper """ if not self.dbm.currentKey: # there is no current database -> no layer for snapping return QgsSnapper(self.canvas.mapRenderer()) snapper=QgsSnapper(self.canvas.mapRenderer()) snapLayers=[] # snap to osm layers from current database only sLayer=QgsSnapper.SnapLayer() sLayer.mLayer=self.dbm.pointLayers[self.dbm.currentKey] sLayer.mTolerance=QgsTolerance.vertexSearchRadius(sLayer.mLayer,self.canvas.mapRenderer()) sLayer.mSnapTo=QgsSnapper.SnapToVertex snapLayers.append(sLayer) sLayer=QgsSnapper.SnapLayer() sLayer.mLayer=self.dbm.lineLayers[self.dbm.currentKey] sLayer.mTolerance=QgsTolerance.vertexSearchRadius(sLayer.mLayer,self.canvas.mapRenderer()) sLayer.mSnapTo=QgsSnapper.SnapToVertex snapLayers.append(sLayer) sLayer=QgsSnapper.SnapLayer() sLayer.mLayer=self.dbm.polygonLayers[self.dbm.currentKey] sLayer.mTolerance=QgsTolerance.vertexSearchRadius(sLayer.mLayer,self.canvas.mapRenderer()) sLayer.mSnapTo=QgsSnapper.SnapToVertex snapLayers.append(sLayer) snapper.setSnapLayers(snapLayers) return snapper def deactivate(self): """Functions is called when create line map-tool is being deactivated. Function performs standard cleaning; re-initialization etc. """ self.lineRubBand.reset() self.snapVerMarker.setCenter(QgsPoint(-1000,-1000)) self.snappingEnabled=True self.lastPointIsStable=True self.linePoints=[] self.dockWidget.toolButtons.setExclusive(False) self.dockWidget.createLineButton.setChecked(False) self.dockWidget.toolButtons.setExclusive(True) self.dockWidget.activeEditButton=self.dockWidget.dummyButton def keyPressEvent(self, event): """This function is called after keyPressEvent(QKeyEvent *) signal is emmited when using this map tool. If Control key was pressed, function disables snapping til key is released again. @param event event that occured when key pressing """ if (event.key() == Qt.Key_Control): self.snappingEnabled = False self.snapVerMarker.setCenter(QgsPoint(-1000,-1000)) self.snappedPoint=None self.dockWidget.plugin.iface.mainWindow().statusBar().showMessage("Snapping OFF.") def keyReleaseEvent(self, event): """This function is called after keyReleaseEvent(QKeyEvent *) signal is emmited when using this map tool. If Control key was released, function enables snapping again. @param event event that occured when key releasing """ if (event.key() == Qt.Key_Control): self.snappingEnabled = True self.dockWidget.plugin.iface.mainWindow().statusBar().showMessage("Snapping ON. Hold Ctrl to disable it.") def canvasMoveEvent(self, event): """This function is called when mouse moving. @param event event that occured when mouse moving. """ self.mapPoint=self.dockWidget.canvasToOsmCoords(event.pos()) if len(self.linePoints)>0: if not self.lastPointIsStable: self.lineRubBand.removeLastPoint() self.lineRubBand.addPoint(QgsPoint(self.mapPoint.x(),self.mapPoint.y())) self.lastPointIsStable=False if not self.snappingEnabled: self.snapVerMarker.setCenter(self.mapPoint) return # snapping! first reset old snapping vertexMarker self.snapVerMarker.setCenter(QgsPoint(-1000,-1000)) # try snapping to the closest vertex/segment (retval,snappingResults)=self.snapper.snapPoint(event.pos(),[]) if len(snappingResults)==0: self.snapVerMarker.setCenter(self.mapPoint) self.snappedPoint=None self.snapFeat=None self.snapFeatType=None if self.dockWidget.feature: self.dockWidget.clear() return # process snapping result (get point, set vertex marker) self.snappedPoint=QgsPoint(snappingResults[0].snappedVertex) self.snapVerMarker.setCenter(self.snappedPoint) if len(self.linePoints)>0: self.lineRubBand.removeLastPoint() self.lineRubBand.addPoint(QgsPoint(self.snappedPoint.x(),self.snappedPoint.y())) # start identification feature=self.dbm.findFeature(self.snappedPoint) if feature: (self.snapFeat,self.snapFeatType)=feature if not self.dockWidget.feature or self.snapFeat.id()<>self.dockWidget.feature.id(): self.dockWidget.loadFeature(self.snapFeat,self.snapFeatType) def canvasReleaseEvent(self, event): """This function is called after mouse button releasing when using this map tool. If left button is released new vertex of line is created (pre-created). If right button is released the whole process of line creation is finished. @param event event that occured when button releasing """ # we are interested in left/right button clicking only if event.button() not in (Qt.LeftButton,Qt.RightButton): return if event.button()==Qt.LeftButton: # where we are exactly? actualMapPoint = self.dockWidget.canvasToOsmCoords(event.pos()) # what point will be the next line member? newLinePoint=actualMapPoint if self.snappedPoint: newLinePoint=self.snappedPoint # add new point into rubberband (and removing last one if neccessary) and into new line members list if not self.lastPointIsStable: self.lineRubBand.removeLastPoint() self.lastPointIsStable=True self.lineRubBand.addPoint(newLinePoint) self.linePoints.append((newLinePoint,self.snapFeat,self.snapFeatType)) # right button clicking signalizes the last line member! elif event.button()==Qt.RightButton: # line must have at least 2 member points (else it's point rather than line) if len(self.linePoints)<2: self.lineRubBand.reset() self.snapVerMarker.setCenter(QgsPoint(-1000,-1000)) self.lastPointIsStable=True self.linePoints=[] return self.ur.startAction("Create a line.") # call function of database manager that will create new line (line,affected)=self.dbm.createLine(self.linePoints) self.ur.stopAction(affected) self.dbm.recacheAffectedNow(affected) if line: self.dockWidget.loadFeature(line,"Line",2) # cleaning.. self.lineRubBand.reset() self.linePoints=[] # after line creation canvas must be refresh so that changes take effect on map self.canvas.refresh()