def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            # if the result is already a number, it's a relation ID.
            # we don't perform a nominatim query
            if search.isdigit():
                osm_id = search
            else:
                # We perform a nominatim query
                if not self._nominatim:
                    self._nominatim = Nominatim()
                osm_id = self._nominatim.get_first_polygon_from_query(search)

            # HACK when running in test.
            # In production, osm_id IS an integer, not in tests.
            if osm_id == 'foo_BAR':
                osm_id = 12345
            elif osm_id == 'bar_FOO':
                osm_id = 54321
            # END big hack

            area = int(osm_id) + 3600000000

            if self.is_oql_query():
                new_string = 'area({})'.format(area)
            else:
                new_string = 'ref="{}" type="area"'.format(area)

            return new_string
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            lat = None
            lon = None

            # Try first if it's a point written in WKT
            geom = QgsGeometry.fromWkt(search)
            if geom.isGeosValid():
                try:
                    point = geom.asPoint()
                    lon = point.x()
                    lat = point.y()
                    LOGGER.info(tr('WKT detected for "geocodeCoords".'))
                except TypeError:
                    pass

            if lat is None and lon is None:
                if not self._nominatim:
                    self._nominatim = Nominatim()
                lon, lat = self._nominatim.get_first_point_from_query(search)

            if self.is_oql_query():
                new_string = '{},{}'.format(lat, lon)
            else:
                new_string = 'lat="{}" lon="{}"'.format(lat, lon)

            return new_string
Esempio n. 3
0
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            lat = None
            lon = None

            # Try first if it's a point written in WKT
            geom = QgsGeometry.fromWkt(search)
            if geom.isGeosValid():
                try:
                    point = geom.asPoint()
                    lon = point.x()
                    lat = point.y()
                except TypeError:
                    pass

            if lat is None and lon is None:
                nominatim = Nominatim()
                lon, lat = nominatim.get_first_point_from_query(search)

            if self.is_oql_query():
                new_string = '{},{}'.format(lat, lon)
            else:
                new_string = 'lat="{}" lon="{}"'.format(lat, lon)

            return new_string
Esempio n. 4
0
    def processAlgorithm(self, progress):

        server = self.getParameterValue(self.SERVER)
        query = self.getParameterValue(self.NOMINATIM_STRING)

        nominatim = Nominatim(url=server)
        try:
            osm_id = nominatim.get_first_polygon_from_query(query)
            progress.setInfo("Getting first OSM relation ID from Nominatim :",
                             osm_id)
        except:
            raise NominatimAreaException

        self.setOutputValue("OSM_ID", osm_id)
Esempio n. 5
0
    def processAlgorithm(self, progress):

        server = self.getParameterValue(self.SERVER)
        query = self.getParameterValue(self.NOMINATIM_STRING)

        nominatim = Nominatim(url=server)
        try:
            osm_id = nominatim.get_first_polygon_from_query(query)
            progress.setInfo(
                "Getting first OSM relation ID from Nominatim :", osm_id)
        except:
            raise NominatimAreaException

        self.setOutputValue("OSM_ID", osm_id)
Esempio n. 6
0
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            nominatim = Nominatim()
            lon, lat = nominatim.get_first_point_from_query(search)

            if self.is_oql_query():
                new_string = '{},{}'.format(lat, lon)
            else:
                new_string = 'lat="{}" lon="{}"'.format(lat, lon)

            return new_string
Esempio n. 7
0
    def replace(catch, default_nominatim):

        if default_nominatim:
            search = default_nominatim
        else:
            search = catch

        nominatim = Nominatim()
        lon, lat = nominatim.get_first_point_from_query(search)

        if is_oql(query):
            new_string = '%s,%s' % (lat, lon)
        else:
            new_string = 'lat="%s" lon="%s"' % (lat, lon)

        return new_string
Esempio n. 8
0
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            # if the result is already a number, it's a relation ID.
            # we don't perform a nominatim query
            if search.isdigit():
                osm_id = search
            else:
                # We perform a nominatim query
                nominatim = Nominatim()
                osm_id = nominatim.get_first_polygon_from_query(search)

            area = int(osm_id) + 3600000000

            if self.is_oql_query():
                new_string = 'area({})'.format(area)
            else:
                new_string = 'ref="{}" type="area"'.format(area)

            return new_string
