diff --git a/QuickOSM/core/query_factory.py b/QuickOSM/core/query_factory.py index ca5494fb..f97fe279 100644 --- a/QuickOSM/core/query_factory.py +++ b/QuickOSM/core/query_factory.py @@ -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 = [] @@ -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. @@ -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 diff --git a/QuickOSM/quick_osm_processing/advanced/build_query.py b/QuickOSM/quick_osm_processing/advanced/build_query.py index 05258f79..1478f518 100644 --- a/QuickOSM/quick_osm_processing/advanced/build_query.py +++ b/QuickOSM/quick_osm_processing/advanced/build_query.py @@ -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, diff --git a/QuickOSM/quick_osm_processing/build_input.py b/QuickOSM/quick_osm_processing/build_input.py index de96205a..8bb9a014 100644 --- a/QuickOSM/quick_osm_processing/build_input.py +++ b/QuickOSM/quick_osm_processing/build_input.py @@ -141,11 +141,13 @@ 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): @@ -153,6 +155,7 @@ def fetch_based_parameters(self, parameters, context): 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.""" @@ -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.""" diff --git a/QuickOSM/quick_osm_processing/quickosm_process.py b/QuickOSM/quick_osm_processing/quickosm_process.py index b190a3e2..ff40e110 100644 --- a/QuickOSM/quick_osm_processing/quickosm_process.py +++ b/QuickOSM/quick_osm_processing/quickosm_process.py @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/QuickOSM/test/test_processing.py b/QuickOSM/test/test_processing.py index dd82d7fa..a7a69e8a 100644 --- a/QuickOSM/test/test_processing.py +++ b/QuickOSM/test/test_processing.py @@ -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( diff --git a/QuickOSM/test/test_query_factory.py b/QuickOSM/test/test_query_factory.py index 74efa9e7..7d2b02a7 100644 --- a/QuickOSM/test/test_query_factory.py +++ b/QuickOSM/test/test_query_factory.py @@ -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()