Skip to content

Commit

Permalink
Merge branch 'bbox_filter_option' into 'main'
Browse files Browse the repository at this point in the history
enable bbox option

Closes #13

See merge request qgis/hvbg-filterplugin!10
  • Loading branch information
pgipper committed Nov 15, 2022
2 parents fd6a243 + 4323ec0 commit c8de426
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 13 deletions.
6 changes: 5 additions & 1 deletion controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class FilterController(QObject):
def __init__(self, parent: Optional[QObject] = None) -> None:
super().__init__(parent=parent)
self.currentFilter = FilterDefinition(
self.tr('New Filter'), '', QgsCoordinateReferenceSystem(), Predicate.INTERSECTS
self.tr('New Filter'), '', QgsCoordinateReferenceSystem(), Predicate.INTERSECTS, False
)
self.rubberBands = []
self.toolbarIsActive = False
Expand Down Expand Up @@ -88,6 +88,10 @@ def setFilterPredicate(self, predicate: Predicate):
self.currentFilter.predicate = predicate.value
self.refreshFilter()

def setFilterBbox(self, bbox: bool):
self.currentFilter.bbox = bbox
self.refreshFilter()

def saveCurrentFilter(self):
FilterManager().saveFilterDefinition(self.currentFilter)
self.refreshFilter()
24 changes: 16 additions & 8 deletions filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class FilterDefinition:
wkt: str
crs: QgsCoordinateReferenceSystem
predicate: int
bbox: bool

def __post_init__(self):
self.predicate = int(self.predicate)
Expand All @@ -43,19 +44,25 @@ def filterString(self, layer: QgsVectorLayer) -> str:
Returns:
str: A layer filter string
"""
template = "{spatial_predicate}({geom_name}, ST_TRANSFORM(ST_GeomFromText('{wkt}', {srid}), {layer_srid}))"

# ST_DISJOINT does not use spatial indexes, but we can use its opposite "NOT ST_INTERSECTS" which does
spatial_predicate = f"ST_{Predicate(self.predicate).name}"
if self.predicate == Predicate.DISJOINT:
spatial_predicate = "NOT ST_INTERSECTS"
else:
spatial_predicate = f"ST_{Predicate(self.predicate).name}"

template = "{spatial_predicate}({geom_name}, ST_TRANSFORM(ST_GeomFromText('{wkt}', {srid}), {layer_srid}))"

wkt = self.wkt
if self.bbox:
rect = QgsGeometry.fromWkt(self.wkt).boundingBox()
wkt = QgsGeometry.fromRect(rect).asWkt()


geom_name = getLayerGeomName(layer)
return template.format(
spatial_predicate=spatial_predicate,
geom_name=geom_name,
wkt=self.wkt,
wkt=wkt,
srid=self.crs.postgisSrid(),
layer_srid=layer.crs().postgisSrid()
)
Expand All @@ -66,15 +73,16 @@ def storageString(self) -> str:
For the CRS just the Auth ID is stored, e.g. EPSG:1234 or PROJ:9876.
"""
return SPLIT_STRING.join([self.name, self.wkt, self.crs.authid(), str(self.predicate)])
return SPLIT_STRING.join([self.name, self.wkt, self.crs.authid(), str(self.predicate), str(self.bbox)])

@staticmethod
def fromStorageString(value: str) -> 'FilterDefinition':
parameters = value.split(SPLIT_STRING)
assert len(parameters) == 4, "Malformed FilterDefinition loaded from settings: {value}"
name, wkt, crs_auth_id, predicate = parameters
assert len(parameters) == 5, "Malformed FilterDefinition loaded from settings: {value}"
name, wkt, crs_auth_id, predicate, bbox_str = parameters
crs = QgsCoordinateReferenceSystem(crs_auth_id)
return FilterDefinition(name, wkt, crs, predicate)
bbox = bool(bbox_str == 'True')
return FilterDefinition(name, wkt, crs, predicate, bbox)

@property
def isValid(self) -> bool:
Expand Down
51 changes: 47 additions & 4 deletions widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,16 @@ def onNameClicked(self):

class PredicateButton(QPushButton):
predicateChanged = pyqtSignal(Predicate)
bboxChanged = pyqtSignal(bool)

def __init__(self, parent: Optional[QWidget] = None) -> None:
super().__init__(parent=parent)
self.setObjectName('mPredicateSelectAction')
self.setToolTip(self.tr('Geometric predicate'))
self.setIcon(QgsApplication.getThemeIcon('/mActionOptions.svg'))
self.menu = QMenu(parent=parent)

