mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
769 lines
29 KiB
Python
769 lines
29 KiB
Python
|
"""@package MoveMapTool
|
||
|
This module holds all structures and methods required to perform move operation on OSM data.
|
||
|
|
||
|
Snapping to existing features is supported when moving a feature.
|
||
|
Moving process generates some rubberBands and vertexMarkers so that user can watch
|
||
|
the whole action on the map in a nice way.
|
||
|
|
||
|
There is also an interaction with plugin's "OSM Feature" dialog. Affected features are dynamically
|
||
|
loaded to it; thanks to that user is not confused about what (s)he is moving.
|
||
|
"""
|
||
|
|
||
|
|
||
|
from PyQt4.QtCore import *
|
||
|
from PyQt4.QtGui import *
|
||
|
from qgis.core import *
|
||
|
from qgis.gui import *
|
||
|
from math import *
|
||
|
|
||
|
|
||
|
class MoveMapTool(QgsMapTool):
|
||
|
"""This class represents map tool for feature moving (see QgsMapTool from Quantum GIS API).
|
||
|
|
||
|
It enables to move any OSM feature. User is expected to left click, select and move... The second phase (selecting)
|
||
|
is necessary, because there can be more than one feature at the same place.
|
||
|
|
||
|
At the beginning of action left-click is used to choose the position on the map.
|
||
|
Repeatable right-clicking is then used to select required feature from specified position.
|
||
|
After that mouse moving moves selected feature.
|
||
|
The last action is left-clicking again (that confirms the moving operation) or right-clicking (canceling operation).
|
||
|
|
||
|
Snapping to existing features is supported when moving a feature. When moving a line/polygon, only three closest
|
||
|
vertexes to the mouse position can be snapped. If snapping is enabled for all vertexes, operation will be very slow
|
||
|
on features with many vertexes.
|
||
|
When moving a point (also vertex of line/polygon) snapping to both vertexes and segments is done.
|
||
|
When moving a line/polygon snapping to vertexes is supported only.
|
||
|
|
||
|
Moving process generates some rubberBands and vertexMarkers so that user can watch
|
||
|
the whole action on the map in a nice way.
|
||
|
|
||
|
There is also an interaction with plugin's "OSM Feature" dialog. Affected features are dynamically
|
||
|
loaded to it; thanks to that user is not confused about what (s)he is moving.
|
||
|
|
||
|
Map tool catches the signal of changing OSM database. It such case not-ended operation is canceled.
|
||
|
"""
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
# init info about feature that is being moved!
|
||
|
self.mapPointFrom=None
|
||
|
self.mapPointTo=None
|
||
|
self.movFeatType=None
|
||
|
self.movFeat=None
|
||
|
self.movIsPolygon=False
|
||
|
self.movIndexes=[]
|
||
|
|
||
|
self.snapDeltas=None
|
||
|
self.snapVertexIx=None
|
||
|
self.snapFeat=None
|
||
|
self.snapFeatType=None
|
||
|
|
||
|
# init variables that keeps system state
|
||
|
self.snappingEnabled=True
|
||
|
self.doubleclick=False
|
||
|
self.movingMode="INTRO" # -> "SELECTION" -> "MOVING"
|
||
|
self.moves=0
|
||
|
self.featuresFound=[]
|
||
|
self.ixFeature=0
|
||
|
|
||
|
# init objects to display on canvas
|
||
|
self.rubBands=[] # rubBands of neighbour (non-point) feats that are also affected by moving operation
|
||
|
self.isPolygonFlags=[] # isPolygon flags for self.rubBands[]
|
||
|
self.memberIndexes=[]
|
||
|
self.snapRubBand=self.__createSnapRubberband() # creating rubberband for snapped objects
|
||
|
self.verMarker=self.__createVertexMarker() # creating vertex marker for point moving
|
||
|
|
||
|
# creating Vertex & Segment snapper + creating Vertex (only!) snapper
|
||
|
self.snapperVS=self.__createVSSnapper(self.canvas.mapRenderer())
|
||
|
self.snapperV=self.__createVSnapper(self.canvas.mapRenderer())
|
||
|
|
||
|
# create dialog with feature selection
|
||
|
self.dlgSelect=QDialog(self.dockWidget)
|
||
|
self.dlgSelect.setWindowTitle("Feature identification")
|
||
|
butBox=QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel,Qt.Horizontal,self.dlgSelect)
|
||
|
|
||
|
self.lw=QListWidget(self.dlgSelect)
|
||
|
|
||
|
layout=QVBoxLayout(self.dlgSelect)
|
||
|
layout.addWidget(self.lw)
|
||
|
layout.addWidget(butBox)
|
||
|
self.dlgSelect.setLayout(layout)
|
||
|
|
||
|
# set dialog signals
|
||
|
QObject.connect(butBox,SIGNAL("accepted()"),self.__onSelectDlgOK)
|
||
|
QObject.connect(butBox,SIGNAL("rejected()"),self.__onSelectDlgCancel)
|
||
|
|
||
|
self.dockWidget.plugin.iface.mainWindow().statusBar().showMessage("")
|
||
|
|
||
|
|
||
|
def reinit(self):
|
||
|
"""Function re-initializes the map tool, prepare necessary rubberbands again.
|
||
|
|
||
|
After calling this function, move map tool is in the same state as after its creation.
|
||
|
"""
|
||
|
|
||
|
# reinit info about feature that is being moved!
|
||
|
self.mapPointFrom=None
|
||
|
self.mapPointTo=None
|
||
|
self.movFeatType=None
|
||
|
self.movFeat=None
|
||
|
self.movIsPolygon=False
|
||
|
self.movIndexes=[]
|
||
|
|
||
|
self.snapDeltas=None
|
||
|
self.snapVertexIx=None
|
||
|
self.snapFeat=None
|
||
|
self.snapFeatType=None
|
||
|
|
||
|
# reinit variables that keeps system state
|
||
|
self.snappingEnabled=True
|
||
|
self.doubleclick=False
|
||
|
self.movingMode="INTRO" # -> "SELECTION" -> "MOVING"
|
||
|
self.moves=0
|
||
|
self.featuresFound=[]
|
||
|
self.ixFeature=0
|
||
|
|
||
|
# reinit objects to display on canvas
|
||
|
for ix in range(0,len(self.rubBands)):
|
||
|
self.rubBands[ix].reset(self.isPolygonFlags[ix])
|
||
|
|
||
|
del self.rubBands
|
||
|
self.rubBands=[] # rubBands of neighbour feats that are also affected by moving operation
|
||
|
|
||
|
del self.isPolygonFlags
|
||
|
del self.memberIndexes
|
||
|
self.isPolygonFlags=[] # isPolygon flags for self.rubBands[]
|
||
|
self.memberIndexes=[]
|
||
|
|
||
|
self.snapRubBand.reset() # todo: ??? polygon ???
|
||
|
del self.snapRubBand
|
||
|
self.snapRubBand=self.__createSnapRubberband() # recreating rubberband for snapped objects
|
||
|
|
||
|
self.verMarker.setCenter(QgsPoint(-1000,-1000))
|
||
|
del self.verMarker
|
||
|
self.verMarker=self.__createVertexMarker() # recreating vertex marker for point moving
|
||
|
|
||
|
self.dockWidget.plugin.iface.mainWindow().statusBar().showMessage("")
|
||
|
|
||
|
|
||
|
def databaseChanged(self,dbKey):
|
||
|
"""This function is called automatically when current OSM database has changed.
|
||
|
|
||
|
Function calls re-initialization of maptool and create new snappers again.
|
||
|
|
||
|
@param dbKey key of database with new current OSM data
|
||
|
"""
|
||
|
|
||
|
# re-initialization
|
||
|
self.reinit()
|
||
|
|
||
|
# plus creation of new snappers
|
||
|
if dbKey:
|
||
|
del self.snapperVS
|
||
|
self.snapperVS=self.__createVSSnapper(self.canvas.mapRenderer())
|
||
|
del self.snapperV
|
||
|
self.snapperV=self.__createVSnapper(self.canvas.mapRenderer())
|
||
|
|
||
|
|
||
|
def __createFeatRubberband(self,isPolygon):
|
||
|
"""Function creates rubberband that is used for marking moved feature on the map.
|
||
|
|
||
|
@param isPolygon is hint for this function; it says if feature is of polygon type or not
|
||
|
@return rubberband for marking moved feature on the map
|
||
|
"""
|
||
|
|
||
|
# 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,isPolygon)
|
||
|
rband.setColor(QColor(qgsLineRed[0],qgsLineGreen[0],qgsLineBlue[0]))
|
||
|
rband.setWidth( qgsLineWidth[0] )
|
||
|
|
||
|
return rband
|
||
|
|
||
|
|
||
|
def __createSnapRubberband(self):
|
||
|
"""Function creates rubberband that is used for marking map features
|
||
|
to which snapping will be performed.
|
||
|
|
||
|
@return rubberband for marking map features
|
||
|
"""
|
||
|
|
||
|
# get qgis settings of line width and color for rubberband
|
||
|
settings=QSettings()
|
||
|
qgsLineWidth=settings.value( "/qgis/digitizing/line_width", QVariant(10) ).toInt()
|
||
|
|
||
|
rband=QgsRubberBand(self.canvas,False)
|
||
|
rband.setColor(QColor(255,0,0))
|
||
|
rband.setWidth(qgsLineWidth[0])
|
||
|
|
||
|
return rband
|
||
|
|
||
|
|
||
|
def __createVertexMarker(self):
|
||
|
"""Function creates vertexMarker that is used for marking moved feature (point) on the map.
|
||
|
|
||
|
@return vertex marker for marking moved feature on map
|
||
|
"""
|
||
|
|
||
|
# 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 __createVSSnapper(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(canvasRenderer)
|
||
|
|
||
|
snapper=QgsSnapper(canvasRenderer)
|
||
|
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,canvasRenderer)
|
||
|
sLayer.mSnapTo=QgsSnapper.SnapToVertex
|
||
|
snapLayers.append(sLayer)
|
||
|
|
||
|
sLayer=QgsSnapper.SnapLayer()
|
||
|
sLayer.mLayer=self.dbm.lineLayers[self.dbm.currentKey]
|
||
|
sLayer.mTolerance=QgsTolerance.vertexSearchRadius(sLayer.mLayer,canvasRenderer)
|
||
|
sLayer.mSnapTo=QgsSnapper.SnapToVertexAndSegment
|
||
|
snapLayers.append(sLayer)
|
||
|
|
||
|
sLayer=QgsSnapper.SnapLayer()
|
||
|
sLayer.mLayer=self.dbm.polygonLayers[self.dbm.currentKey]
|
||
|
sLayer.mTolerance=QgsTolerance.vertexSearchRadius(sLayer.mLayer,canvasRenderer)
|
||
|
sLayer.mSnapTo=QgsSnapper.SnapToVertexAndSegment
|
||
|
snapLayers.append(sLayer)
|
||
|
|
||
|
snapper.setSnapLayers(snapLayers)
|
||
|
return snapper
|
||
|
|
||
|
|
||
|
def __createVSnapper(self,canvasRenderer):
|
||
|
"""Function creates snapper that snaps within standard qgis tolerance.
|
||
|
|
||
|
Snapping of this snapper is done to all vertexes (but not segments)
|
||
|
of all three layers of current OSM database.
|
||
|
|
||
|
@param canvasRenderer renderer of current map canvas
|
||
|
@return instance of vertex QgsSnapper
|
||
|
"""
|
||
|
|
||
|
if not self.dbm.currentKey:
|
||
|
# there is no current database -> no layer for snapping
|
||
|
return QgsSnapper(canvasRenderer)
|
||
|
|
||
|
snapper=QgsSnapper(canvasRenderer)
|
||
|
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,canvasRenderer)
|
||
|
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,canvasRenderer)
|
||
|
sLayer.mSnapTo=QgsSnapper.SnapToVertex
|
||
|
snapLayers.append(sLayer)
|
||
|
|
||
|
snapper.setSnapLayers(snapLayers)
|
||
|
return snapper
|
||
|
|
||
|
|
||
|
def deactivate(self):
|
||
|
"""Functions is called when move-map-tool is being deactivated.
|
||
|
|
||
|
Function performs standard cleaning; re-initialization etc.
|
||
|
"""
|
||
|
|
||
|
self.reinit()
|
||
|
|
||
|
self.dockWidget.toolButtons.setExclusive(False)
|
||
|
self.dockWidget.moveButton.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 move map tool.
|
||
|
If Control key was pressed, function disables snapping til key is released.
|
||
|
|
||
|
@param event event that occured when key pressing
|
||
|
"""
|
||
|
|
||
|
if (event.key() == Qt.Key_Control):
|
||
|
self.snappingEnabled=False
|
||
|
self.snapRubBand.reset()
|
||
|
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 move 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 canvasDoubleClickEvent(self,event):
|
||
|
"""This function is called after doubleclick on map when using move map tool.
|
||
|
Function is interested into left-doubleclicking only.
|
||
|
|
||
|
It finds out all features that are currently at the place where doubleclick was done.
|
||
|
Then it shows simple dialog with the list of all these features. User can select the required one,
|
||
|
close dialog and continue moving.
|
||
|
|
||
|
@param event event that occured when double clicking
|
||
|
"""
|
||
|
|
||
|
if event.button()<>Qt.LeftButton:
|
||
|
return
|
||
|
|
||
|
self.dockWidget.clear()
|
||
|
#self.removeMarkers()
|
||
|
|
||
|
# find out map coordinates from mouse click
|
||
|
mapPoint=self.dockWidget.canvasToOsmCoords(event.pos())
|
||
|
|
||
|
# display modal dialog with features selection
|
||
|
self.featuresFound=self.dbm.findAllFeatures(mapPoint)
|
||
|
self.ixFeature=0
|
||
|
|
||
|
lwItems=[]
|
||
|
for f in self.featuresFound:
|
||
|
feat=f[0]
|
||
|
featType=f[1]
|
||
|
name=self.dbm.getTagValue(feat.id(),featType,"name")
|
||
|
lwItems.append(QString("[%1] ").arg(feat.id()).append(featType).append(QString(" ")).append(name))
|
||
|
|
||
|
self.lw.clear()
|
||
|
self.lw.addItems(lwItems)
|
||
|
|
||
|
self.dockWidget.plugin.iface.mainWindow().statusBar().showMessage("")
|
||
|
self.doubleclick=True
|
||
|
|
||
|
if self.dlgSelect:
|
||
|
# continue only if OK button was clicked
|
||
|
if self.dlgSelect.exec_()==0:
|
||
|
return
|
||
|
|
||
|
|
||
|
def canvasReleaseEvent(self, event):
|
||
|
"""This function is called after mouse button releasing on map when using move map tool.
|
||
|
|
||
|
Such button releasing can have a lot of meanings.
|
||
|
It depends on the current phase of moving. See documentation of the whole move map tool
|
||
|
to know how moving works and how many times user has to release the mouse button to perform the whole moving.
|
||
|
|
||
|
@param event event that occured when button releasing
|
||
|
"""
|
||
|
|
||
|
if self.doubleclick:
|
||
|
self.doubleclick=False
|
||
|
return
|
||
|
|
||
|
if self.movingMode=="INTRO":
|
||
|
|
||
|
# we are interested only in left button clicking
|
||
|
if event.button()<>Qt.LeftButton:
|
||
|
return
|
||
|
|
||
|
# find out map coordinates from mouse click
|
||
|
self.mapPointFrom = self.dockWidget.canvasToOsmCoords(event.pos())
|
||
|
|
||
|
# what to move?
|
||
|
self.featuresFound=self.dbm.findAllFeatures(self.mapPointFrom)
|
||
|
self.ixFeature=0
|
||
|
|
||
|
if len(self.featuresFound)>0:
|
||
|
self.movingMode="SELECTION"
|
||
|
|
||
|
(feat,featType)=self.featuresFound[self.ixFeature]
|
||
|
self.dockWidget.loadFeature(feat,featType,2)
|
||
|
|
||
|
elif self.dockWidget.feature:
|
||
|
self.dockWidget.clear()
|
||
|
self.mapPointFrom=None
|
||
|
return
|
||
|
|
||
|
elif self.movingMode=="SELECTION":
|
||
|
|
||
|
# we are interested only in left/right button clicking
|
||
|
if event.button() not in (Qt.LeftButton,Qt.RightButton):
|
||
|
return
|
||
|
|
||
|
if event.button()==Qt.RightButton:
|
||
|
if len(self.featuresFound)<1:
|
||
|
return
|
||
|
|
||
|
self.ixFeature=self.ixFeature+1
|
||
|
if self.ixFeature>=len(self.featuresFound):
|
||
|
self.ixFeature=0
|
||
|
|
||
|
(feat,featType)=self.featuresFound[self.ixFeature]
|
||
|
self.dockWidget.loadFeature(feat,featType,2)
|
||
|
|
||
|
else: # LeftButton
|
||
|
|
||
|
self.reinit()
|
||
|
if self.dockWidget.feature:
|
||
|
self.dockWidget.clear()
|
||
|
|
||
|
elif self.movingMode=="MOVING":
|
||
|
# finish feature moving
|
||
|
|
||
|
# we are interested only in left button clicking; other buttons just cancel moving operation
|
||
|
if event.button()<>Qt.LeftButton:
|
||
|
self.reinit()
|
||
|
return
|
||
|
|
||
|
whereIAm = self.dockWidget.canvasToOsmCoords(event.pos())
|
||
|
|
||
|
if self.snapDeltas:
|
||
|
self.mapPointTo=QgsPoint(whereIAm.x()+self.snapDeltas[0],whereIAm.y()+self.snapDeltas[1])
|
||
|
else:
|
||
|
# no snapping for this moving
|
||
|
self.snapVertexIx=-1
|
||
|
self.mapPointTo=whereIAm
|
||
|
|
||
|
deltaX=self.mapPointTo.x()-self.mapPointFrom.x()
|
||
|
deltaY=self.mapPointTo.y()-self.mapPointFrom.y()
|
||
|
|
||
|
self.__finishFeatureMoving(deltaX,deltaY)
|
||
|
|
||
|
|
||
|
def __tryIdentifyFeature(self,event):
|
||
|
"""Function just finds first feature at the place when event occured.
|
||
|
Feature is marked on map with rubberBand (or vertexMarker) and is loaded to OSM Feature widget.
|
||
|
|
||
|
@param event event that occured
|
||
|
"""
|
||
|
|
||
|
# find out map coordinates from mouse click
|
||
|
mapPoint=self.dockWidget.canvasToOsmCoords(event.pos())
|
||
|
feature=self.dbm.findFeature(mapPoint)
|
||
|
|
||
|
if feature:
|
||
|
(feat,featType)=feature
|
||
|
if not self.dockWidget.feature or feat.id()<>self.dockWidget.feature.id():
|
||
|
self.dockWidget.loadFeature(feat,featType,1)
|
||
|
|
||
|
elif self.dockWidget.feature:
|
||
|
self.dockWidget.clear()
|
||
|
|
||
|
|
||
|
def canvasMoveEvent(self,event):
|
||
|
"""This function is called after mouse moving.
|
||
|
|
||
|
Mouse moving is ignored before feature (to move) is selected.
|
||
|
|
||
|
@param event event that occured when mouse moving.
|
||
|
"""
|
||
|
|
||
|
# ignore one move from each two moves
|
||
|
if self.moves<>1:
|
||
|
self.moves=self.moves+1
|
||
|
return
|
||
|
self.moves=0
|
||
|
|
||
|
|
||
|
if self.movingMode=="INTRO":
|
||
|
self.__tryIdentifyFeature(event)
|
||
|
return
|
||
|
|
||
|
if self.movingMode=="SELECTION":
|
||
|
|
||
|
# remember what to move
|
||
|
self.movFeat=self.featuresFound[self.ixFeature][0]
|
||
|
self.movFeatType=self.featuresFound[self.ixFeature][1]
|
||
|
self.movIsPolygon=False
|
||
|
self.featuresFound=[]
|
||
|
self.ixFeature=0
|
||
|
|
||
|
# initializing rubberbands
|
||
|
if self.movFeatType in ('Polygon','Line'):
|
||
|
|
||
|
layer=self.dbm.lineLayers[self.dbm.currentKey]
|
||
|
if self.movFeatType=='Polygon':
|
||
|
self.movIsPolygon=True
|
||
|
layer=self.dbm.polygonLayers[self.dbm.currentKey]
|
||
|
|
||
|
# finding out three closest vertexes (for these snapping will be enabled)
|
||
|
self.movIndexes=[]
|
||
|
(p,ix,ixB,ixA,dis)=self.movFeat.geometry().closestVertex(self.mapPointFrom)
|
||
|
self.movIndexes.append(ix)
|
||
|
self.movIndexes.append(ixB)
|
||
|
self.movIndexes.append(ixA)
|
||
|
|
||
|
rubBand=self.__createFeatRubberband(self.movIsPolygon)
|
||
|
rubBand.setToGeometry(self.movFeat.geometry(),layer)
|
||
|
self.rubBands.append(rubBand)
|
||
|
self.isPolygonFlags.append(self.movIsPolygon)
|
||
|
|
||
|
elif self.movFeatType in ('Point'):
|
||
|
|
||
|
# find out parent features
|
||
|
(parentFeats,self.memberIndexes,self.isPolygonFlags)=self.dbm.getNodeParents(self.movFeat)
|
||
|
self.movParentVertices=[]
|
||
|
|
||
|
if len(parentFeats)==0:
|
||
|
self.verMarker.setCenter(self.movFeat.geometry().asPoint())
|
||
|
|
||
|
for ix in range(0,len(parentFeats)):
|
||
|
layer=None
|
||
|
if self.isPolygonFlags[ix]:
|
||
|
layer=self.dbm.polygonLayers[self.dbm.currentKey]
|
||
|
self.movParentVertices=self.movParentVertices+(parentFeats[ix].geometry().asPolygon())[0]
|
||
|
else:
|
||
|
layer=self.dbm.lineLayers[self.dbm.currentKey]
|
||
|
self.movParentVertices=self.movParentVertices+parentFeats[ix].geometry().asPolyline()
|
||
|
|
||
|
parentRubBand=self.__createFeatRubberband(self.isPolygonFlags[ix])
|
||
|
parentRubBand.setToGeometry(parentFeats[ix].geometry(),layer)
|
||
|
self.rubBands.append(parentRubBand)
|
||
|
|
||
|
if self.dockWidget.feature:
|
||
|
self.dockWidget.clear()
|
||
|
|
||
|
# change moving mode to the last one!
|
||
|
self.movingMode="MOVING"
|
||
|
|
||
|
# movingMode ~ "MOVING"
|
||
|
if self.movFeatType=='Point':
|
||
|
|
||
|
(deltaX,deltaY)=self.__getDeltaForPoint(event) # snapping is done in this function
|
||
|
targetPoint=QgsPoint(self.mapPointFrom.x()+deltaX,self.mapPointFrom.y()+deltaY)
|
||
|
|
||
|
if len(self.rubBands)==0:
|
||
|
point=self.movFeat.geometry().asPoint()
|
||
|
self.verMarker.setCenter(QgsPoint(point.x()+deltaX,point.y()+deltaY))
|
||
|
|
||
|
# move rubberbands
|
||
|
for ix in range(0,len(self.rubBands)):
|
||
|
for j in range(0,len(self.memberIndexes[ix])):
|
||
|
vertexIx=self.memberIndexes[ix][j]
|
||
|
lastVertexIx=self.rubBands[ix].numberOfVertices()-1
|
||
|
self.rubBands[ix].movePoint(vertexIx,targetPoint)
|
||
|
|
||
|
if self.isPolygonFlags[ix]:
|
||
|
if vertexIx==1:
|
||
|
self.rubBands[ix].movePoint(lastVertexIx,targetPoint)
|
||
|
elif vertexIx==lastVertexIx:
|
||
|
self.rubBands[ix].movePoint(vertexIx,targetPoint)
|
||
|
|
||
|
if vertexIx==1:
|
||
|
self.rubBands[ix].movePoint(0,targetPoint)
|
||
|
|
||
|
elif self.movFeatType in ('Line','Polygon'):
|
||
|
|
||
|
(deltaX,deltaY)=self.__getDeltaForLinePolygon(event)
|
||
|
# move feature rubberband
|
||
|
self.rubBands[0].setTranslationOffset(deltaX,deltaY)
|
||
|
|
||
|
|
||
|
def __getDeltaForPoint(self,event):
|
||
|
"""Function gets an event object, performs snapping from place where event occured and then counts distance
|
||
|
of found position from the place where the whole moving operation has started.
|
||
|
|
||
|
Special version for points.
|
||
|
|
||
|
@param event event that occured
|
||
|
"""
|
||
|
|
||
|
# find out where and how far (from the place where moving was started) we are now
|
||
|
mapPoint = self.dockWidget.canvasToOsmCoords(event.pos())
|
||
|
|
||
|
if not self.snappingEnabled:
|
||
|
self.snapDeltas=self.snapFeat=self.snapFeatType=None
|
||
|
# returns how far from the place where moving was started we are now
|
||
|
return (mapPoint.x()-self.mapPointFrom.x(),mapPoint.y()-self.mapPointFrom.y())
|
||
|
|
||
|
# perform snapping
|
||
|
self.movParentVertices.append(self.movFeat.geometry().asPoint())
|
||
|
(retval,snappingResults)=self.snapperVS.snapPoint(event.pos(),self.movParentVertices)
|
||
|
|
||
|
if len(snappingResults)==0:
|
||
|
self.snapDeltas=self.snapFeat=self.snapFeatType=None
|
||
|
# returns how far from the place where moving was started we are now
|
||
|
return (mapPoint.x()-self.mapPointFrom.x(),mapPoint.y()-self.mapPointFrom.y())
|
||
|
|
||
|
# we snapped successfully to something
|
||
|
snappedPoint=QgsPoint(snappingResults[0].snappedVertex)
|
||
|
|
||
|
self.snapDeltas=(snappedPoint.x()-mapPoint.x(),snappedPoint.y()-mapPoint.y())
|
||
|
# use snappingResults[0].layer in findFeature() ??? findFeatureInLayer() ???
|
||
|
(self.snapFeat,self.snapFeatType)=self.dbm.findFeature(snappedPoint)
|
||
|
|
||
|
# returns how far from the place where moving was started we are now
|
||
|
return (snappedPoint.x()-self.mapPointFrom.x(),snappedPoint.y()-self.mapPointFrom.y())
|
||
|
|
||
|
|
||
|
def __getDeltaForLinePolygon(self,event):
|
||
|
"""Function gets an event object, performs snapping from place where event occured and then counts distance
|
||
|
of found position from the place where the whole moving operation has started.
|
||
|
|
||
|
Special version for lines and polygons.
|
||
|
|
||
|
@param event event that occured
|
||
|
"""
|
||
|
|
||
|
# find out where and how far (from the place where moving was started) we are now
|
||
|
mapPoint = self.dockWidget.canvasToOsmCoords(event.pos())
|
||
|
deltaX=mapPoint.x()-self.mapPointFrom.x()
|
||
|
deltaY=mapPoint.y()-self.mapPointFrom.y()
|
||
|
|
||
|
if not self.snappingEnabled:
|
||
|
self.snapDeltas=self.snapFeat=self.snapFeatType=None
|
||
|
return (deltaX,deltaY)
|
||
|
|
||
|
lineMembers=[]
|
||
|
for ix in self.movIndexes:
|
||
|
lineMembers.append(self.movFeat.geometry().vertexAt(ix))
|
||
|
|
||
|
allMembers=[]
|
||
|
if self.movFeatType=='Line':
|
||
|
allMembers=self.movFeat.geometry().asPolyline()
|
||
|
else:
|
||
|
polygon=self.movFeat.geometry().asPolygon()
|
||
|
allMembers=polygon[0]
|
||
|
|
||
|
minDistance=99999
|
||
|
bestSnappedPoint=None
|
||
|
bestActualLineMember=None
|
||
|
|
||
|
for i in range(0,len(lineMembers)):
|
||
|
|
||
|
actualLineMember=QgsPoint(lineMembers[i].x()+deltaX,lineMembers[i].y()+deltaY)
|
||
|
point=self.canvas.getCoordinateTransform().transform(actualLineMember)
|
||
|
(retval,snappingResults)=self.snapperV.snapPoint(QPoint(point.x(),point.y()),allMembers)
|
||
|
|
||
|
if len(snappingResults)>0:
|
||
|
snappedPoint=QgsPoint(snappingResults[0].snappedVertex)
|
||
|
dX=snappedPoint.x()-(actualLineMember.x()+deltaX)
|
||
|
dY=snappedPoint.y()-(actualLineMember.y()+deltaY)
|
||
|
dist=sqrt(pow(dX,2)+pow(dY,2)) # pythagoras ;)
|
||
|
|
||
|
if dist<minDistance:
|
||
|
minDistance=dist
|
||
|
bestSnappedPoint=snappedPoint
|
||
|
bestActualLineMember=actualLineMember
|
||
|
self.snapVertexIx=self.movIndexes[i]
|
||
|
|
||
|
if not bestSnappedPoint:
|
||
|
self.snapDeltas=self.snapFeat=self.snapFeatType=None
|
||
|
return (deltaX,deltaY)
|
||
|
|
||
|
self.snapDeltas=(bestSnappedPoint.x()-bestActualLineMember.x(),bestSnappedPoint.y()-bestActualLineMember.y())
|
||
|
# use snappingResults[0].layer in findFeature() ??? findFeatureInLayer() ???
|
||
|
(self.snapFeat,self.snapFeatType)=self.dbm.findFeature(bestSnappedPoint)
|
||
|
return (deltaX+self.snapDeltas[0],deltaY+self.snapDeltas[1])
|
||
|
|
||
|
|
||
|
def __finishFeatureMoving(self,deltaX,deltaY):
|
||
|
"""It finishes the whole moving process.
|
||
|
Function also refreshes map canvas.
|
||
|
|
||
|
@param deltaX distance from target position (of moving) to start position on X axis
|
||
|
@param deltaY distance from target position (of moving) to start position on Y axis
|
||
|
"""
|
||
|
|
||
|
affected=set()
|
||
|
if self.movFeatType=="Point":
|
||
|
self.ur.startAction("Move a point.")
|
||
|
affected=self.dbm.movePoint(self.movFeat,deltaX,deltaY,self.snapFeat,self.snapFeatType)
|
||
|
|
||
|
elif self.movFeatType=="Line":
|
||
|
self.ur.startAction("Move a line.")
|
||
|
affected=self.dbm.moveLine(self.movFeat,deltaX,deltaY,self.snapFeat,self.snapFeatType,self.snapVertexIx)
|
||
|
|
||
|
elif self.movFeatType=="Polygon":
|
||
|
self.ur.startAction("Move a polygon.")
|
||
|
affected=self.dbm.movePolygon(self.movFeat,deltaX,deltaY,self.snapFeat,self.snapFeatType,self.snapVertexIx)
|
||
|
|
||
|
self.ur.stopAction(affected)
|
||
|
self.dbm.recacheAffectedNow(affected)
|
||
|
self.dockWidget.loadFeature(self.movFeat,self.movFeatType,0)
|
||
|
self.reinit()
|
||
|
|
||
|
# reload map canvas so that changes take effect
|
||
|
self.canvas.refresh()
|
||
|
|
||
|
|
||
|
def __onSelectDlgOK(self):
|
||
|
"""This function handles clicking on OK button of selection dialog.
|
||
|
"""
|
||
|
|
||
|
self.dlgSelect.close()
|
||
|
|
||
|
if not self.lw.currentItem():
|
||
|
return
|
||
|
|
||
|
self.ixFeature=self.lw.currentRow()
|
||
|
(feat,featType)=self.featuresFound[self.ixFeature]
|
||
|
|
||
|
if feat:
|
||
|
self.dockWidget.loadFeature(feat,featType,2)
|
||
|
|
||
|
|
||
|
def __onSelectDlgCancel(self):
|
||
|
"""This function handles clicking on Cancel button of selection dialog.
|
||
|
"""
|
||
|
|
||
|
self.dlgSelect.close()
|
||
|
|
||
|
|
||
|
|