Esempio n. 9
0
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            # if the result is already a number, it's a relation ID.
            # we don't perform a nominatim query
            if search.isdigit():
                osm_id = search
            else:
                # We perform a nominatim query
                nominatim = Nominatim()
                osm_id = nominatim.get_first_polygon_from_query(search)

            area = int(osm_id) + 3600000000

            if self.is_oql_query():
                new_string = 'area({})'.format(area)
            else:
                new_string = 'ref="{}" type="area"'.format(area)

            return new_string
class QueryPreparation:
    """Prepare the query before sending it to Overpass."""
    def __init__(self,
                 query,
                 extent=None,
                 area=None,
                 overpass=None,
                 output_format='xml'):
        """Constructor.

        :param query: The query to prepare.
        :type query: str

        :param extent: The extent to use in 4326, if needed. It can be None.
        :type extent: QgsRectangle

        :param area: A name or a list of place names.
        :type area: str, list(str)
        """
        if overpass is None:
            server = get_setting('defaultOAPI',
                                 OVERPASS_SERVERS[0]) + 'interpreter'
            self._overpass = QUrl(server + 'interpreter')
        else:
            self._overpass = QUrl(overpass)

        self._query = query
        self._query_prepared = query
        self._extent = extent
        self._places = area
        self._output_format = output_format

        self._nominatim = None

        self._query_is_ready = False

    @property
    def query(self):
        """The original query.

        :return: The original query.
        :rtype: str
        """
        return self._query

    @property
    def final_query(self):
        """The generated query or None if it's not yet generated.

        :return: The final query.
        :rtype: str
        """
        if self._query_is_ready:
            return self._query_prepared
        else:
            return None

    def is_oql_query(self):
        """Return if the query is written in OQL or not.

        :return: If the it's OQL query.
        :rtype: bool
        """
        return self._query[-1] == ';'

    def is_compatible(self):
        """The plugin doesn't support all special tags like Overpass Turbo.

        :return: A tuple (bool, reason).
        :rtype: tuple
        """
        # token to look for, error returned to the user
        incompatible_queries = {
            'geometry="center"': 'center',
            'out center;': 'center',
            '{{style': '{{style}}',
            '{{data': '{{data}}',
            '{{date': '{{date}}',
            '{{geocodeId:': '{{geocodeId:}}',
            '{{geocodeBbox:': '{{geocodeBbox:}}',
        }

        for expression, error in incompatible_queries.items():
            if re.search(expression, self._query):
                return False, error

        return True, None

    def _replace_center(self):
        """Replace {{center}} by the centroid of the extent if needed.

        The temporary query will be updated.
        """
        template = r'{{center}}'
        if not re.search(template, self._query_prepared):
            return
        else:
            if self._extent is None:
                raise QueryFactoryException(tr('Missing extent parameter.'))

        y = self._extent.center().y()
        x = self._extent.center().x()
        if self.is_oql_query():
            new_string = '{},{}'.format(y, x)
        else:
            new_string = 'lat="{}" lon="{}"'.format(y, x)

        self._query_prepared = (re.sub(template, new_string,
                                       self._query_prepared))

    def replace_bbox(self):
        """Replace {{bbox}} by the extent BBOX if needed.

        The temporary query will be updated.
        """
        template = r'{{bbox}}'
        if not re.search(template, self._query_prepared):
            return
        else:
            if self._extent is None:
                raise QueryFactoryException(tr('Missing extent parameter.'))

        y_min = self._extent.yMinimum()
        y_max = self._extent.yMaximum()
        x_min = self._extent.xMinimum()
        x_max = self._extent.xMaximum()

        # make sure we don't query for invalid bounds #222
        area_is_too_big = False
        if y_min < -90:
            y_min = -90
            area_is_too_big = True
        if y_max > 90:
            y_max = 90
            area_is_too_big = True
        if x_min < -180:
            x_min = -180
            area_is_too_big = True
        if x_max > 180:
            x_max = 180
            area_is_too_big = True

        if area_is_too_big:
            LOGGER.info(
                tr('The area was overlapping the WGS84 limits ±90 / ±180 degrees. The query has '
                   'been restricted.'))

        if self.is_oql_query():
            new_string = '{},{},{},{}'.format(y_min, x_min, y_max, x_max)
        else:
            new_string = 'e="{}" n="{}" s="{}" w="{}"'.format(
                x_max, y_max, y_min, x_min)
        self._query_prepared = (re.sub(template, new_string,
                                       self._query_prepared))

    def _replace_geocode_coords(self):
        """Replace {{geocodeCoords}} by the centroid of the extent.

        The temporary query will be updated.
        """
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            lat = None
            lon = None

            # Try first if it's a point written in WKT
            geom = QgsGeometry.fromWkt(search)
            if geom.isGeosValid():
                try:
                    point = geom.asPoint()
                    lon = point.x()
                    lat = point.y()
                    LOGGER.info(tr('WKT detected for "geocodeCoords".'))
                except TypeError:
                    pass

            if lat is None and lon is None:
                if not self._nominatim:
                    self._nominatim = Nominatim()
                lon, lat = self._nominatim.get_first_point_from_query(search)

            if self.is_oql_query():
                new_string = '{},{}'.format(lat, lon)
            else:
                new_string = 'lat="{}" lon="{}"'.format(lat, lon)

            return new_string

        template = r'{{(geocodeCoords):([^}]*)}}'
        self._query_prepared = re.sub(
            template, lambda m: replace(m.groups()[1], self._places),
            self._query_prepared)

    def _replace_geocode_area(self):
        """Replace {{geocodeCoords}} by the centroid of the extent.

        The temporary query will be updated.
        """
        def replace(catch, default_nominatim):

            if default_nominatim:
                search = default_nominatim
            else:
                search = catch

            # if the result is already a number, it's a relation ID.
            # we don't perform a nominatim query
            if search.isdigit():
                osm_id = search
            else:
                # We perform a nominatim query
                if not self._nominatim:
                    self._nominatim = Nominatim()
                osm_id = self._nominatim.get_first_polygon_from_query(search)

            # HACK when running in test.
            # In production, osm_id IS an integer, not in tests.
            if osm_id == 'foo_BAR':
                osm_id = 12345
            elif osm_id == 'bar_FOO':
                osm_id = 54321
            # END big hack

            area = int(osm_id) + 3600000000

            if self.is_oql_query():
                new_string = 'area({})'.format(area)
            else:
                new_string = 'ref="{}" type="area"'.format(area)

            return new_string

        template = r'{{(nominatimArea|geocodeArea):([^}]*)}}'
        self._query_prepared = re.sub(
            template, lambda m: replace(m.groups()[1], self._places),
            self._query_prepared)

        return self._query_prepared

    def clean_query(self):
        """Remove extra characters that might be present in the query.

        The temporary query will be updated.
        """
        query = self._query_prepared.strip()

        # Correction of ; in the OQL at the end
        self._query_prepared = re.sub(r';;$', ';', query)

    def prepare_query(self):
        """Prepare the query before sending it to Overpass.

        The temporary query will be updated.

        :return: The final query.
        :rtype: basestring
        """
        result, error = self.is_compatible()
        if not result:
            raise QueryNotSupported(error)

        self.clean_query()
        self.replace_bbox()
        self._replace_center()
        self._replace_geocode_area()
        self._replace_geocode_coords()

        self._query_is_ready = True
        return self._query_prepared

    def prepare_url(self):
        """Prepare a query to be as an URL.

        if the query is not ready to be URL prepared, a None is returned.

        :return: The URL encoded with the query.
        :rtype: basestring
        """
        if not self._query_is_ready:
            return None

        if self._output_format:
            query = re.sub(r'output="[a-z]*"',
                           'output="{}"'.format(self._output_format),
                           self._query_prepared)
            query = re.sub(r'\[out:[a-z]*',
                           '[out:{}'.format(self._output_format), query)
        else:
            query = self._query_prepared

        url_query = QUrl(self._overpass)
        query_string = QUrlQuery()
        query_string.addQueryItem('data', query)
        query_string.addQueryItem('info', 'QgisQuickOSMPlugin')
        url_query.setQuery(query_string)
        return url_query.toString()