Skip to content

Commit

Permalink
[FEATURE] Add error handling for invalid geometry
Browse files Browse the repository at this point in the history
Lines check for < 2 points
Polygons < 3 points + overlaps and other errors
Add error report to message. Add grey error band and error locations
  • Loading branch information
NathanW2 committed Oct 12, 2017
1 parent a87c05b commit a01e99c
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 12 deletions.
6 changes: 6 additions & 0 deletions src/roam/api/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

class _Events(QObject):
# Emit when you need to open a image in the main window

INFO = 0
WARNING = 1
CRITICAL = 2
SUCCESS = 3

openimage = pyqtSignal(object)

#Emit to open a url
Expand Down
59 changes: 48 additions & 11 deletions src/roam/maptools/maptools.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ def setstate(self, fixed, *args):
class PolylineTool(QgsMapToolEdit):
mouseClicked = pyqtSignal(QgsPoint)
geometryComplete = pyqtSignal(QgsGeometry)
error = pyqtSignal(str)


def __init__(self, canvas):
super(PolylineTool, self).__init__(canvas)
Expand All @@ -181,6 +183,14 @@ def __init__(self, canvas):
self.editcolour = QColor.fromRgb(0, 255, 0, 150)
self.band = RubberBand(self.canvas, QGis.Line, width=5, iconsize=20)
self.band.setColor(self.startcolour)

self.errorband = RubberBand(self.canvas, QGis.Line, width=5, iconsize=20)
self.errorband.setColor(QColor.fromRgb(153, 153, 153, 90))
self.errorlocations = QgsRubberBand(self.canvas, QGis.Point)
self.errorlocations.setColor(self.invalid_color)
self.errorlocations.setIconSize(20)
self.errorlocations.setIcon(QgsRubberBand.ICON_BOX)

self.pointband = QgsRubberBand(self.canvas, QGis.Point)
self.pointband.setColor(self.startcolour)
self.pointband.setIconSize(20)
Expand Down Expand Up @@ -394,21 +404,28 @@ def is_safe(message):
return False

# We need to remove errors that are "ok" and not really that bad
for error in errors:
if not is_safe(error.what()):
othererrors.append(error)

for error in othererrors:
print error.what()
## We are harder on what is considered valid for polygons.
if isinstance(self, PolygonTool):
for error in errors:
if not is_safe(error.what()):
othererrors.append(error)

if self.band.numberOfVertices() -1 < self.minpoints:
error = QgsGeometry.Error("Number of nodes < {0}".format(self.minpoints))
othererrors.append(error)

invalid = len(othererrors) > 0
return invalid
return othererrors

def canvasReleaseEvent(self, event):
if event.button() == Qt.RightButton:
if self.has_errors() and self.band.numberOfVertices() >= self.minpoints:
# TODO we need to handle invalid geometry case.
pass
errors = self.has_errors()
if errors:
self.error.emit("Invalid geometry. <br>"
"Please recapture. Last capture shown in grey <br>"
"<h2>Errors</h2> {0}".format("<br>".join(error.what() for error in errors)))
self.endinvalidcapture(errors)
return
else:
self.endcapture()
return
Expand All @@ -427,6 +444,20 @@ def add_point(self, point):
self.capturing = True
self.endcaptureaction.setEnabled(True)

def endinvalidcapture(self, errors):
geometry = self.band.asGeometry()
if geometry:
self.errorband.setToGeometry(geometry, None)
for error in errors:
if error.hasWhere():
self.errorlocations.addPoint(error.where())

self.capturing = False
self.set_tracking(False)
self.captureaction.setChecked(True)
self.endcaptureaction.setEnabled(False)
self.reset()

def endcapture(self):
self.capturing = False
self.set_tracking(False)
Expand All @@ -438,8 +469,13 @@ def endcapture(self):
if not geometry:
return

self.clearErrors()
self.geometryComplete.emit(geometry)

def clearErrors(self):
self.errorband.reset(QGis.Line)
self.errorlocations.reset(QGis.Point)

def clearBand(self):
self.reset()

Expand Down Expand Up @@ -504,7 +540,7 @@ class PolygonTool(PolylineTool):

def __init__(self, canvas):
super(PolygonTool, self).__init__(canvas)
self.minpoints = 4
self.minpoints = 3
self.captureaction = CaptureAction(self, "polygon", text="Digitize")
self.captureaction.toggled.connect(self.update_state)
self.reset()
Expand All @@ -520,6 +556,7 @@ class PointTool(TouchMapTool):
point based actions.
"""
geometryComplete = pyqtSignal(QgsGeometry)
error = pyqtSignal(str)

def __init__(self, canvas):
super(PointTool, self).__init__(canvas)
Expand Down
6 changes: 5 additions & 1 deletion src/roam/mapwidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,11 @@ def create_capture_buttons(self, form):
tool.geometryComplete.connect(add)
else:
tool.finished.connect(self.openForm)
tool.error.connect(partial(self.showUIMessage, form.label))

tool.error.connect(self.show_invalid_geometry_message)

def show_invalid_geometry_message(self, message):
RoamEvents.raisemessage("Invalid geometry capture", message, level=RoamEvents.CRITICAL)

def add_new_feature(self, form, geometry):
"""
Expand Down

0 comments on commit a01e99c

Please sign in to comment.