self.menu.addSection(self.tr('Geometric Predicate'))
self.predicateActionGroup = QActionGroup(self)
self.predicateActionGroup.setExclusive(True)
for predicate in Predicate:
Expand All @@ -200,6 +203,29 @@ def __init__(self, parent: Optional[QWidget] = None) -> None:
action.triggered.connect(self.onPredicateChanged)
self.predicateActionGroup.addAction(action)
self.menu.addActions(self.predicateActionGroup.actions())
self.menu.addSeparator()

self.menu.addSection(self.tr('Object of comparison'))
self.bboxActionGroup = QActionGroup(self)
self.bboxActionGroup.setExclusive(True)
self.bboxTrueAction = QAction(self.menu)
self.bboxTrueAction.setCheckable(True)
self.bboxTrueAction.setText(self.tr('BBOX'))
self.bboxTrueAction.setToolTip(self.tr('Compare features to the filters bounding box'))
self.bboxTrueAction.bbox = True
self.bboxTrueAction.triggered.connect(self.onBboxChanged)
self.bboxFalseAction = QAction(self.menu)
self.bboxFalseAction.setCheckable(True)
self.bboxFalseAction.setChecked(True)
self.bboxFalseAction.setText(self.tr('GEOM'))
self.bboxFalseAction.setToolTip(self.tr('Compare features to the exact filter geometry'))
self.bboxFalseAction.bbox = False
self.bboxFalseAction.triggered.connect(self.onBboxChanged)

self.bboxActionGroup.addAction(self.bboxTrueAction)
self.bboxActionGroup.addAction(self.bboxFalseAction)
self.menu.addActions(self.bboxActionGroup.actions())

self.setMenu(self.menu)
self.setFlat(True)

Expand All @@ -211,13 +237,27 @@ def getPredicate(self) -> Predicate:
if currentAction:
return currentAction.predicate

def setCurrentAction(self, predicate: int):
def setCurrentPredicateAction(self, predicate: int):
for action in self.predicateActionGroup.actions():
if action.predicate == Predicate(predicate):
action.triggered.disconnect()
action.setChecked(True)
action.triggered.connect(self.onPredicateChanged)

def setCurrentBboxAction(self, bbox: bool):
action = self.bboxTrueAction if bbox else self.bboxFalseAction
action.triggered.disconnect()
action.setChecked(True)
action.triggered.connect(self.onBboxChanged)

def onBboxChanged(self):
self.bboxChanged.emit(self.getBbox())

def getBbox(self):
currentAction = self.bboxActionGroup.checkedAction()
if currentAction:
return currentAction.bbox


class FilterToolbar(QToolBar):

Expand Down Expand Up @@ -302,6 +342,7 @@ def setupConnections(self):
self.manageFiltersAction.triggered.connect(self.startManageFiltersDialog)
self.saveCurrentFilterAction.triggered.connect(self.controller.saveCurrentFilter)
self.predicateButton.predicateChanged.connect(self.controller.setFilterPredicate)
self.predicateButton.bboxChanged.connect(self.controller.setFilterBbox)
self.filterFromSelectionAction.triggered.connect(self.controller.setFilterFromSelection)
self.controller.filterChanged.connect(self.onFilterChanged)
self.toggleVisibilityAction.toggled.connect(self.onShowGeom)
Expand All @@ -317,7 +358,8 @@ def onToggled(self, checked: bool):

def onFilterChanged(self, filterDef: FilterDefinition):
self.changeDisplayedName(filterDef)
self.predicateButton.setCurrentAction(filterDef.predicate)
self.predicateButton.setCurrentPredicateAction(filterDef.predicate)
self.predicateButton.setCurrentBboxAction(filterDef.bbox)
self.onShowGeom(self.showGeomStatus)

def changeDisplayedName(self, filterDef: FilterDefinition):
Expand Down Expand Up @@ -359,8 +401,9 @@ def onShowGeom(self, checked: bool):
def showFilterGeom(self):
# Get filterRubberBand geometry, transform it and show it on canvas
filterRubberBand = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.PolygonGeometry)
filterWkt = self.controller.currentFilter.wkt
filterGeom = QgsGeometry.fromWkt(filterWkt)
filterGeom = self.controller.currentFilter.geometry
if self.controller.currentFilter.bbox:
filterGeom = QgsGeometry.fromRect(filterGeom.boundingBox())
filterCrs = self.controller.currentFilter.crs
projectCrs = QgsCoordinateReferenceSystem(QgsProject.instance().crs())
filterProj = QgsCoordinateTransform(filterCrs, projectCrs, QgsProject.instance())
Expand Down

0 comments on commit c8de426

Please sign in to comment.