Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logical operators to processing algos #374

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions QuickOSM/core/query_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def __init__(
"""
if isinstance(type_multi_request, list):
self._type_multi_request = [x for x in type_multi_request if x]
elif isinstance(type_multi_request, str):
self._type_multi_request = self._convert_to_multitypes(type_multi_request)
else:
self._type_multi_request = []

Expand Down Expand Up @@ -154,6 +156,32 @@ def area(self) -> list:
"""
return self._area

@staticmethod
def _convert_to_multitypes(type_multi_request: str) -> List[MultiType]:
"""Converts a string of comma separated 'AND'/'OR's to a list of MultiType equivalents.

:param type_multi_request: A string of comma separated 'AND'/'OR's provided from input of processing
algorithms
:type type_multi_request: str

:return: list of MultiType
:rtype: list

:raise QueryFactoryException:
"""
types = []
if type_multi_request:
for type_ in type_multi_request.split(','):
type_ = type_.strip()
if type_ == 'AND':
types.append(MultiType.AND)
elif type_ == 'OR':
types.append(MultiType.OR)
else:
raise QueryFactoryException(
tr('Only values "AND"/"OR" are allowed as logical operators.'))
return types

def _check_parameters(self) -> bool:
"""Internal function to check that the query can be built.

Expand Down Expand Up @@ -214,6 +242,17 @@ def _check_parameters(self) -> bool:
raise QueryFactoryException(
tr(f'Key "{key}" contains leading or trailing whitespace.'))

if len(self._key) != 0:
if len(self._type_multi_request) > len(self._key) - 1:
raise QueryFactoryException(
tr('Too many logical operators were provided.')
)

if len(self._type_multi_request) < len(self._key) - 1:
raise QueryFactoryException(
tr('Not enough logical operators were provided.')
)

self._checked = True
return True

Expand Down
1 change: 1 addition & 0 deletions QuickOSM/quick_osm_processing/advanced/build_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def initAlgorithm(self, config=None):
def build_query(self) -> Dict[str, str]:
"""Build the query requested."""
query_factory = QueryFactory(
type_multi_request=self.type_multi_request,
query_type=self.QUERY_TYPE,
key=self.key,
value=self.value,
Expand Down
16 changes: 16 additions & 0 deletions QuickOSM/quick_osm_processing/build_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,21 @@ class BuildBasedQuery(BuildBased):

KEY = 'KEY'
VALUE = 'VALUE'
TYPE_MULTI_REQUEST = 'TYPE_MULTI_REQUEST'

def __init__(self):
super().__init__()
self.key = None
self.value = None
self.type_multi_request = None
self.distance = None

def fetch_based_parameters(self, parameters, context):
"""Get the parameters."""
super().fetch_based_parameters(parameters, context)
self.key = self.parameterAsString(parameters, self.KEY, context)
self.value = self.parameterAsString(parameters, self.VALUE, context)
self.type_multi_request = self.parameterAsString(parameters, self.TYPE_MULTI_REQUEST, context)

def add_top_parameters(self):
"""Set up the parameters."""
Expand Down Expand Up @@ -180,6 +183,19 @@ def add_top_parameters(self):
param.setHelp(help_string)
self.addParameter(param)

param = QgsProcessingParameterString(
self.TYPE_MULTI_REQUEST,
tr('Operator types to combine multiple keys and values with'),
optional=True
)

# TODO: expand help string
help_string = tr(
'The logical operators used to combine keys and values, if there are multiple.'
)
param.setHelp(help_string)
self.addParameter(param)


class BuildBasedNotSpatialQuery(BuildBasedQuery):
"""Set up the parameters for a not spatial query."""
Expand Down
4 changes: 4 additions & 0 deletions QuickOSM/quick_osm_processing/quickosm_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ def processAlgorithm(self, parameters, context, feedback):
'KEY': self.key,
'SERVER': self.server,
'TIMEOUT': self.timeout,
'TYPE_MULTI_REQUEST': self.type_multi_request,
'VALUE': self.value
},
feedback=self.feedback
Expand Down Expand Up @@ -342,6 +343,7 @@ def processAlgorithm(self, parameters, context, feedback):
'KEY': self.key,
'SERVER': self.server,
'TIMEOUT': self.timeout,
'TYPE_MULTI_REQUEST': self.type_multi_request,
'VALUE': self.value
},
feedback=self.feedback
Expand Down Expand Up @@ -389,6 +391,7 @@ def processAlgorithm(self, parameters, context, feedback):
'KEY': self.key,
'SERVER': self.server,
'TIMEOUT': self.timeout,
'TYPE_MULTI_REQUEST': self.type_multi_request,
'VALUE': self.value
},
feedback=self.feedback
Expand Down Expand Up @@ -435,6 +438,7 @@ def processAlgorithm(self, parameters, context, feedback):
'KEY': self.key,
'SERVER': self.server,
'TIMEOUT': self.timeout,
'TYPE_MULTI_REQUEST': self.type_multi_request,
'VALUE': self.value
},
feedback=self.feedback
Expand Down
29 changes: 29 additions & 0 deletions QuickOSM/test/test_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,35 @@ def test_build_in_extent_query(self):

self.assertEqual(result_expected, result)

def test_build_in_extent_query_with_multitypes(self):
"""Test for the build of an in extent algorithm that uses the multitypes parameter."""
result = processing.run(
'quickosm:buildqueryextent',
{
'EXTENT': '8.71679,8.79689,51.70687,51.72602 [EPSG:4326]',
'KEY': 'highway,footway',
'SERVER': 'https://lz4.overpass-api.de/api/interpreter',
'TIMEOUT': 25,
'TYPE_MULTI_REQUEST': 'AND',
'VALUE': 'footway,sidewalk'
}
)
result_expected = {
'OUTPUT_OQL_QUERY':
'[out:xml] [timeout:25];\n(\n node["highway"="footway"]["footway"="sidewalk"]'
'( 51.70687,8.71679,51.72602,8.79689);\n way["highway"="footway"]["footway"="sidewalk"]'
'( 51.70687,8.71679,51.72602,8.79689);\n relation["highway"="footway"]'
'["footway"="sidewalk"]( 51.70687,8.71679,51.72602,8.79689);\n);\n(._;>;);\nout body;',
'OUTPUT_URL':
'https://lz4.overpass-api.de/api/interpreter?data=[out:xml] [timeout:25];%0A(%0A'
' node[%22highway%22%3D%22footway%22][%22footway%22%3D%22sidewalk%22]'
'( 51.70687,8.71679,51.72602,8.79689);%0A way[%22highway%22%3D%22footway%22]'
'[%22footway%22%3D%22sidewalk%22]( 51.70687,8.71679,51.72602,8.79689);%0A '
'relation[%22highway%22%3D%22footway%22][%22footway%22%3D%22sidewalk%22]'
'( 51.70687,8.71679,51.72602,8.79689);%0A);%0A(._;%3E;);%0Aout body;&info=QgisQuickOSMPlugin'
}
self.assertEqual(result_expected, result)

def test_build_raw_query(self):
"""Test for the build of a raw query algorithm."""
result = processing.run(
Expand Down
2 changes: 1 addition & 1 deletion QuickOSM/test/test_query_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_impossible_queries(self):
"""Test queries which are not possible and must raise an exception."""
# Query type
# noinspection PyTypeChecker
query = QueryFactory('fake_query_type', area='foo')
query = QueryFactory(query_type='fake_query_type', area='foo')
msg = 'Wrong query type.'
with self.assertRaisesRegex(QueryFactoryException, msg):
query._check_parameters()
Expand Down